【PyMuPDF和pdf2image】Python將PDF轉(zhuǎn)成圖片PNG和JPG

公眾號:軟測小生捷沸, 獲取更多精彩內(nèi)容偿乖。
前言:在最近的測試中遇到一個與PDF相關(guān)的測試需求缨硝,其中有一個過程是將PDF轉(zhuǎn)換成圖片腺律,然后對圖片進(jìn)行測試宜肉。

粗略的試了好幾種方式,其中語言嘗試了Python和Java翎碑,總體而言所找到的Python方式相對比Java更快一些谬返,更簡單一些。
下面首先分享一下Python將PDF轉(zhuǎn)換成圖片日杈,Java后續(xù)有時間在進(jìn)行分享遣铝。

需求:我需要先將PDF轉(zhuǎn)換成為PNG圖片,并截取圖片的一部分存儲,然后作為測試目標(biāo)進(jìn)行測試涨冀。
操作:
1填硕、PDF轉(zhuǎn)PNG圖片
2、對PNG圖片進(jìn)行指定區(qū)域截圖鹿鳖,在另存到指定文件夾下
針對截圖此處所找到的方法如上一篇文章:Python圖片裁剪的兩種方式——Pillow和OpenCV

1姻檀、PyMuPDF將PDF轉(zhuǎn)換成圖片

pip install PyMuPDF

import sys, fitz, os, datetime

def pyMuPDF_fitz(pdfPath, imagePath):
    startTime_pdf2img = datetime.datetime.now()#開始時間

    print("imagePath="+imagePath)
    pdfDoc = fitz.open(pdfPath)
    for pg in range(pdfDoc.pageCount):
        page = pdfDoc[pg]
        rotate = int(0)
        # 每個尺寸的縮放系數(shù)為1.3,這將為我們生成分辨率提高2.6的圖像涝滴。
        # 此處若是不做設(shè)置绣版,默認(rèn)圖片大小為:792X612, dpi=96
        zoom_x = 1.33333333 #(1.33333333-->1056x816)   (2-->1584x1224)
        zoom_y = 1.33333333
        mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
        pix = page.getPixmap(matrix=mat, alpha=False)

        if not os.path.exists(imagePath):#判斷存放圖片的文件夾是否存在
            os.makedirs(imagePath) # 若圖片文件夾不存在就創(chuàng)建

        pix.writePNG(imagePath+'/'+'images_%s.png' % pg)#將圖片寫入指定的文件夾內(nèi)

    endTime_pdf2img = datetime.datetime.now()#結(jié)束時間
    print('pdf2img時間=',(endTime_pdf2img - startTime_pdf2img).seconds)

if __name__ == "__main__":
    pdfPath = '../path/demo.pdf'
    imagePath = '../path/image'
    pyMuPDF_fitz(pdfPath, imagePath)
image.gif

PDF文檔頁數(shù)超過100頁的話需要十幾秒,因為先轉(zhuǎn)換成一整張1056X816的圖片歼疮,再對本地文件中的所有圖片進(jìn)行遍歷截圖杂抽,時間上比較慢,通過查看文檔發(fā)現(xiàn):
還可以在轉(zhuǎn)換的同時指定圖片的大小,對圖片指定區(qū)域進(jìn)行截取腋妙,這樣快很多默怨,一步到位,省去了二次截圖的過程骤素,前提使我們必須要知道想要截取哪一塊區(qū)域并保存
官方示例代碼如下:

image

#下面的這段代碼就是想要從一頁PDF的中心點為起點截取到右下角的區(qū)域匙睹,截取整張圖的1/4.
>>> mat = fitz.Matrix(2, 2)                  # 在每個方向縮放因子2
>>> rect = page.rect                         # 頁面的矩形
>>> mp = rect.tl + (rect.br - rect.tl) * 0.5 # 矩形的中心
>>> clip = fitz.Rect(mp, rect.br)            # 我們想要的剪切區(qū)域
>>> pix = page.getPixmap(matrix = mat, clip = clip)

image.gif

實際用到的例子是:
整張圖片導(dǎo)出之后是1056x816愚屁,但是我想要的是這張圖片最底部的部分1056x75,相當(dāng)于PDF文檔的頁腳部分痕檬。

import sys, fitz, os, datetime

def pyMuPDF_fitz(pdfPath, imagePath):
    startTime_pdf2img = datetime.datetime.now()#開始時間

    pdfDoc = fitz.open(pdfPath)
    for pg in range(pdfDoc.pageCount):
        page = pdfDoc[pg]
        rotate = int(0)
        # 每個尺寸的縮放系數(shù)為1.3霎槐,這將為我們生成分辨率提高2.6的圖像。
        # 此處若是不做設(shè)置梦谜,默認(rèn)圖片大小為:792X612, dpi=96
        zoom_x = 1.33333333 #(1.33333333-->1056x816)   (2-->1584x1224)
        zoom_y = 1.33333333
        mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
        pix = page.getPixmap(matrix=mat, alpha=False)

        if not os.path.exists(imagePath):#判斷存放圖片的文件夾是否存在
            os.makedirs(imagePath) # 若圖片文件夾不存在就創(chuàng)建

        pix.writePNG(imagePath+'/'+'images_%s.png' % pg)#將圖片寫入指定的文件夾內(nèi)

    endTime_pdf2img = datetime.datetime.now()#結(jié)束時間
    print('pdf2img時間=',(endTime_pdf2img - startTime_pdf2img).seconds)

def pyMuPDF2_fitz(pdfPath, imagePath):
    pdfDoc = fitz.open(pdfPath) # open document
    for pg in range(pdfDoc.pageCount): # iterate through the pages
        page = pdfDoc[pg]
        rotate = int(0)
        # 每個尺寸的縮放系數(shù)為1.3丘跌,這將為我們生成分辨率提高2.6的圖像
        # 此處若是不做設(shè)置,默認(rèn)圖片大小為:792X612, dpi=96
        zoom_x = 1.33333333 #(1.33333333-->1056x816)   (2-->1584x1224)
        zoom_y = 1.33333333
        # 縮放系數(shù)1.3在每個維度  .preRotate(rotate)是執(zhí)行一個旋轉(zhuǎn)
        mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate) 
        rect = page.rect                         # 頁面大小
        mp = rect.tl + (rect.bl - (0,75/zoom_x)) # 矩形區(qū)域    56=75/1.3333
        clip = fitz.Rect(mp, rect.br)            # 想要截取的區(qū)域
        pix = page.getPixmap(matrix=mat, alpha=False, clip=clip) # 將頁面轉(zhuǎn)換為圖像
        if not os.path.exists(imagePath):
            os.makedirs(imagePath)
        pix.writePNG(imagePath+'/'+'psReport_%s.png' % pg)# store image as a PNG

if __name__ == "__main__":
    pdfPath = '../path/demo.pdf'
    imagePath = '../path/image'
    #pyMuPDF_fitz(pdfPath, imagePath)#只是轉(zhuǎn)換圖片
    pyMuPDF2_fitz(pdfPath, imagePath)#指定想要的區(qū)域轉(zhuǎn)換成圖片

當(dāng)然上面這種是綜合下來最快的唁桩,另外再介紹一種方法pdf2image

2闭树、pdf2image 將PDF轉(zhuǎn)換成圖片

pdf2image也是個包裝器,真正的轉(zhuǎn)換工具是poppler
GitHub地址:https://github.com/Belval/pdf2image 荒澡,上面也有相關(guān)的配置說明报辱。

1、安裝pdf2image: pip install pdf2image
2单山、Windows安裝配置poppler(這里只介紹Windows碍现,Mac和Linux去上面Github地址里面參考官網(wǎng))
Windows用戶必須為Windows安裝poppler (http://blog.alivate.com.au/poppler-windows/),然后將bin/文件夾添加到PATH(開始>輸入env>編輯系統(tǒng)環(huán)境變量>環(huán)境變量...>系統(tǒng)變量>Path)

注意這里配置之后需要重啟一下電腦才會生效米奸,不然會報如下錯誤:
ERROE:FileNotFoundError: [WinError 2] The system cannot find the file specified
During handling of the above exception, another exception occurred:


3昼接、pip install pillow (如果你還沒有安裝過的話)

from pdf2image import convert_from_path,convert_from_bytes
import tempfile
from pdf2image.exceptions import (
    PDFInfoNotInstalledError,
    PDFPageCountError,
    PDFSyntaxError
)

def pdf2image2(pdfPath, imagePath, pageNum):
  #方法一:
  #convert_from_path('a.pdf', dpi=500, "output",fmt="JPEG",output_file="ok"
  #,thread_count=4)
  #這會將a.pdf轉(zhuǎn)換成在output文件夾下形如ok_線程id-頁碼.jpg的一些文件。
  #若不指定thread_count則默認(rèn)為1悴晰,并且在文件名中顯示id. 這種轉(zhuǎn)換是直接寫入到磁盤上的慢睡,
  #因此不會占用太多內(nèi)存。

#下面的寫法直接寫入到內(nèi)存,默認(rèn)是C:\Users\pppp\AppData\Local\Temp\生成的uuid4名字
    images = convert_from_path(pdfPath, dpi=96)
    for image in images:
        if not os.path.exists(imagePath):
            os.makedirs(imagePath)
        image.save(imagePath+'/'+'psReport_%s.png' % images.index(image), 'PNG')

    #方法二:
    images = convert_from_bytes(open('/home/belval/example.pdf', 'rb').read())
    for image in images:
        if not os.path.exists(imagePath):
            os.makedirs(imagePath)
        image.save(imagePath+'/'+'psReport_%s.png' % images.index(image), 'PNG')    

    #方法三膨疏,也是最推薦的方法
    with tempfile.TemporaryDirectory() as path:
        images_from_path = convert_from_path(pdfPath, output_folder=path,
 dpi=96)
        for image in images_from_path:
            if not os.path.exists(imagePath):
                os.makedirs(imagePath)
            image.save(imagePath+'/'
+'psReport_%s.png' % images_from_path.index(image), 'PNG')
        print(images_from_path)

以下是參數(shù)定義:

convert_from_path(pdf_path, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None, use_cropbox=False, strict=False, transparent=False, single_file=False, output_file=str(uuid.uuid4()), poppler_path=None)

convert_from_bytes(pdf_file, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None, use_cropbox=False, strict=False, transparent=False, single_file=False, output_file=str(uuid.uuid4()), poppler_path=None)

pdf_path --> 要轉(zhuǎn)換的PDF文檔路徑
dpi -->DPI中的圖像質(zhì)量(默認(rèn)為200)一睁,Windows默認(rèn)為96dpi
output_folder --> 將生成的圖像寫入文件夾(而不是直接寫入內(nèi)存)若是path不做指定的話,path的默認(rèn)地址是:C:\Users\pppp\AppData\Local\Temp\生成的uuid4佃却。
first_page --> 從哪一頁開始轉(zhuǎn)換者吁,默認(rèn)是PDF的第一頁
last_page -->轉(zhuǎn)換到哪一頁,默認(rèn)是PDF的最后一頁
fmt --> 輸出圖像格式默認(rèn)格式是ppm饲帅,還可以設(shè)置為png和jpeg等
thread_count --> 允許生成多少個線程進(jìn)行處理复凳,一般不超過4個線程;
userpw --> PDF的密碼(若有密碼的話需要添加)
use_cropbox -->使用cropbox而不是mediabox
strict --> 參數(shù)允許您使用自定義類型PDFSyntaxError捕獲pdftoppm語法錯誤
transparent --> 參數(shù)允許生成沒有背景的圖像灶泵,而不是通常的白色圖像(為此需要pdftocairo)
single_file --> 使用pdftoppm / pdftocairo中的-singlefile選項
output_file --> 輸出文件名是什么
poppler_path --> 查找poppler二進(jìn)制文件的路徑育八,允許用戶使用poppler_path指定poppler的安裝路徑;默認(rèn)不指定的話需要將bin添加到系統(tǒng)PATH

pdf2image應(yīng)該也可以對指定區(qū)域進(jìn)行截取赦邻,暫時還沒詳細(xì)研究其方法髓棋,因為已經(jīng)找到更快的方法解決問題了,對比如下所示:

3、 比較PyMuPDF和pdf2image

以下是對一份75頁的PDF按声,輸出DPI=96的時間性能對比膳犹,pdf2image使用的是默認(rèn)線程數(shù),下面的對比并沒有設(shè)置多線程签则,使用多線程會快一點须床,當(dāng)線程數(shù)設(shè)為5的時候,速度是9秒渐裂。

可以看出使用pyMuPDF_Fitz明顯快一倍多豺旬,最終選取了這種方式。

4柒凉、Wand將PDF轉(zhuǎn)換成圖片

和pdf2image一樣族阅,wand都是包裝接口(bindings),而實際進(jìn)行轉(zhuǎn)換的工具是ImageMagick.
Wind官網(wǎng):http://docs.wand-py.org/en/0.5.6/

ImageMagick: https://imagemagick.org/script/download.php#windows

from wand.image import Image

filename="somefile.pdf"

with(Image(filename=filename, resolution=120)) as source: 
    images = source.sequence
    pages = len(images)
    for i in range(pages):
        n = i + 1
        newfilename = filename[:-4] + str(n) + '.jpeg'
        Image(images[i]).save(filename=newfilename)

由于問題已經(jīng)解決膝捞,而且性能也還不錯,就沒有具體去研究Wind這種方式了绑警,感興趣的可以去看看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末央渣,一起剝皮案震驚了整個濱河市计盒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芽丹,老刑警劉巖北启,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拔第,居然都是意外死亡咕村,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門蚊俺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懈涛,“玉大人,你說我怎么就攤上這事泳猬∨疲” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵得封,是天一觀的道長埋心。 經(jīng)常有香客問我,道長忙上,這世上最難降的妖魔是什么拷呆? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上茬斧,老公的妹妹穿的比我還像新娘腰懂。我一直安慰自己,他們只是感情好啥供,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布悯恍。 她就那樣靜靜地躺著,像睡著了一般伙狐。 火紅的嫁衣襯著肌膚如雪涮毫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天贷屎,我揣著相機(jī)與錄音罢防,去河邊找鬼。 笑死唉侄,一個胖子當(dāng)著我的面吹牛咒吐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播属划,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼恬叹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了同眯?” 一聲冷哼從身側(cè)響起绽昼,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎须蜗,沒想到半個月后硅确,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡明肮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年菱农,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柿估。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡循未,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出官份,到底是詐尸還是另有隱情只厘,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布舅巷,位于F島的核電站羔味,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钠右。R本人自食惡果不足惜赋元,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搁凸,春花似錦媚值、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嫡良,卻和暖如春锰扶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寝受。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工坷牛, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人很澄。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓京闰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親甩苛。 傳聞我的和親對象是個殘疾皇子蹂楣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容