튜토리얼

이 튜토리얼은 Python에서 PyMuPDF, MuPDF 사용법을 단계별로 보여줍니다.

MuPDF 가 PDF뿐만 아니라 XPS, OpenXPS, CBZ, CBR, FB2, EPUB 형식도 지원하므로 PyMuPDF 도 마찬가지입니다 [1]. 그러나 간결성을 위해 PDF 파일만 다룹니다. PDF 파일만 지원되는 경우에는 명시적으로 언급합니다.

이 소개 외에도 PyMuPDFYouTube 채널 을 방문하시기 바랍니다. YouTube “Shorts” 및 더 긴 비디오 형식으로 대부분의 내용을 다루고 있습니다.

바인딩 가져오기

이 import 문으로 MuPDF 에 대한 Python 바인딩을 사용할 수 있습니다. 여기서 버전을 확인하는 방법도 보여줍니다:

>>> import pymupdf
>>> print(pymupdf.__doc__)
PyMuPDF 1.16.0: Python bindings for the MuPDF 1.16.0 library.
Version date: 2019-07-28 07:30:14.
Built for Python 3.7 on win32 (64-bit).

fitz 이름에 대한 참고사항

이전 버전의 PyMuPDFPython import 이름이 fitz 였습니다. 최신 버전은 대신 pymupdf 를 사용하며, 이전 코드가 계속 작동하도록 fitz 를 대체 옵션으로 제공합니다.

fitz 라는 이름의 이유는 역사적인 호기심입니다:

MuPDF 의 원래 렌더링 라이브러리는 Libart 라고 불렸습니다.

“Artifex Software가 MuPDF 프로젝트를 인수한 후, 개발 초점은 “Fitz”라는 새로운 현대적인 그래픽 라이브러리 작성으로 옮겨졌습니다. Fitz는 원래 노후화된 Ghostscript 그래픽 라이브러리를 대체하기 위한 R&D 프로젝트로 의도되었지만, 대신 MuPDF를 구동하는 렌더링 엔진이 되었습니다.” (Wikipedia 에서 인용).

참고

사용되지 않는 pypi.org 패키지 fitz 가 설치된 경우 레거시 이름 fitz 사용이 실패할 수 있습니다. 설치 후 문제 을 참조하세요.

문서 열기

지원되는 문서 에 액세스하려면 다음 문으로 열어야 합니다:

doc = pymupdf.open(filename)  # or pymupdf.Document(filename)

이것은 Document 객체 doc 을 생성합니다. filename 은 기존 파일의 이름을 지정하는 Python 문자열(또는 pathlib.Path)이어야 합니다.

메모리 데이터에서 문서를 열거나 새로운 빈 PDF를 생성할 수도 있습니다. 자세한 내용은 Document 를 참조하세요. Documentcontext manager 로 사용할 수도 있습니다.

문서에는 많은 속성과 함수가 포함되어 있습니다. 그 중에는 메타 정보(“author” 또는 “subject” 등), 총 페이지 수, 개요 및 암호화 정보가 있습니다.

일부 Document 메서드 및 속성

메서드 / 속성

설명

Document.page_count

페이지 수 (int)

Document.metadata

메타데이터 (dict)

Document.get_toc()

목차 가져오기 (list)

Document.load_page()

Page 읽기

메타데이터 액세스

PyMuPDF 는 표준 메타데이터를 완전히 지원합니다. Document.metadata 는 다음 키를 가진 Python 딕셔너리입니다. 모든 문서 타입 에서 사용할 수 있지만, 모든 항목이 항상 데이터를 포함하지는 않을 수 있습니다. 의미와 형식에 대한 자세한 내용은 각 매뉴얼(예: PDF의 경우 Adobe PDF 참조)을 참조하세요. 추가 정보는 Document 장에서도 찾을 수 있습니다. 메타데이터 필드는 달리 표시되지 않는 한 문자열 또는 None 입니다. 또한 모든 필드가 항상 의미 있는 데이터를 포함하는 것은 아니라는 점에 유의하세요. None 이 아니더라도 그렇습니다.

producer

생성자(생성 소프트웨어)

format

형식: ‘PDF-1.4’, ‘EPUB’ 등

encryption

사용된 암호화 방법(있는 경우)

author

author

modDate

마지막 수정 날짜

keywords

keywords

title

title

creationDate

생성 날짜

creator

생성 애플리케이션

subject

subject

참고

이러한 표준 메타데이터 외에도 PDF 버전 1.4부터 시작하는 PDF 문서 는 소위 “metadata streams” (stream 도 참조)를 포함할 수도 있습니다. 이러한 스트림의 정보는 XML로 인코딩됩니다. PyMuPDF 는 의도적으로 이 목적을 위한 XML 구성 요소를 포함하지 않습니다(PyMuPDF Xml classStory 객체의 DOM 콘텐츠에 액세스하기 위한 헬퍼 클래스입니다). 따라서 그 안에 포함된 정보에 대한 직접 액세스를 지원하지 않습니다. 그러나 스트림 전체를 추출하고 lxml 같은 패키지를 사용하여 검사하거나 수정한 다음 결과를 PDF에 다시 저장할 수 있습니다. 원하는 경우 이 데이터를 완전히 삭제할 수도 있습니다.

참고

저장소에는 CSV 파일에서 메타데이터를 가져오거나(metadata import (PDF only)) CSV 파일로 내보내는(metadata export) 두 가지 유틸리티 스크립트가 있습니다.

개요 작업

문서의 모든 개요(“bookmarks”라고도 함)를 가져오는 가장 쉬운 방법은 목차 를 로드하는 것입니다:

toc = doc.get_toc()

이것은 책에서 볼 수 있는 일반적인 목차와 매우 유사한 Python 리스트의 리스트 [[lvl, title, page, …], …] 를 반환합니다.

lvl 은 항목의 계층 레벨(1부터 시작), title 은 항목의 제목, page 는 페이지 번호(1 기반!)입니다. 다른 매개변수는 북마크 대상의 세부 정보를 설명합니다.

참고

저장소에는 CSV 파일에서 목차를 가져오거나(toc import (PDF only)) CSV 파일로 내보내는(toc export) 두 가지 유틸리티 스크립트가 있습니다.

페이지 작업

Page 처리는 MuPDF 기능의 핵심입니다.

  • 페이지를 래스터 또는 벡터(SVG) 이미지로 렌더링할 수 있으며, 선택적으로 확대/축소, 회전, 이동 또는 기울이기를 할 수 있습니다.

  • 페이지의 텍스트와 이미지를 여러 형식으로 추출하고 텍스트 문자열을 검색할 수 있습니다.

  • PDF 문서의 경우 페이지에 텍스트나 이미지를 추가하는 더 많은 메서드를 사용할 수 있습니다.

먼저 Page 를 생성해야 합니다. 이것은 Document 의 메서드입니다:

page = doc.load_page(pno)  # loads page number 'pno' of the document (0-based)
page = doc[pno]  # the short form

여기서는 -∞ < pno < page_count 범위의 정수가 가능합니다. 음수는 끝에서 역순으로 계산되므로 Python 시퀀스와 같이 doc[-1] 은 마지막 페이지입니다.

더 고급 방법은 문서를 페이지에 대한 반복자 로 사용하는 것입니다:

for page in doc:
    # do something with 'page'

# ... or read backwards
for page in reversed(doc):
    # do something with 'page'

# ... or even use 'slicing'
for page in doc.pages(start, stop, step):
    # do something with 'page'

페이지를 얻으면 일반적으로 다음과 같은 작업을 수행합니다:

페이지 렌더링

이 예제는 페이지 콘텐츠의 래스터 이미지를 생성합니다:

pix = page.get_pixmap()

pix 는 (이 경우) 페이지의 RGB 이미지를 포함하는 Pixmap 객체로, 다양한 용도로 사용할 준비가 되어 있습니다. Page.get_pixmap() 메서드는 이미지를 제어하기 위한 많은 변형을 제공합니다: 해상도/DPI, 색 공간(예: 그레이스케일 이미지 또는 감산 색상 체계의 이미지 생성), 투명도, 회전, 미러링, 이동, 기울이기 등. 예를 들어: RGBA 이미지(즉, 알파 채널 포함)를 생성하려면 pix = page.get_pixmap(alpha=True) 를 지정하세요.

Pixmap 은 아래에서 참조되는 많은 메서드와 속성을 포함합니다. 그 중에는 정수 width, height (각각 픽셀 단위) 및 stride (한 수평 이미지 라인의 바이트 수)가 있습니다. 속성 samples 는 이미지 데이터를 나타내는 바이트의 사각형 영역(Python bytes 객체)을 나타냅니다.

참고

Page.get_svg_image() 를 사용하여 페이지의 벡터 이미지를 생성할 수도 있습니다. 자세한 내용은 Vector Image Support page 를 참조하세요.

파일에 페이지 이미지 저장

이미지를 PNG 파일에 간단히 저장할 수 있습니다:

pix.save(f"page-{page.number}.png")

GUI에서 이미지 표시

GUI 대화 상자 관리자에서도 사용할 수 있습니다. Pixmap.samples 는 모든 픽셀의 바이트 영역을 Python bytes 객체로 나타냅니다. 다음은 몇 가지 예입니다. examples 디렉토리에서 더 많은 예를 찾을 수 있습니다.

wxPython

RGB(A) pixmap 조정 및 wxPython 릴리스별 세부 사항에 대해서는 해당 문서를 참조하세요:

if pix.alpha:
    bitmap = wx.Bitmap.FromBufferRGBA(pix.width, pix.height, pix.samples)
else:
    bitmap = wx.Bitmap.FromBuffer(pix.width, pix.height, pix.samples)

Tkinter

Pillow documentation 의 섹션 3.19도 참조하세요:

from PIL import Image, ImageTk

# set the mode depending on alpha
mode = "RGBA" if pix.alpha else "RGB"
img = Image.frombytes(mode, [pix.width, pix.height], pix.samples)
tkimg = ImageTk.PhotoImage(img)

다음은 Pillow 사용을 피합니다:

# remove alpha if present
pix1 = pymupdf.Pixmap(pix, 0) if pix.alpha else pix  # PPM does not support transparency
imgdata = pix1.tobytes("ppm")  # extremely fast!
tkimg = tkinter.PhotoImage(data = imgdata)

지원되는 모든 문서를 페이지로 나누는 완전한 Tkinter 스크립트를 찾고 있다면 여기 있습니다!. 페이지 확대도 가능하며 Python 2 또는 3에서 실행됩니다. 매우 편리한 PySimpleGUI 순수 Python 패키지가 필요합니다.

PyQt4, PyQt5, PySide

Pillow documentation 의 섹션 3.16도 참조하세요:

from PIL import Image, ImageQt

# set the mode depending on alpha
mode = "RGBA" if pix.alpha else "RGB"
img = Image.frombytes(mode, [pix.width, pix.height], pix.samples)
qtimg = ImageQt.ImageQt(img)

다시 말하지만, Pillow를 사용하지 않고도 진행할 수 있습니다. Qt의 QImage 는 다행히 네이티브 Python 포인터를 지원하므로 다음은 Qt 이미지를 생성하는 권장 방법입니다:

from PyQt5.QtGui import QImage

# set the correct QImage format depending on alpha
fmt = QImage.Format_RGBA8888 if pix.alpha else QImage.Format_RGB888
qtimg = QImage(pix.samples_ptr, pix.width, pix.height, fmt)

텍스트 및 이미지 추출

페이지의 모든 텍스트, 이미지 및 기타 정보를 다양한 형식과 세부 수준으로 추출할 수도 있습니다:

text = page.get_text(opt)

다양한 형식을 얻으려면 opt 에 다음 문자열 중 하나를 사용하세요 [2]:

  • “text”: (기본값) 줄바꿈이 있는 일반 텍스트. 서식 없음, 텍스트 위치 세부 정보 없음, 이미지 없음.

  • “blocks”: 텍스트 블록(= 단락) 목록 생성.

  • “words”: 단어 목록 생성(공백을 포함하지 않는 문자열).

  • “html”: 이미지를 포함한 페이지의 전체 시각적 버전 생성. 인터넷 브라우저로 표시할 수 있습니다.

  • “dict” / “json”: HTML과 동일한 정보 수준이지만 Python 딕셔너리 또는 JSON 문자열로 제공됩니다. 구조에 대한 자세한 내용은 TextPage.extractDICT() 를 참조하세요.

  • “rawdict” / “rawjson”: “dict” / “json” 의 상위 집합. XML과 같은 문자 세부 정보를 추가로 제공합니다. 구조에 대한 자세한 내용은 TextPage.extractRAWDICT() 를 참조하세요.

  • “xhtml”: TEXT 버전과 동일한 텍스트 정보 수준이지만 이미지를 포함합니다. 인터넷 브라우저로도 표시할 수 있습니다.

  • “xml”: 이미지는 포함하지 않지만 각 단일 텍스트 문자까지 전체 위치 및 글꼴 정보를 포함합니다. XML 모듈을 사용하여 해석합니다.

이러한 대안의 출력에 대한 아이디어를 제공하기 위해 텍스트 예제 추출을 수행했습니다. 부록 1: 텍스트 추출에 대한 세부사항 을 참조하세요.

텍스트 검색

페이지에서 특정 텍스트 문자열이 정확히 어디에 나타나는지 확인할 수 있습니다:

areas = page.search_for("mupdf")

이것은 사각형 목록(Rect 참조)을 제공하며, 각각은 문자열 “mupdf” 의 한 번 발생을 둘러쌉니다(대소문자 구분 안 함). 이 정보를 사용하여 예를 들어 해당 영역을 강조 표시하거나(PDF만) 문서의 상호 참조를 생성할 수 있습니다.

함께 작업하기: DisplayList 및 TextPage 장과 데모 프로그램 demo.pydemo-lowlevel.py 도 살펴보세요. 특히 성능 고려 사항이 제안될 때 TextPage, Device (디바이스)DisplayList (디스플레이 리스트) 클래스를 더 직접적으로 제어하는 방법에 대한 세부 정보를 포함합니다.

Stories: HTML 소스에서 PDF 생성

Story 클래스는 PyMuPDF 버전 1.21.0의 새로운 기능입니다. MuPDF“story” 인터페이스 지원을 나타냅니다.

다음은 Artifex 의 Robin Watts가 쓴 “MuPDF Explored” 책에서 인용한 내용입니다:


Stories는 Document Writers가 제공하는 것과 같은 장치에서 사용할 수 있도록 스타일이 지정된 콘텐츠를 쉽게 레이아웃하는 방법을 제공합니다(…). story의 개념은 데스크톱 출판에서 나왔으며, 이는 차례로(…) 신문에서 가져온 것입니다. 전통적인 신문 레이아웃을 고려하면 여러 열로 배치되고 여러 페이지에 걸칠 수 있는 다양한 뉴스 기사(stories)로 구성됩니다.

따라서 |MuPDF| 는 스타일 정보가 있는 텍스트 흐름을 나타내기 위해 story를 사용합니다. story 사용자는 story가 배치될 사각형 시퀀스를 제공할 수 있으며, 배치된 텍스트는 출력 장치에 그려질 수 있습니다. 이것은 텍스트 자체(story)의 개념을 텍스트가 흐를 영역(레이아웃)과 분리된 상태로 유지합니다.


참고

Story는 인터넷 브라우저와 다소 유사하게 작동합니다: HTML 하이퍼텍스트와 선택적 스타일시트(CSS)를 충실하게 구문 분석하고 렌더링합니다. 그러나 출력은 PDF 입니다. 웹 페이지가 아닙니다.

Story 를 생성할 때 최대 세 가지 다른 정보 소스의 입력이 고려됩니다. 이러한 항목은 모두 선택 사항입니다.

  1. HTML 소스 코드, Python 문자열이거나 Xml 의 메서드를 사용하여 스크립트로 생성된 것.

  2. CSS(Cascaded Style Sheet) 소스 코드, Python 문자열로 제공됩니다. CSS는 웹 페이지에서와 같이 스타일 정보(텍스트 글꼴 크기, 색상 등)를 제공하는 데 사용할 수 있습니다. 명백히 이 문자열은 파일에서 읽을 수도 있습니다.

  3. DOM이 이미지를 참조하거나 표준 PDF Base 14 글꼴, CJK 글꼴 및 PyMuPDF 바이너리에 생성된 NOTO 글꼴을 제외한 텍스트 글꼴을 사용할 때마다 Archive (아카이브) 를 사용해야 합니다.

API 는 원하는 스타일 정보를 포함하여 처음부터 DOM을 생성할 수 있게 합니다. 또한 제공된 HTML을 수정하거나 확장하는 데 사용할 수 있습니다: 텍스트를 삭제하거나 교체하거나 스타일을 변경할 수 있습니다. 데이터베이스에서 추출한 것과 같은 텍스트도 추가하고 템플릿과 같은 HTML 문서를 채울 수 있습니다.

구문적으로 완전한 HTML 문서를 제공할 필요는 없습니다: <b>Hello 와 같은 스니펫이 완전히 허용되며 많은/대부분의 구문 오류가 자동으로 수정됩니다.

HTML이 완료된 것으로 간주되면 PDF 문서를 생성하는 데 사용할 수 있습니다. 이것은 새로운 DocumentWriter 클래스를 통해 수행됩니다. 프로그래머는 메서드를 호출하여 새로운 빈 페이지를 만들고 Story에 사각형을 전달하여 채웁니다.

story는 차례로 더 많은 콘텐츠가 작성되기를 기다리고 있는지 여부를 나타내는 완료 코드를 반환합니다. 콘텐츠의 어느 부분이 어떤 사각형이나 어떤 페이지에 배치될지는 story 자체에 의해 자동으로 결정됩니다. 사각형을 제공하는 것 외에는 영향을 줄 수 없습니다.

일반적인 사용 사례는 Stories recipes 를 참조하세요.

PDF 유지보수

PDF는 PyMuPDF 를 사용하여 수정 할 수 있는 유일한 문서 타입입니다. 다른 파일 타입은 읽기 전용입니다.

그러나 모든 문서 (이미지 포함)를 PDF로 변환한 다음 변환 결과에 모든 PyMuPDF 기능을 적용할 수 있습니다. 자세한 내용은 Document.convert_to_pdf() 를 참조하고, 모든 :ref:` 지원되는 문서<Supported_File_Types>` 를 PDF로 변환할 수 있는 데모 스크립트 pdf-converter.py 도 살펴보세요.

Document.save() 는 항상 PDF를 현재(잠재적으로 수정된) 상태로 디스크에 저장합니다.

일반적으로 새 파일에 저장할지 또는 기존 파일에 수정 사항만 추가할지(“incremental save”) 선택할 수 있으며, 후자가 종종 훨씬 빠릅니다.

다음은 PDF 문서를 조작하는 방법을 설명합니다. 이 설명은 결코 완전하지 않습니다. 다음 장에서 더 많은 내용을 찾을 수 있습니다.

페이지 수정, 생성, 재배열 및 삭제

PDF의 소위 페이지 트리 (모든 페이지를 설명하는 구조)를 조작하는 여러 방법이 있습니다:

Document.delete_page()Document.delete_pages() 는 페이지를 삭제합니다.

Document.copy_page(), Document.fullcopy_page()Document.move_page() 는 페이지를 같은 문서 내의 다른 위치로 복사하거나 이동합니다.

Document.select() 는 PDF를 선택한 페이지로 축소합니다. 매개변수는 유지하려는 페이지 번호의 시퀀스 [3] 입니다. 이러한 정수는 모두 0 <= i < page_count 범위에 있어야 합니다. 실행되면 이 목록에 없는 모든 페이지가 삭제됩니다. 남은 페이지는 시퀀스에서 지정한 횟수만큼(!) 나타납니다.

따라서 다음과 같은 새 PDF를 쉽게 만들 수 있습니다:

  • 처음 또는 마지막 10페이지,

  • 홀수 페이지만 또는 짝수 페이지만(양면 인쇄용),

  • 주어진 텍스트를 포함하는 또는 포함하지 않는 페이지,

  • 페이지 시퀀스 반전, …

… 생각할 수 있는 모든 것.

저장된 새 문서는 여전히 유효한 링크, 주석 및 북마크를 포함합니다(즉, 선택한 페이지 또는 일부 외부 리소스를 가리킴).

Document.insert_page()Document.new_page() 는 새 페이지를 삽입합니다.

페이지 자체는 다양한 메서드(예: 페이지 회전, 주석 및 링크 유지보수, 텍스트 및 이미지 삽입)로 수정할 수 있습니다.

PDF 문서 결합 및 분할

Document.insert_pdf() 메서드는 다른 PDF 문서 간에 페이지를 복사합니다. 다음은 간단한 결합 예제입니다(doc1doc2 는 열린 PDF):

# append complete doc2 to the end of doc1
doc1.insert_pdf(doc2)

다음은 doc1분할 하는 스니펫입니다. 처음 10페이지와 마지막 10페이지로 새 문서를 만듭니다:

doc2 = pymupdf.open()                 # new empty PDF
doc2.insert_pdf(doc1, to_page = 9)  # first 10 pages
doc2.insert_pdf(doc1, from_page = len(doc1) - 10) # last 10 pages
doc2.save("first-and-last-10.pdf")

더 많은 내용은 Document 장에서 찾을 수 있습니다. PDFjoiner.py 도 살펴보세요.

데이터 임베딩

PDF는 ZIP 아카이브와 매우 유사하게 임의의 데이터(실행 파일, 다른 PDF, 텍스트 또는 바이너리 파일 등)의 컨테이너로 사용할 수 있습니다.

PyMuPDF fully supports this feature via Document embfile_* methods and attributes. For some detail read 부록 2: 임베디드 파일에 대한 고려사항, consult the Wiki on dealing with embedding files, or the example scripts embedded-copy.py, embedded-export.py, embedded-import.py, and embedded-list.py.

저장

위에서 언급한 대로 Document.save()항상 문서를 현재 상태로 저장합니다.

incremental=True 옵션을 지정하여 변경 사항을 원본 PDF 에 다시 쓸 수 있습니다. 이 프로세스는 (일반적으로) 매우 빠릅니다. 파일을 완전히 다시 쓰지 않고 변경 사항을 원본 파일에 추가 하기 때문입니다.

Document.save() 옵션은 MuPDF 의 명령줄 유틸리티 mutool clean 의 옵션에 해당합니다. 다음 표를 참조하세요.

저장 옵션

mutool

효과

garbage=1

g

사용하지 않는 객체 가비지 수집

garbage=2

gg

1에 추가하여 xref 테이블 압축

garbage=3

ggg

2에 추가하여 중복 객체 병합

garbage=4

gggg

3에 추가하여 중복 스트림 콘텐츠 병합

clean=True

cs

콘텐츠 스트림 정리 및 정화

deflate=True

z

압축되지 않은 스트림 압축

deflate_images=True

i

이미지 스트림 압축

deflate_fonts=True

f

글꼴 파일 스트림 압축

ascii=True

a

바이너리 데이터를 ASCII 형식으로 변환

linear=True

l

선형화된 버전 생성

expand=True

d

모든 스트림 압축 해제

참고

object, stream, xref 와 같은 용어에 대한 설명은 용어집 장을 참조하세요.

예를 들어 mutool clean -ggggz file.pdf 는 우수한 압축 결과를 제공합니다. 이것은 doc.save(filename, garbage=4, deflate=True) 에 해당합니다.

닫기

프로그램이 계속 실행되는 동안 기본 파일에 대한 제어를 OS에 반환하기 위해 문서를 “닫는” 것이 종종 바람직합니다.

이것은 Document.close() 메서드로 달성할 수 있습니다. 기본 파일을 닫는 것 외에도 문서와 연결된 버퍼 영역이 해제됩니다.

추가 읽기

PyMuPDFWiki 페이지도 살펴보세요. 특히 사이드바의 “Recipes” 제목 아래에 나열된 항목은 “How-To” 스타일로 작성된 15개 이상의 주제를 다룹니다.

이 문서에는 자주 묻는 질문 도 포함되어 있습니다. 이 장은 앞서 언급한 recipes와 밀접한 관련이 있으며, 시간이 지나면서 더 많은 콘텐츠로 확장될 것입니다.


각주

This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at artifex.com or contact Artifex Software Inc., 39 Mesa Street, Suite 108A, San Francisco CA 94129, United States for further information.