vlambda博客
学习文章列表

PDF 处理的种种方法-R语言


今天,来谈一下 PDF 处理的一些办法:


PDF 处理的种种方法-R语言

PDF 裁剪、合并、抽取

需要用到 qpdf 包


library(qpdf)
> ls('package:qpdf')
[1"pdf_combine"  "pdf_compress" "pdf_length"  
[4"pdf_split"    "pdf_subset" 


1/  pdf_combine: 用于合并 “正规格式”的 pdf( 注:Acrobat 不能识别合并的 PDF, 该函数也无法进行操作)

2/  pdf_compress: 用于压缩、线性化 PDF, 即减小 PDF 的大小

3/  pdf_length:  返回 PDF 的页数

4/  pdf_split:  把 PDF 拆分为单页的 PDF

5/  pdf_subset: 从原始 PDF 中抽提相应的页数,组成新的 PDF


一些注意事项:


pdf_split('1.pdf','D:\\D\\less\\')
sprintf('%03.0f',1:242) %>% paste0('D:\\D\\lm\\',.,'.pdf') -> q
fi <- list.files("D:\\D\\less\\")
setwd('D:\\D\\less\\')
file.rename(fi,q)

pdf_split() 的路径参数不要含有中文信息。拆分为单页 PDF 后,文件名过长。这时,利用 file.rename() 函数进行文件的批量重命名。

如此:


> dir() %>% head; dir() %>% tail
[1"001.pdf" "002.pdf" "003.pdf" "004.pdf"
[5"005.pdf" "006.pdf"
[1"237.pdf" "238.pdf" "239.pdf" "240.pdf"
[5"241.pdf" "242.pdf"

重新获得:001.pdf 至 242.pdf


> pdf_length('001.pdf')
[11


> pdf_subset('1.pdf',pages = 1:5)
[1"D:\\D\\1_output.pdf"
> pdf_subset('1.pdf',pages = 1:5,'a.pdf')
[1"D:\\D\\a.pdf"

或者,抽取一些不相邻的页面:


> pdf_subset('1.pdf',pages = c(1,10,3,6,7,20),'a1.pdf')
[1"D:\\D\\a1.pdf"
> pdf_length('a1.pdf')
[16


pdf 合并:


>  sprintf('%03.0f',c(1,20,100,120,176,174,200,210)) %>% 
+     paste0('D:\\D\\less\\',.,'.pdf')  %>% 
+      pdf_combine('az.pdf')
[1"D:\\D\\less\\az.pdf"
> pdf_length('az.pdf')
[18



PDF 处理的种种方法-R语言


pdftools


library(pdftools)
> ls('package:pdftools')
 [1"pdf_attachments" "pdf_combine"    
 [3"pdf_compress"    "pdf_convert"    
 [5"pdf_data"        "pdf_fonts"      
 [7"pdf_info"        "pdf_length"     
 [9"pdf_ocr_data"    "pdf_ocr_text"   
[11"pdf_pagesize"    "pdf_render_page"
[13"pdf_split"       "pdf_subset"     
[15"pdf_text"        "pdf_toc"        
[17"poppler_config" 

值得注意的是:pdf_combine, pdf_compress, pdf_convert, pdf_length,pdf_split, pdf_subset 源自 qpdf


> unload('qpdf')
Error in unload("qpdf") : 
  namespace ‘qpdf’ is imported by ‘pdftools’ so cannot be unloaded

以上报错信息提示:

pdftools 在使用时,会自动导入 qpdf, 因此无法在 pdftools 之前卸载 qpdf


> pdf_info('a1.pdf')
$version
[1"1.3"

$pages
[16

$encrypted
[1FALSE

$linearized
[1FALSE

$keys
list()

$metadata
[1""

$locked
[1FALSE

$attachments
[1FALSE

$layout
[1"no_layout"

显示 pdf 相关信息。

1/  pdf_fonts:  获取字体信息

2/  pdf_ocr_data, pdf_ocr_text: 进行 OCR 识别

3/  pdf_toc: 识别 PDF 的目录书签

4/  pdf_convert: 用于 PDF 向其他文件格式的转换



> pdf_convert(
pdf,
format = "png",
pages = NULL,
filenames = NULL,
dpi = 72,
antialias = TRUE,
opw = "",
upw = "",
verbose = TRUE
)

注意事项:

format 格式必须为下列内容之一:


>  poppler_config()$supported_image_formats
[1"png"  "jpeg" "jpg"  "tiff" "pnm" 

即意味着:pdf_convert 不能生成单页的 pdf

由于默认的参数:dpi = 72, 所以在导出往往建议 dpi 改至 220 以上,以达到较高的清晰度。

filenames 参数值必须与 pages 值相对应。


示例:


setwd('D:\\D\\lm')
sprintf('%03.0f',1:242) %>% paste0('D:\\D\\lm\\',.,'.png') -> q
pdf_convert('1.pdf', pages = 1:242,filenames = q,dpi = 220)




PDF 处理的种种方法-R语言


关于 一页 PDF 内含有多页,如何

裁剪、重新合成成新的 PDF


f <- list.files()
   for (i in seq_along(f)) {
          a <- image_read(f[i])
           p1 <- p[i]
          a %>% image_crop('1466x1101+494+466') %>% image_write(paste0(p1,'_1.png'))
          Sys.sleep(0.1)
          a %>% image_crop('1466x1101+494+466') %>% image_write(paste0(p1,'_2.png'))
          Sys.sleep(0.1)
        }  

敲重点,需要使用 magick 包,以及必须完成 ImageMagick 的环境配置


library(magick)
Linking to ImageMagick 6.9.9.14
Enabled features: cairo, freetype, fftw, ghostscript, lcms, pango, rsvg, webp
Disabled features: fontconfig, x11


解读:


> setwd('D:\\D\\lm\\m')
> f <- list.files()
> f
 [1"001.png" "002.png" "003.png" "004.png"
 [5"005.png" "006.png" "007.png" "008.png"
 [9"009.png" "010.png"

这里以 10 张 png 为例,里面是 2x1 的ppt页面内容,需要把这些实现批量裁剪输出,最后合并成 PDF


> a <- image_read(f[1])
> a
# A tibble: 1 x 7
  format width height colorspace matte filesize
  <chr>  <int>  <int> <chr>      <lgl>    <int>
1 PNG     1819   2573 sRGB       FALSE   717226
# ... with 1 more variable: density <chr>

返回的 tibble 格式信息中, width 和 height 是后续裁剪所需的依据。


f <- list.files(pattern = '*.png')
library(magick)
  nchar(as.character(length(f))) %>% 
           paste0('%0',.,'.0f') %>% 
               sprintf(1:length(f))  -> p
   for (i in seq_along(f)) {
          a <- image_read(f[i])
           pp <- p[i] %>% paste0(c('_01.png','_02.png'))
          a %>% image_crop('1090x818+362+342') %>% image_write(pp[1])
          Sys.sleep(0.1)
          a %>% image_crop('1090x818+362+1413') %>% image_write(pp[2])
          Sys.sleep(0.1)
        }  

查看文件:

> g <- list.files(pattern = '_')
> g
 [1"01_01.png" "01_02.png" "02_01.png"
 [4"02_02.png" "03_01.png" "03_02.png"
 [7"04_01.png" "04_02.png" "05_01.png"
[10"05_02.png" "06_01.png" "06_02.png"
[13"07_01.png" "07_02.png" "08_01.png"
[16"08_02.png" "09_01.png" "09_02.png"
[19"10_01.png" "10_02.png"

由于在 for 循环中,image_write() 无法接收字符串向量形式的路径,所以只能使新生成的文件与原文件处于同一路径下


> g <- list.files(pattern = '_')
> g
 [1"01_01.png" "01_02.png" "02_01.png"
 [4"02_02.png" "03_01.png" "03_02.png"
 [7"04_01.png" "04_02.png" "05_01.png"
[10"05_02.png" "06_01.png" "06_02.png"
[13"07_01.png" "07_02.png" "08_01.png"
[16"08_02.png" "09_01.png" "09_02.png"
[19"10_01.png" "10_02.png"

利用 pattern 参数,进行过滤,获得文件名,当然也可以利用 paste0() 进行生成字符串向量。



接下来使用,file.copy() 函数


> file.copy
function (from, to, overwrite = recursive, recursive = FALSE
    copy.mode = TRUE, copy.date = FALSE


>  g <- list.files(pattern = '_')
> dir.create('D:\\D\\lm\\m\\try')
> file.copy(g, 'D:\\D\\lm\\m\\try')
 [1TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [9TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[17TRUE TRUE TRUE TRUE

如此,即复制至新的路径下,

之后:合并 png 成为 PDF


setwd('D:\\D\\lm\\m\\try')
system('magick *.png a.pdf')

注:此处调用 ImageMagick 命令行,执行。不可添加中文字符!



琐碎的技巧点


Ⅰ/   像素位置的获取:

①   利用 Windows 系统上的 「画图」工具,即可获得像素点位置,属于免费工具!

②   利用 Photoshop 的标尺功能,并显示单位为像素,来查找待裁剪区域的像素点位置


Ⅱ/   环境问题:

   magick 包必须依赖本地安装的 ImageMagick

   ImageMagick 安装时,必须勾选:添加到环境变量


Ⅲ/   qpdf

    qpdf 里面的部分函数,在生成文件的路径设置上,不要添加中文字符!

    这一点,类似于 ImageMagick 的命令行操作。



---end---