PyMuPDF Documentation

PyMuPDF is a high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents.

10,083 stars Python Python 1 file ยท ~30,078 tokens #data-science#epub#extract-data#font#mupdf#ocr#pdf#pdf-documents
RAW Doc

File: README.md

PyMuPDF











The PDF engine behind over 50 million monthly downloads, powering AI pipelines worldwide.

PyMuPDF is a high-performance Python library for data extraction, analysis, conversion, rendering and manipulation of PDF (and other) documents. Built on top of MuPDF โ€” a lightweight, fast C engine โ€” PyMuPDF gives you precise, low-level control over documents alongside high-level convenience APIs. No mandatory external dependencies.


Why PyMuPDF?

  • Fast โ€” powered by MuPDF, a best-in-class C rendering engine
  • Accurate โ€” pixel-perfect text extraction with font, color, and position metadata
  • Versatile โ€” read, write, annotate, redact, merge, split, and convert documents
  • LLM-ready โ€” native Markdown output via PyMuPDF4LLM for RAG and AI pipelines
  • No mandatory dependencies โ€” pip install pymupdf and you're done

Installation

bash
pip install pymupdf

Wheels are available for Windows, macOS, and Linux on Python 3.10โ€“3.14. If no pre-built wheel exists for your platform, pip will compile from source (requires a C/C++ toolchain).

Optional extras

Package Purpose
pymupdf-fonts Extended font collection for text output
pymupdf4llm LLM/RAG-optimised Markdown and JSON extraction
pymupdfpro Adds Office document support
tesseract-ocr OCR for scanned pages and images (separate install)
bash
# More fonts
pip install pymupdf-fonts

# LLM-ready extraction
pip install pymupdf4llm

# Office support
pip install pymupdfpro

# OCR (Tesseract must be installed separately)
# macOS
brew install tesseract

# Ubuntu / Debian
sudo apt install tesseract-ocr

Supported File Formats

Input

Category Formats
PDF & derivatives PDF, XPS, EPUB, CBZ, MOBI, FB2, SVG, TXT, MD
Images PNG, JPEG, BMP, TIFF, GIF, and more
Microsoft Office (Pro) DOC, DOCX, XLS, XLSX, PPT, PPTX
Korean Office (Pro) HWP, HWPX

Output

Format Notes
PDF Full fidelity conversion from Office formats
SVG Vector page rendering
Image (PNG, JPEG, โ€ฆ) Page rasterisation at any DPI
Markdown Structure-aware, LLM-ready
JSON Bounding boxes, layout data, per-element detail
Plain text Fast, lightweight extraction

Quick start

Extract text

python
import pymupdf

doc = pymupdf.open("document.pdf")
for page in doc:
    print(page.get_text())

Extract text with layout metadata

python
import pymupdf

doc = pymupdf.open("document.pdf")
page = doc[0]

blocks = page.get_text("dict")["blocks"]
for block in blocks:
    if block["type"] == 0:  # text block
        for line in block["lines"]:
            for span in line["spans"]:
                print(f"{span['text']!r}  font={span['font']}  size={span['size']:.1f}")

Extract tables

python
import pymupdf

doc = pymupdf.open("spreadsheet.pdf")
page = doc[0]

tables = page.find_tables()
for table in tables:
    print(table.to_markdown())

    # or get as Pandas DataFrame
    df = table.to_pandas()

Render a page to an image

python
import pymupdf

doc = pymupdf.open("document.pdf")
page = doc[0]

pixmap = page.get_pixmap(dpi=150)
pixmap.save("page_0.png")

OCR a scanned document

python
import pymupdf

doc = pymupdf.open("scanned.pdf")
page = doc[0]

# Requires Tesseract installed and on PATH
text = page.get_textpage_ocr(language="eng").extractText()
print(text)

Convert Markdown to PDF

python
import pymupdf

md_doc = pymupdf.open("example.md")
md_doc.save("example.pdf")

Convert to Markdown for LLMs

python
import pymupdf4llm

md = pymupdf4llm.to_markdown("report.pdf")
# Pass directly to your LLM or vector store
print(md)

Annotate and redact

python
import pymupdf

doc = pymupdf.open("contract.pdf")
page = doc[0]

# Add a highlight annotation
rect = pymupdf.Rect(72, 100, 400, 120)
page.add_highlight_annot(rect)

# Add a redaction and apply it
page.add_redact_annot(rect)
page.apply_redactions()

doc.save("contract_redacted.pdf")

Merge PDFs

python
import pymupdf

merger = pymupdf.open()
for path in ["part1.pdf", "part2.pdf", "part3.pdf"]:
    merger.insert_pdf(pymupdf.open(path))

merger.save("merged.pdf")

Convert an Office document to PDF

python
import pymupdf.pro

pymupdf.pro.unlock("YOUR-LICENSE-KEY")

doc = pymupdf.open("presentation.pptx")
pdf_bytes = doc.convert_to_pdf()

with open("output.pdf", "wb") as f:
    f.write(pdf_bytes)

Extract LLM-ready Markdown from a Word document

python
import pymupdf4llm
import pymupdf.pro

pymupdf.pro.unlock("YOUR-LICENSE-KEY")

md = pymupdf4llm.to_markdown("document.docx")
print(md)

Features

Core capabilities

Feature Description
Text extraction Plain text, rich dict (font, size, color, bbox), HTML, XML, raw blocks
Table detection find_tables() โ€” locate, extract, and export tables as Markdown or structured data
Image extraction Extract embedded images and render any page to a high-resolution Pixmap
Rendering Render PDF pages to images or Pixmap data for use in UI or other workflows
OCR Tesseract integration โ€” full-page or partial OCR, configurable language
Annotations Read and write highlights, underlines, squiggly lines, sticky notes, free text, ink, stamps
Redaction Add and permanently apply redaction annotations
Forms Read and fill PDF AcroForm fields
PDF creation Create PDFs directly with the API or quickly convert from Markdown files
PDF editing Insert, delete, and reorder pages; set metadata; merge and split documents
Drawing Draw lines, curves, rectangles, and circles; insert HTML boxes
Encryption Open password-protected PDFs; save with RC4 or AES encryption
Links Extract hyperlinks, internal cross-references, and URI targets
Bookmarks Read and write the outline / table of contents tree
Metadata Title, author, creation date, producer, subject, and custom entries
Color spaces RGB, CMYK, greyscale; color space conversion

LLM & AI output (via PyMuPDF4LLM)

Output API
Markdown pymupdf4llm.to_markdown(path)
JSON pymupdf4llm.to_json(path)
Plain text pymupdf4llm.to_text(path)

Supports multi-column layouts, natural reading order and page chunking.


Supported Python versions

Python 3.10 โ€“ 3.14 (as of v1.27.x). Wheels ship for:

  • manylinux x86_64 and aarch64
  • musllinux x86_64
  • macOS x86_64 and arm64
  • Windows x86 and x86_64

Performance

PyMuPDF is built on MuPDF โ€” one of the fastest PDF rendering engines available. Typical benchmarks against pure-Python PDF libraries show 10โ€“50ร— speed improvements for text extraction and 100ร— or more for page rendering, with a minimal memory footprint.

For AI workloads, PyMuPDF4LLM processes documents without a GPU, cutting infrastructure costs significantly compared to vision-based LLM approaches.


Recipes

Extract all images from a PDF

python
import pymupdf
from pathlib import Path

doc = pymupdf.open("document.pdf")
out = Path("images")
out.mkdir(exist_ok=True)

for page_index, page in enumerate(doc):
    for img_index, img in enumerate(page.get_images()):
        xref = img[0]
        pix = pymupdf.Pixmap(doc, xref)
        if pix.n > 4:  # convert CMYK
            pix = pymupdf.Pixmap(pymupdf.csRGB, pix)
        pix.save(out / f"page{page_index}_img{img_index}.png")

Search for text across a document

python
import pymupdf

doc = pymupdf.open("document.pdf")
needle = "confidential"

for page in doc:
    hits = page.search_for(needle)
    if hits:
        print(f"Page {page.number}: {len(hits)} occurrence(s)")
        for rect in hits:
            page.add_highlight_annot(rect)

doc.save("highlighted.pdf")

Split a PDF into individual pages

python
import pymupdf

doc = pymupdf.open("document.pdf")
for i, page in enumerate(doc):
    out = pymupdf.open()
    out.insert_pdf(doc, from_page=i, to_page=i)
    out.save(f"page_{i + 1}.pdf")

Insert a watermark on every page

python
import pymupdf

doc = pymupdf.open("document.pdf")
for page in doc:
    page.insert_text(
        point=pymupdf.Point(72, page.rect.height / 2),
        text="DRAFT",
        fontsize=72,
        color=(0.8, 0.8, 0.8),
        rotate=45,
    )

doc.save("watermarked.pdf")

Office Document Processing

PyMuPDF can be extended with PyMuPDF Pro. This adds a conversion layer that handles Microsoft and Korean Office formats natively โ€” no Office installation, no COM interop, no LibreOffice subprocess.

Once unlocked, pymupdf.open() accepts Office files exactly like PDFs:

python
import pymupdf.pro
pymupdf.pro.unlock("YOUR-LICENSE-KEY")

# Works identically regardless of format
for fmt in ["contract.docx", "data.xlsx", "deck.pptx", "report.hwpx"]:
    doc = pymupdf.open(fmt)
    for page in doc:
        print(page.get_text())

Get a trial license key for PyMuPDF Pro

What you can do with Office documents:

  • Extract text and images page-by-page
  • Convert to PDF with doc.convert_to_pdf()
  • Rasterise pages to PNG/JPEG for visual inspection
  • Feed directly into PyMuPDF4LLM for AI-ready output

Restrictions Without a License Key

When pymupdf.pro.unlock() is called without a key, the following restrictions apply:

Restriction Detail
Page limit Only the first 3 pages of any document are accessible
Time limit Evaluation period โ€” functionality expires after a set duration

All other Pro features work normally within these constraints, making it straightforward to prototype before purchasing a license.


Frequently Asked Questions

Can I use PyMuPDF, PyMuPDF4LLM and PyMuPDF Pro without sending data to the cloud?

Yes, absolutely โ€” and this is one of PyMuPDF's most significant advantages.

PyMuPDF runs entirely locally. It is a native Python library built on top of the MuPDF C engine. When you call pymupdf.open(), page.get_text(), page.find_tables(), or any other method, everything executes in-process on your own machine. No data is transmitted anywhere.

There are no telemetry calls, no licence validation callbacks, no cloud dependencies of any kind in the open-source AGPL build or the commercial build. Once the package is installed, it works fully air-gapped.

This makes PyMuPDF well-suited for:

  • Regulated industries โ€” healthcare (HIPAA), finance, legal, government, where documents cannot leave a controlled environment
  • On-premise deployments โ€” servers with no outbound internet access
  • Air-gapped systems โ€” classified or sensitive environments
  • Self-hosted RAG pipelines โ€” processing confidential documents locally before feeding an on-premise LLM
  • Saving on token costs for document pre-processing before sending data to your LLM

The only thing you need an internet connection for is the initial pip install. After that, the package and all its capabilities are entirely self-contained.

Should I `import pymupdf` or `import fitz`?

Use import pymupdf. The fitz name is a legacy alias that still works as of v1.24.0+, but import pymupdf is the recommended and future-proof approach. The two are interchangeable in existing code:

python
import pymupdf          # recommended
# import fitz           # legacy alias โ€” still works but avoid for new code

Does PyMuPDF work with Korean, Japanese, or Chinese documents?

Yes โ€” PyMuPDF has solid CJK support

How do I extract Markdown from PDF for LLM?

Let PyMuPDF4LLM do everything (recommended for RAG).

PyMuPDF4LLM is a high-level wrapper that outputs standard text and table content together in an integrated Markdown-formatted string across all document pages PyMuPDF โ€” tables are detected, converted to GitHub-compatible Markdown, and interleaved with surrounding text in the correct reading order. This is the best starting point for feeding an LLM or building a RAG pipeline.

python
import pymupdf4llm

md = pymupdf4llm.to_markdown("report.pdf")
print(md)
# Tables appear as Markdown | col1 | col2 | ... inline with the text

Text extraction returns garbled characters or empty output. Why?

This usually means the PDF uses custom font encodings without a proper character map (CMAP). The font's glyphs are present but cannot be mapped back to Unicode. In these cases:

  • Use OCR as a fallback (page.get_textpage_ocr())
  • Consider that scanned PDFs will always need OCR โ€” text extraction on scans returns nothing

How do I extract text from a specific area of a page?

Pass a clip rectangle to get_text():

python
import pymupdf

doc = pymupdf.open("input.pdf")
page = doc[0]

# Define the area you want (x0, y0, x1, y1) in points
clip = pymupdf.Rect(50, 100, 400, 300)
text = page.get_text("text", clip=clip)

How do I search for text and find its location on the page?

python
import pymupdf

doc = pymupdf.open("input.pdf")
page = doc[0]

# Returns a list of Rect objects surrounding each match
locations = page.search_for("invoice number")
for rect in locations:
    print(rect)  # e.g. Rect(72.0, 120.5, 210.0, 134.0)

`get_images` shows no images but I can clearly see charts in the PDF. Why?

Charts and diagrams created by tools like matplotlib, Excel, or R are typically rendered as vector graphics (PDF drawing commands), not raster images. get_images only lists embedded raster image objects and will not detect vector graphics. To capture these, rasterise the entire page with page.get_pixmap().

How does OCR work in PyMuPDF? Does it require a separate Tesseract installation?

PyMuPDF uses MuPDF's built-in Tesseract-based OCR support, so there is no Python-level pytesseract dependency. However, PyMuPDF still needs access to the Tesseract language data files (tessdata), and automatic tessdata discovery may invoke the tesseract executable (for example, to list available languages) if you do not explicitly provide a tessdata path. In practice, the recommended setup is to either install Tesseract so discovery works automatically, or configure the tessdata location yourself via the tessdata parameter or the TESSDATA_PREFIX environment variable. Over 100 languages are supported.

python
import pymupdf

doc = pymupdf.open("scanned.pdf")
page = doc[0]

# Get a text page using OCR
tp = page.get_textpage_ocr(language="eng")
text = page.get_text(textpage=tp)
print(text)

How do I run OCR on a standalone image file (not a PDF)?

python
import pymupdf

pix = pymupdf.Pixmap("image.png")
if pix.alpha:
    pix = pymupdf.Pixmap(pix, 0)  # remove alpha channel โ€” required for OCR

# Wrap in a 1-page PDF and OCR it
doc = pymupdf.open()
page = doc.new_page(width=pix.width, height=pix.height)
page.insert_image(page.rect, pixmap=pix)
tp = page.get_textpage_ocr()
text = page.get_text(textpage=tp)

How do I highlight text in a PDF?

python
import pymupdf

doc = pymupdf.open("input.pdf")
page = doc[0]

# Use quads=True for accurate highlights on non-horizontal text
quads = page.search_for("important term", quads=True)
page.add_highlight_annot(quads)

doc.save("highlighted.pdf")

PyMuPDF supports all standard PDF text markers: highlight, underline, strikeout, and squiggly.

How do I permanently redact (remove) content from a PDF?

Redaction is a deliberate two-step process so you can review before committing:

python
import pymupdf

doc = pymupdf.open("input.pdf")
page = doc[0]

# Step 1: Mark the area(s) to redact
rect = page.search_for("confidential")[0]
page.add_redact_annot(rect, fill=(1, 1, 1))  # white fill

# Step 2: Apply โ€” permanently removes the underlying content
page.apply_redactions()

doc.save("redacted.pdf")

After apply_redactions(), the original content is gone. It cannot be recovered from the saved file.

How do I read form field values from a PDF?

python
import pymupdf

doc = pymupdf.open("form.pdf")
page = doc[0]

for field in page.widgets():
    print(f"{field.field_name}: {field.field_value}")

How do I fill in a PDF form programmatically?

python
import pymupdf

doc = pymupdf.open("form.pdf")
page = doc[0]

for field in page.widgets():
    if field.field_name == "First Name":
        field.field_value = "Ada"
        field.update()

doc.save("filled_form.pdf")

Can I use multithreading with PyMuPDF?

No. PyMuPDF does not support multithreaded use, even with Python's newer free-threading mode. The underlying MuPDF library only provides partial thread safety, and a fully thread-safe PyMuPDF implementation would still impose a single-threaded overhead โ€” negating the benefit.

Use multiprocessing instead. Each process opens the file independently and works on its own page range:

python
from multiprocessing import Pool
import pymupdf

def process_pages(args):
    path, start, end = args
    doc = pymupdf.open(path)  # each process opens its own handle
    results = []
    for i in range(start, end):
        results.append(doc[i].get_text())
    return results

with Pool(4) as pool:
    chunks = [("input.pdf", 0, 25), ("input.pdf", 25, 50), ...]
    all_results = pool.map(process_pages, chunks)

How can I speed up repeated text extraction on the same page?

Reuse a TextPage object. Creating a TextPage is the expensive part โ€” once created, switching between extraction formats is cheap:

python
import pymupdf

page = doc[0]
tp = page.get_textpage()  # create once

text  = page.get_text("text",    textpage=tp)
words = page.get_text("words",   textpage=tp)
data  = page.get_text("dict",    textpage=tp)

This can reduce execution time by 50โ€“95% for repeated extractions on the same page.

How do I read and write PDF metadata?

python
import pymupdf

doc = pymupdf.open("input.pdf")

# Read
print(doc.metadata)
# {'title': '...', 'author': '...', 'subject': '...', 'keywords': '...', ...}

# Write
doc.set_metadata({
    "title": "Annual Report 2025",
    "author": "Finance Team",
    "keywords": "annual, finance, 2025"
})
doc.save("output.pdf")

How do I read or set the table of contents / bookmarks?

python
import pymupdf

doc = pymupdf.open("input.pdf")

# Read โ€” returns a list of [level, title, page_number] entries
toc = doc.get_toc()
for level, title, page in toc:
    print(" " * level, title, "โ†’ page", page)

# Write
new_toc = [
    [1, "Introduction",  1],
    [1, "Methods",       5],
    [2, "Data sources",  6],
]
doc.set_toc(new_toc)
doc.save("output.pdf")

Documentation

Full installation guide, API reference, cookbook, and tutorial at pymupdf.readthedocs.io.


Project Description
PyMuPDF4LLM LLM/RAG-optimised Markdown and JSON extraction
PyMuPDF Pro Adds Office and HWP document support
pymupdf-fonts Extended font collection for PyMuPDF text output

Licensing

PyMuPDF and MuPDF are maintained by Artifex Software, Inc.

  • Open source โ€” GNU AGPL v3. Free for open-source projects.
  • Commercial โ€” separate commercial licences available from Artifex for proprietary applications.

Contributing

Contributions are welcome. Please open an issue before submitting large pull requests.

โญ Support this project

If you find this useful, please consider giving it a star โ€” it helps others discover it!


File: docs/algebra.rst

.. include:: header.rst

.. _Algebra:

Operator Algebra for Geometry Objects

.. highlight:: python

Instances of classes :ref:Point, :ref:IRect, :ref:Rect, :ref:Quad and :ref:Matrix are collectively also called "geometry" objects.

They all are special cases of Python sequences, see :ref:SequenceTypes for more background.

We have defined operators for these classes that allow dealing with them (almost) like ordinary numbers in terms of addition, subtraction, multiplication, division, and some others.

This chapter is a synopsis of what is possible.

General Remarks

  1. Operators can be either binary (i.e. involving two objects) or unary.

  2. The resulting type of binary operations is either a new object of the left operand's class, a bool or (for dot products) a float.

  3. The result of unary operations is either a new object of the same class, a bool or a float.

  4. The binary operators +, -, *, / are defined for all classes. They roughly do what you would expect -- except, that the second operand ...

    • may always be a number which then performs the operation on every component of the first one,
    • may always be a numeric sequence of the same length (2, 4 or 6) -- we call such sequences :data:point_like, :data:rect_like, :data:quad_like or :data:matrix_like, respectively.
  5. Rectangles support additional binary operations: intersection (operator "&"), union (operator "|") and containment checking.

  6. Binary operators fully support in-place operations. So if "ยฐ" is a binary operator then the expression a ยฐ= b is always valid and the same as a = a ยฐ b. Therefore, be careful and do not do p1 *= p2 for two points, because thereafter "p1" is a float.

Unary Operations

=========== ===================================================================
Oper. Result
=========== ===================================================================
bool(OBJ) is false exactly if all components of OBJ are zero
abs(OBJ) the rectangle area -- equal to norm(OBJ) for the other types
norm(OBJ) square root of the component squares (Euclidean norm)
+OBJ new copy of OBJ
-OBJ new copy of OBJ with negated components
~m inverse of matrix "m", or the null matrix if not invertible
=========== ===================================================================

Binary Operations

These are expressions like a ยฐ b where "ยฐ" is any of the operators +, -, *, /. Also binary operations are expressions of the form a == b and b in a.

If "b" is a number, then the respective operation is executed for each component of "a". Otherwise, if "b" is not a number, then the following happens:

========= ===========================================================================
Oper. Result
========= ===========================================================================
a+b, a-b component-wise execution, "b" must be "a-like".
am, a/m "a" can be a point, rectangle or matrix and "m" is a
:data:matrix_like. "a/m" is treated as "a~m"
(see note below
for non-invertible matrices). If "a" is a point or a rectangle,
then "a.transform(m)" is executed. If "a" is a matrix, then
matrix concatenation takes place.
a*b returns the vector dot product for a point "a" and point-like "b".
a&b intersection rectangle: "a" must be a rectangle and
"b" :data:rect_like. Delivers the largest rectangle
contained in both operands.
a|b union rectangle: "a" must be a rectangle, and "b" may be
:data:point_like or :data:rect_like.
Delivers the smallest rectangle containing both operands.
b in a if "b" is a number, then b in tuple(a) is returned.
If "b" is :data:point_like, :data:rect_like or :data:quad_like,
then "a" must be a rectangle, and a.contains(b) is returned.
a == b True if bool(a-b) is False ("b" may be "a-like").
========= ===========================================================================

.. note:: Please note an important difference to usual arithmetic:

text
Matrix multiplication is **not commutative**, i.e. in general we have `m*n != n*m` for two matrices. Also, there are non-zero matrices which have no inverse, for example `m = Matrix(1, 0, 1, 0, 1, 0)`. If you try to divide by any of these, you will receive a `ZeroDivisionError` exception using operator *"/"*, e.g. for the expression `pymupdf.Identity / m`. But if you formulate `pymupdf.Identity * ~m`, the result will be `pymupdf.Matrix()` (the null matrix).

    Admittedly, this represents an inconsistency, and we are considering to remove it. For the time being, you can choose to avoid an exception and check whether ~m is the null matrix, or accept a potential *ZeroDivisionError* by using `pymupdf.Identity / m`.

.. note::

  • With these conventions, all the usual algebra rules apply. For example, arbitrarily using brackets (among objects of the same class!) is possible: if r1, r2 are rectangles and m1, m2 are matrices, you can do this (r1 + r2) * m1 * m2.
  • For all objects of the same class, a + b + c == (a + b) + c == a + (b + c) is true.
  • For matrices in addition the following is true: (m1 + m2) * m3 == m1 * m3 + m2 * m3 (distributivity property).
  • But the sequence of applying matrices is important: If r is a rectangle and m1, m2 are matrices, then -- caution!:
    • r * m1 * m2 == (r * m1) * m2 != r * (m1 * m2)

Some Examples

Manipulation with numbers

text
For the usual arithmetic operations, numbers are always allowed as second operand. In addition, you can formulate `"x in OBJ"`, where x is a number. It is implemented as `"x in tuple(OBJ)"`::

  >>> pymupdf.Rect(1, 2, 3, 4) + 5
  pymupdf.Rect(6.0, 7.0, 8.0, 9.0)
  >>> 3 in pymupdf.Rect(1, 2, 3, 4)
  True
  >>> 

The following will create the upper left quarter of a document page rectangle::

  >>> page.rect
  Rect(0.0, 0.0, 595.0, 842.0)
  >>> page.rect / 2
  Rect(0.0, 0.0, 297.5, 421.0)
  >>> 

The following will deliver the **middle point of a line** that connects two points **p1** and **p2**::

  >>> p1 = pymupdf.Point(1, 2)
  >>> p2 = pymupdf.Point(4711, 3141)
  >>> mp = (p1 + p2) / 2
  >>> mp
  Point(2356.0, 1571.5)
  >>> 

Compute the **vector dot product** of two points. You can compute the **cosine of angles** and check orthogonality.

  >>> p1 = pymupdf.Point(1, 0)
  >>> p2 = pymupdf.Point(1, 1)
  >>> dot = p1 * p2
  >>> dot
  1.0

  >>> # compute the cosine of the angle between p1 and p2:
  >>> cosine = dot / (abs(p1) * abs(p2))
  >>> cosine  # cosine of 45 degrees
  0.7071067811865475

  >>> math.cos(mat.radians(45))  # verify:
  0.7071067811865476

  >>> # check orhogonality
  >>> p3 = pymupdf.Point(0, 1)
  >>> # p1 and p3 are orthogonal so, as expected:
  >>> p1 * p3  
  0.0


Manipulation with "like" Objects

The second operand of a binary operation can always be "like" the left operand. "Like" in this context means "a sequence of numbers of the same length". With the above examples::

p1 + p2
Point(4712.0, 3143.0)
p1 + (4711, 3141)
Point(4712.0, 3143.0)
p1 += (4711, 3141)
p1
Point(4712.0, 3143.0)

To shift a rectangle for 5 pixels to the right, do this::

pymupdf.Rect(100, 100, 200, 200) + (5, 0, 5, 0) # add 5 to the x coordinates
Rect(105.0, 100.0, 205.0, 200.0)

Points, rectangles and matrices can be transformed with matrices. In PyMuPDF, we treat this like a "multiplication" (or resp. "division"), where the second operand may be "like" a matrix. Division in this context means "multiplication with the inverted matrix"::

m = pymupdf.Matrix(1, 2, 3, 4, 5, 6)
n = pymupdf.Matrix(6, 5, 4, 3, 2, 1)
p = pymupdf.Point(1, 2)
p * m
Point(12.0, 16.0)
p * (1, 2, 3, 4, 5, 6)
Point(12.0, 16.0)
p / m
Point(2.0, -2.0)
p / (1, 2, 3, 4, 5, 6)
Point(2.0, -2.0)

m * n # matrix multiplication
Matrix(14.0, 11.0, 34.0, 27.0, 56.0, 44.0)
m / n # matrix division
Matrix(2.5, -3.5, 3.5, -4.5, 5.5, -7.5)

m / m # result is equal to the Identity matrix
Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)

look at this non-invertible matrix:

m = pymupdf.Matrix(1, 0, 1, 0, 1, 0)
~m
Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

we try dividing by it in two ways:

p = pymupdf.Point(1, 2)
p * ~m # this delivers point (0, 0):
Point(0.0, 0.0)
p / m # but this is an exception:
Traceback (most recent call last):
File "", line 1, in
p / m
File "... /site-packages/fitz/pymupdf.py", line 869, in truediv
raise ZeroDivisionError("matrix not invertible")
ZeroDivisionError: matrix not invertible

As a specialty, rectangles support additional binary operations:

  • intersection -- the common area of rectangle-likes, operator "&"
  • inclusion -- enlarge to include a point-like or rect-like, operator "|"
  • containment check -- whether a point-like or rect-like is inside

Here is an example for creating the smallest rectangle enclosing given points::

first define some point-likes

points = []
for i in range(10):
for j in range(10):
points.append((i, j))

now create a rectangle containing all these 100 points

start with an empty rectangle

r = pymupdf.Rect(points[0], points[0])
for p in points[1:]: # and include remaining points one by one
r |= p

r # here is the to be expected result:
Rect(0.0, 0.0, 9.0, 9.0)
(4, 5) in r # this point-like lies inside the rectangle
True

and this rect-like is also inside

(4, 4, 5, 5) in r
True

.. include:: footer.rst


File: docs/annot.rst

.. include:: header.rst

.. _Annot:

================ Annot

|pdf_only_class|

Quote from the :ref:AdobeManual:

"An annotation associates an object such as a note, sound, or movie with a location on a page of a PDF document, or provides a way to interact with the user by means of the mouse and keyboard."

There is a parent-child relationship between an annotation and its page. If the page object becomes unusable (closed document, any document structure change, etc.), then so does every of its existing annotation objects -- an exception is raised saying that the object is "orphaned", whenever an annotation property or method is accessed.

================================== ==============================================================
Attribute Short Description
================================== ==============================================================
:meth:Annot.delete_responses delete all responding annotations
:meth:Annot.get_file get attached file content
:meth:Annot.get_oc get :data:xref of an :data:OCG / :data:OCMD
:meth:Annot.get_pixmap image of the annotation as a pixmap
:meth:Annot.get_sound get the sound of an audio annotation
:meth:Annot.get_text extract annotation text
:meth:Annot.get_textbox extract annotation text
:meth:Annot.get_textpage create a TextPage for the annotation
:meth:Annot.set_border set annotation's border properties
:meth:Annot.set_blendmode set annotation's blend mode
:meth:Annot.set_colors set annotation's colors
:meth:Annot.set_flags set annotation's flags field
:meth:Annot.set_irt_xref define the annotation to being "In Response To"
:meth:Annot.set_name set annotation's name field
:meth:Annot.set_oc set :data:xref to an :data:OCG / :data:OCMD
:meth:Annot.set_opacity change transparency
:meth:Annot.set_open open / close annotation or its Popup
:meth:Annot.set_popup create a Popup for the annotation
:meth:Annot.set_rect change annotation rectangle
:meth:Annot.set_rotation change rotation
:meth:Annot.update_file update attached file content
:meth:Annot.update apply accumulated annot changes
:attr:Annot.blendmode annotation BlendMode
:attr:Annot.border border details
:attr:Annot.colors border / background and fill colors
:attr:Annot.file_info get attached file information
:attr:Annot.flags annotation flags
:attr:Annot.has_popup whether annotation has a Popup
:attr:Annot.irt_xref annotation to which this one responds
:attr:Annot.info various information
:attr:Annot.is_open whether annotation or its Popup is open
:attr:Annot.line_ends start / end appearance of line-type annotations
:attr:Annot.next link to the next annotation
:attr:Annot.opacity the annot's transparency
:attr:Annot.parent page object of the annotation
:attr:Annot.popup_rect rectangle of the annotation's Popup
:attr:Annot.popup_xref the PDF :data:xref number of the annotation's Popup
:attr:Annot.rect rectangle containing the annotation
:attr:Annot.type type of the annotation
:attr:Annot.vertices point coordinates of Polygons, PolyLines, etc.
:attr:Annot.xref the PDF :data:xref number
================================== ==============================================================

Class API

.. class:: Annot

.. index::
pair: matrix; Annot.get_pixmap
pair: colorspace; Annot.get_pixmap
pair: alpha; Annot.get_pixmap
pair: dpi; Annot.get_pixmap

.. method:: get_pixmap(matrix=pymupdf.Identity, dpi=None, colorspace=pymupdf.csRGB, alpha=False)

text
* Changed in v1.19.2: added support of dpi parameter.

  Creates a pixmap from the annotation as it appears on the page in untransformed coordinates. The pixmap's :ref:`IRect` equals *Annot.rect.irect* (see below). **All parameters are keyword only.**

  :arg matrix_like matrix: a matrix to be used for image creation. Default is :ref:`Identity`.

  :arg int dpi: (new in v1.19.2) desired resolution in dots per inch. If not `None`, the matrix parameter is ignored.

  :arg colorspace: a colorspace to be used for image creation. Default is ``pymupdf.csRGB``.
  :type colorspace: :ref:`Colorspace`

  :arg bool alpha: whether to include transparency information. Default is ``False``.

  :rtype: :ref:`Pixmap`

  .. note::
     
     * If the annotation has just been created or modified, you should :meth:`Document.reload_page` the page first via `page = doc.reload_page(page)`.

     * The pixmap will have *"premultiplied"* pixels if `alpha=True`. To learn about some background, e.g. look for "Premultiplied alpha" `in this online glossary <https://en.wikipedia.org/wiki/Glossary_of_computer_graphics#P>`_.

.. index::
pair: blocks; Annot.get_text
pair: dict; Annot.get_text
pair: clip; Annot.get_text
pair: flags; Annot.get_text
pair: html; Annot.get_text
pair: json; Annot.get_text
pair: rawdict; Annot.get_text
pair: text; Annot.get_text
pair: words; Annot.get_text
pair: xhtml; Annot.get_text
pair: xml; Annot.get_text

.. method:: get_text(opt, clip=None, flags=None)

text
* New in 1.18.0

  Retrieves the content of the annotation in a variety of formats -- much like the same method for :ref:`Page`.. This currently only delivers relevant data for annotation types 'FreeText' and 'Stamp'. Other types return an empty string (or equivalent objects).

  :arg str opt: (positional only) the desired format - one of the following values. Please note that this method works exactly like the same-named method of :ref:`Page`.

     * "text" -- :meth:`TextPage.extractTEXT`, default
     * "blocks" -- :meth:`TextPage.extractBLOCKS`
     * "words" -- :meth:`TextPage.extractWORDS`
     * "html" -- :meth:`TextPage.extractHTML`
     * "xhtml" -- :meth:`TextPage.extractXHTML`
     * "xml" -- :meth:`TextPage.extractXML`
     * "dict" -- :meth:`TextPage.extractDICT`
     * "json" -- :meth:`TextPage.extractJSON`
     * "rawdict" -- :meth:`TextPage.extractRAWDICT`

  :arg rect-like clip: (keyword only) restrict the extraction to this area. Should hardly ever be required, defaults to :attr:`Annot.rect`.
  :arg int flags: (keyword only) control the amount of data returned. Defaults to simple text extraction.

.. method:: get_textbox(rect)

text
* New in 1.18.0

  Return the annotation text. Mostly (except line breaks) equal to :meth:`Annot.get_text` with the "text" option.

  :arg rect-like rect: the area to consider, defaults to :attr:`Annot.rect`.

.. method:: get_textpage(clip=None, flags=3)

text
Create a :ref:`TextPage` for the annotation.

  :arg int flags: indicator bits controlling the content available for subsequent text extractions and searches -- see the parameter of :meth:`Annot.get_text`.

  :arg rect-like clip: restrict extracted text to this area.

  :returns: :ref:`TextPage`

  |history_begin|

  * v1.25.5: fixed `clip` arg.

  |history_end|

.. method:: set_info(info=None, content=None, title=None, creationDate=None, modDate=None, subject=None)

text
* Changed in version 1.16.10

  Changes annotation properties. These include dates, contents, subject and author (title). Changes for *name* and *id* will be ignored. The update happens selectively: To leave a property unchanged, set it to ``None``. To delete existing data, use an empty string.

  :arg dict info: a dictionary compatible with the *info* property (see below). All entries must be strings. If this argument is not a dictionary, the other arguments are used instead -- else they are ignored.
  :arg str content: *(new in v1.16.10)* see description in :attr:`info`.
  :arg str title: *(new in v1.16.10)* see description in :attr:`info`.
  :arg str creationDate: *(new in v1.16.10)* date of annot creation. If given, should be in PDF datetime format.
  :arg str modDate: *(new in v1.16.10)* date of last modification. If given, should be in PDF datetime format.
  :arg str subject: *(new in v1.16.10)* see description in :attr:`info`.

.. method:: set_line_ends(start, end)

text
Sets an annotation's line ending styles. Each of these annotation types is defined by a list of points which are connected by lines. The symbol identified by *start* is attached to the first point, and *end* to the last point of this list. For unsupported annotation types, a no-operation with a warning message results.

  .. note::

     * Some symbols have an interior area (diamonds, circles, squares, etc.). These areas are filled with the fill color or the stroke color, depending on the annotation type.

  :arg int start: The symbol number for the first point.
  :arg int end: The symbol number for the last point.

.. method:: set_oc(xref)

text
Set the annotation's visibility using PDF optional content mechanisms. This visibility is controlled by the user interface of supporting PDF viewers. It is independent from other attributes like :attr:`Annot.flags`.

  :arg int xref: the :data:`xref` of an optional contents group (OCG or OCMD). Any previous xref will be overwritten. If zero, a previous entry will be removed. An exception occurs if the xref is not zero and does not point to a valid PDF object.

  .. note:: This does **not require executing** :meth:`Annot.update` to take effect.

.. method:: get_oc()

text
Return the :data:`xref` of an optional content object, or zero if there is none.

  :returns: zero or the xref of an OCG (or OCMD).

.. method:: set_irt_xref(xref)

text
* New in v1.19.3

  Set annotation to be "In Response To" another one.

  :arg int xref: The :data:`xref` of another annotation.

     .. note:: Must refer to an existing annotation on this page. Setting this property requires no subsequent `update()`.

.. method:: set_open(value)

text
* New in v1.18.4

  Set the annotation's Popup annotation to open or closed -- **or** the annotation itself, if its type is 'Text' ("sticky note").

  :arg bool value: the desired open state.

.. method:: set_popup(rect)

text
* New in v1.18.4

  Create a Popup annotation for the annotation and specify its rectangle. If the Popup already exists, only its rectangle is updated.

  :arg rect_like rect: the desired rectangle.

.. method:: set_opacity(value)

text
Set the annotation's transparency. Opacity can also be set in :meth:`Annot.update`.

  :arg float value: a float in range *[0, 1]*. Any value outside is assumed to be 1. E.g. a value of 0.5 sets the transparency to 50%.

  Three overlapping 'Circle' annotations with each opacity set to 0.5:

  .. image:: images/img-opacity.*

.. attribute:: blendmode

text
* New in v1.18.4

  The annotation's blend mode. See :ref:`AdobeManual`, page 324 for explanations.

  :rtype: str
  :returns: the blend mode or ``None``.

.. method:: set_blendmode(blendmode)

text
* New in v1.16.14
  
  Set the annotation's blend mode. See :ref:`AdobeManual`, page 324 for explanations. The blend mode can also be set in :meth:`Annot.update`.

  :arg str blendmode: set the blend mode. Use :meth:`Annot.update` to reflect this in the visual appearance. For predefined values see :ref:`BlendModes`. Use `PDF_BM_Normal` to **remove** a blend mode.

.. method:: set_name(name)

text
* New in version 1.16.0
  
  Change the name field of any annotation type. For 'FileAttachment' and 'Text' annotations, this is the icon name, for 'Stamp' annotations the text in the stamp. The visual result (if any) depends on your PDF viewer. See also :ref:`mupdficons`.

  :arg str name: the new name.

  .. caution:: If you set the name of a 'Stamp' annotation, then this will **not change** the rectangle, nor will the text be layouted in any way. If you choose a standard text from :ref:`StampIcons` (the **exact** name piece after `"STAMP_"`), you should receive the original layout. An **arbitrary text** will not be changed to upper case, but be written in font "Times-Bold" as is, horizontally centered in **one line** and be shortened to fit. To get your text fully displayed, its length using :data:`fontsize` 20 must not exceed 190 points. So please make sure that the following inequality is true: `pymupdf.get_text_length(text, fontname="tibo", fontsize=20) <= 190`.

.. method:: set_rect(rect)

text
Change the rectangle of an annotation. The annotation can be moved around and both sides of the rectangle can be independently scaled. However, the annotation appearance will never get rotated, flipped or sheared. This method only affects certain annotation types [#f2]_ and will lead to a message on Python's `sys.stderr` in other cases. No exception will be raised, but `False` will be returned.

  :arg rect_like rect: the new rectangle of the annotation (finite and not empty). E.g. using a value of *annot.rect + (5, 5, 5, 5)* will shift the annot position 5 pixels to the right and downwards.

  .. note:: You **need not** invoke :meth:`Annot.update` for activation of the effect.

.. method:: set_rotation(angle)

text
Set the rotation of an annotation. This rotates the annotation rectangle around its center point. Then a **new annotation rectangle** is calculated from the resulting quad.

  :arg int angle: rotation angle in degrees. Arbitrary values are possible, but will be clamped to the interval `[0, 360)`.

  .. note::
    * You **must invoke** :meth:`Annot.update` to activate the effect.
    * For PDF_ANNOT_FREE_TEXT, only one of the values 0, 90, 180 and 270 is possible and will **rotate the text** inside the current rectangle (which remains unchanged). Other values are silently ignored and replaced by 0.
    * Otherwise, only the following :ref:`AnnotationTypes` can be rotated: 'Square', 'Circle', 'Caret', 'Text', 'FileAttachment', 'Ink', 'Line', 'Polyline', 'Polygon', and 'Stamp'. For all others the method is a no-op.

.. method:: set_border(border=None, width=None, style=None, dashes=None, clouds=None)

text
* Changed in version 1.16.9: Allow specification without using a dictionary. The direct parameters are used if *border* is not a dictionary.

  * Changed in version 1.22.5: Support of the "cloudy" border effect.

  PDF only: Change border width, dashing, style and cloud effect. See the :attr:`Annot.border` attribute for more details.


  :arg dict border: a dictionary as returned by the :attr:`border` property, with keys *"width"* (*float*), *"style"* (*str*),  *"dashes"* (*sequence*) and *clouds* (*int*). Omitted keys will leave the resp. property unchanged. Set the border argument to `None` (the default) to use the other arguments.

  :arg float width: A non-negative value will change the border line width.
  :arg str style: A value other than `None` will change this border property.
  :arg sequence dashes: All items of the sequence must be integers, otherwise the parameter is ignored. To remove dashing use: `dashes=[]`. If dashes is a non-empty sequence, "style" will automatically be set to "D" (dashed). 
  :arg int clouds: A value >= 0 will change this property. Use `clouds=0` to remove the cloudy appearance completely. Only annotation types 'Square', 'Circle', and 'Polygon' are supported with this property.

.. method:: set_flags(flags)

text
Changes the annotation flags. Use the `|` operator to combine several.

  :arg int flags: an integer specifying the required flags.

.. method:: set_colors(colors=None, stroke=None, fill=None)

text
Changes the "stroke" and "fill" colors for supported annotation types -- not all annotation types accept both. **Do not use this method at all for FreeText annotations** because it has its special conventions to deal with up to three colors (border, fill, text).

  :arg dict colors: a dictionary containing color specifications. For accepted dictionary keys and values see below. The most practical way should be to first make a copy of the *colors* property and then modify this dictionary as required.
  :arg sequence stroke: see above.
  :arg sequence fill: see above.

  To completely remove a color specification, use an empty sequence like `[]`. If you specify `None`, an existing specification will not be changed.

.. method:: delete_responses()

text
* New in version 1.16.12
  
  Delete annotations referring to this one. This includes any 'Popup' annotations and all annotations responding to it.

.. index::
pair: blend_mode; Annot.update
pair: fontsize; Annot.update
pair: text_color; Annot.update
pair: border_color; Annot.update
pair: fill_color; Annot.update
pair: cross_out; Annot.update
pair: rotate; Annot.update

.. method:: update(opacity=None, blend_mode=None, fontsize=0, text_color=None, border_color=None, fill_color=None, cross_out=True, rotate=-1)

text
Synchronize the appearance of an annotation with its properties after relevant changes. 

  You can safely **omit** this method **only** for the following changes:

     * :meth:`Annot.set_rect`
     * :meth:`Annot.set_flags`
     * :meth:`Annot.set_oc`
     * :meth:`Annot.update_file`
     * :meth:`Annot.set_info` (except any changes to *"content"*)

  All arguments are optional. *(Changed in v1.16.14)* Blend mode and opacity are applicable to **all annotation types**. The other arguments are mostly special use, as described below.

  Color specifications may be made in the usual format used in PuMuPDF as sequences of floats ranging from 0.0 to 1.0 (including both). The sequence length must be 1, 3 or 4 (supporting GRAY, RGB and CMYK colorspaces respectively). For GRAY, just a float is also acceptable.

  :arg float opacity: *(new in v1.16.14)* **valid for all annotation types:** change or set the annotation's transparency. Valid values are *0 <= opacity < 1*.

  :arg str blend_mode: *(new in v1.16.14)* **valid for all annotation types:** change or set the annotation's blend mode. For valid values see :ref:`BlendModes`.

  :arg float fontsize: change :data:`fontsize` of the text. 'FreeText' annotations only.

  :arg sequence,float text_color: change the text color. 'FreeText' annotations only. This has the same effect as ``border_color``. Note that the text color of rich-text annotations cannot be changed at all because it is set by HTML / CSS syntax and part of the text itself.

  :arg sequence,float border_color: change the border color. 'FreeText' annotations only. This has the same effect as ``text_color``.

  :arg sequence,float fill_color: the fill color.

  :arg bool cross_out: *(new in v1.17.2)* add two diagonal lines to the annotation rectangle. 'Redact' annotations only. If not desired, ``False`` must be specified even if the annotation was created with ``False``.

  :arg int rotate: new rotation value. Default (-1) means no change. Supports 'FreeText' and several other annotation types (see :meth:`Annot.set_rotation`), [#f1]_. Only choose 0, 90, 180, or 270 degrees for 'FreeText'. Otherwise any integer is acceptable.

  :rtype: bool

  This method is the only way to change the colors of a FreeText annotation. You cannot use :meth:`Annot.set_colors` for this purpose. But be aware that for rich-text annotations, the text color is never changed. The text color is set by the ``text_color`` entry of the ``info`` dictionary. This is a limitation of |MuPDF| and not a bug.

  .. caution:: Using this method inside a :meth:`Page.annots` loop is **not recommended!** This is because most annotation updates require the owning page to be reloaded -- which cannot be done inside this loop. Please use the example coding pattern given in the documentation of this generator.

.. attribute:: file_info

text
Basic information of the annot's attached file.

  :rtype: dict
  :returns: a dictionary with keys *filename*, *ufilename*, *desc* (description), *size* (uncompressed file size), *length* (compressed length) for FileAttachment annot types, else ``None``.

.. method:: get_file()

text
Returns attached file content.

  :rtype: bytes
  :returns: the content of the attached file.

.. index::
pair: buffer; Annot.update_file
pair: filename; Annot.update_file
pair: ufilename; Annot.update_file
pair: desc; Annot.update_file

.. method:: update_file(buffer=None, filename=None, ufilename=None, desc=None)

text
Updates the content of an attached file. All arguments are optional. No arguments lead to a no-op.

  :arg bytes|bytearray|BytesIO buffer: the new file content. Omit to only change meta-information.

     *(Changed in version 1.14.13)* *io.BytesIO* is now also supported.

  :arg str filename: new filename to associate with the file.

  :arg str ufilename: new unicode filename to associate with the file.

  :arg str desc: new description of the file content.

.. method:: get_sound()

text
Return the embedded sound of an audio annotation.

  :rtype: dict
  :returns: the sound audio file and accompanying properties. These are the possible dictionary keys, of which only "rate" and "stream" are always present.

    =========== =======================================================
    Key         Description
    =========== =======================================================
    rate        (float, requ.) samples per second
    channels    (int, opt.) number of sound channels
    bps         (int, opt.) bits per sample value per channel
    encoding    (str, opt.) encoding format: Raw, Signed, muLaw, ALaw
    compression (str, opt.) name of compression filter
    stream      (bytes, requ.) the sound file content
    =========== =======================================================

.. attribute:: opacity

text
The annotation's transparency. If set, it is a value in range *[0, 1]*. The PDF default is 1. However, in an effort to tell the difference, we return *-1.0* if not set.

  :rtype: float

.. attribute:: parent

text
The owning page object of the annotation.

  :rtype: :ref:`Page`

.. attribute:: rotation

text
The annot rotation.

  :rtype: int
  :returns: a value [-1, 359]. If rotation is not at all, -1 is returned (and implies a rotation angle of 0). Other possible values are normalized to some value value 0 <= angle < 360.

.. attribute:: rect

text
The rectangle containing the annotation.

  :rtype: :ref:`Rect`

.. attribute:: next

text
The next annotation on this page or None.

  :rtype: *Annot*

.. attribute:: type

text
A number and one or two strings describing the annotation type, like **[2, 'FreeText', 'FreeTextCallout']**. The second string entry is optional and may be empty. See the appendix :ref:`AnnotationTypes` for a list of possible values and their meanings.

  :rtype: list

.. attribute:: info

text
A dictionary containing various information. All fields are optional strings. For information items not provided, an empty string is returned.

  * *name* -- e.g. for 'Stamp' annotations it will contain the stamp text like "Sold" or "Experimental", for other annot types you will see the name of the annot's icon here ("PushPin" for FileAttachment).

  * *content* -- a string containing the text for type *Text* and *FreeText* annotations. Commonly used for filling the text field of annotation pop-up windows.

  * *title* -- a string containing the title of the annotation pop-up window. By convention, this is used for the **annotation author**.

  * *creationDate* -- creation timestamp.
  * *modDate* -- last modified timestamp.
  * *subject* -- subject.
  * *id* -- *(new in version 1.16.10)* a unique identification of the annotation. This is taken from PDF key */NM*. Annotations added by PyMuPDF will have a unique name, which appears here.

  :rtype: dict

.. attribute:: flags

text
An integer whose low order bits contain flags for how the annotation should be presented.

  :rtype: int

.. attribute:: line_ends

text
A pair of integers specifying start and end symbol of annotations types 'FreeText', 'Line', 'PolyLine', and 'Polygon'. ``None`` if not applicable. For possible values and descriptions in this list, see the :ref:`AdobeManual`, table 1.76 on page 400.

  :rtype: tuple

.. attribute:: vertices

text
A list containing a variable number of point ("vertices") coordinates (each given by a pair of floats) for various types of annotations:

  * 'Line' -- the starting and ending coordinates (2 float pairs).
  * 'FreeText' -- 2 or 3 float pairs designating the starting, the (optional) knee point, and the ending coordinates.
  * 'PolyLine' / 'Polygon' -- the coordinates of the edges connected by line pieces (n float pairs for n points).
  * text markup annotations -- 4 float pairs specifying the *QuadPoints* of the marked text span (see :ref:`AdobeManual`, page 403).
  * 'Ink' -- list of one to many sublists of vertex coordinates. Each such sublist represents a separate line in the drawing.

  :rtype: list

.. attribute:: colors

text
dictionary of two lists of floats in range *0 <= float <= 1* specifying the "stroke" and the interior ("fill") colors. The stroke color is used for borders and everything that is actively painted or written ("stroked"). The fill color is used for the interior of objects like line ends, circles and squares. The lengths of these lists implicitly determine the colorspaces used: 1 = GRAY, 3 = RGB, 4 = CMYK. So "[1.0, 0.0, 0.0]" stands for RGB color red. Both lists can be empty if no color is specified. Be aware about some potentially unexpected cases:

  * The color of Highlight annotations is a **stroke** color, contrary to intuition.
  * The color if FreeText annotations is a **stroke** color, but appears as the color that fills the rectangle and any applicable line end symbols. Text color and border color cannot be accessed at all.

  :rtype: dict

.. attribute:: xref

text
The PDF :data:`xref`.

  :rtype: int

.. attribute:: irt_xref

text
The PDF :data:`xref` of an annotation to which this one responds. Return zero if this is no response annotation.

  :rtype: int

.. attribute:: popup_xref

text
The PDF :data:`xref` of the associated Popup annotation. Zero if non-existent.

  :rtype: int

.. attribute:: has_popup

text
Whether the annotation has a Popup annotation.

  :rtype: bool

.. attribute:: is_open

text
Whether the annotation's Popup is open -- **or** the annotation itself ('Text' annotations only).

  :rtype: bool

.. attribute:: popup_rect

text
The rectangle of the associated Popup annotation. Infinite rectangle if non-existent.

  :rtype: :ref:`Rect`

.. attribute:: rect_delta

text
A tuple of four floats representing the `/RD` entry of the annotation. The four numbers describe the numerical differences (left, top, -right, -bottom) between two rectangles: the :attr:`rect` of the annotation and a rectangle contained within that rectangle. If the entry is missing, this property is `(0, 0, 0, 0)`. If the annotation border is a normal, straight line, these numbers are typically border width divided by 2. If the annotation has a "cloudy" border, you will see the breadth of the cloud semi-circles here. In general, the numbers need not be identical. To compute the inner rectangle do `a.rect + a.rect_delta`.

.. attribute:: border

text
A dictionary containing border characteristics. Empty if no border information exists. The following keys may be present:

  * *width* -- a float indicating the border thickness in points. The value is -1.0 if no width is specified.

  * *dashes* -- a sequence of integers specifying a line dashing pattern. *[]* means no dashes, *[n]* means equal on-off lengths of *n* points, longer lists will be interpreted as specifying alternating on-off length values. See the :ref:`AdobeManual` page 126 for more details.

  * *style* -- 1-byte border style: **"S"** (Solid) = solid line surrounding the annotation, **"D"** (Dashed) = dashed line surrounding the annotation, the dash pattern is specified by the *dashes* entry, **"B"** (Beveled) = a simulated embossed rectangle that appears to be raised above the surface of the page, **"I"** (Inset) = a simulated engraved rectangle that appears to be recessed below the surface of the page, **"U"** (Underline) = a single line along the bottom of the annotation rectangle.

  * *clouds* -- an integer indicating a "cloudy" border, where ``n`` is an integer `-1 <= n <= 2`. A value `n = 0` indicates a straight line (no clouds), 1 means small and 2 means large semi-circles, mimicking the cloudy appearance. If -1, then no specification is present.

  :rtype: dict

.. _mupdficons:

Annotation Icons in MuPDF

This is a list of icons referenceable by name for annotation types 'Text' and 'FileAttachment'. You can use them via the icon parameter when adding an annotation, or use the as argument in :meth:Annot.set_name. It is left to your discretion which item to choose when -- no mechanism will keep you from using e.g. the "Speaker" icon for a 'FileAttachment'.

.. image:: images/mupdf-icons.*

Example

Change the graphical image of an annotation. Also update the "author" and the text to be shown in the popup window::

doc = pymupdf.open("circle-in.pdf")
page = doc[0] # page 0
annot = page.first_annot # get the annotation
annot.set_border(dashes=[3]) # set dashes to "3 on, 3 off ..."

set stroke and fill color to some blue

annot.set_colors({"stroke":(0, 0, 1), "fill":(0.75, 0.8, 0.95)})
info = annot.info # get info dict
info["title"] = "Jorj X. McKie" # set author

text in popup window ...

info["content"] = "I changed border and colors and enlarged the image by 20%."
info["subject"] = "Demonstration of PyMuPDF" # some PDF viewers also show this
annot.set_info(info) # update info dict
r = annot.rect # take annot rect
r.x1 = r.x0 + r.width * 1.2 # new location has same top-left
r.y1 = r.y0 + r.height * 1.2 # but 20% longer sides
annot.set_rect(r) # update rectangle
annot.update() # update the annot's appearance
doc.save("circle-out.pdf") # save

This is how the circle annotation looks like before and after the change (pop-up windows displayed using Nitro PDF viewer):

|circle|

.. |circle| image:: images/img-circle.*

.. rubric:: Footnotes

.. [#f1] Rotating an annotation also changes its rectangle. Depending on how the annotation was defined, the original rectangle is cannot be reconstructed by setting the rotation value to zero again and will be lost.

.. [#f2] Only the following annotation types support method :meth:Annot.set_rect: Text, FreeText, Square, Circle, Redact, Stamp, Caret, FileAttachment, Sound, and Movie.

.. include:: footer.rst


File: docs/app1.rst

.. include:: header.rst

.. _Appendix1:

====================================== Appendix 1: Details on Text Extraction

This chapter provides background on the text extraction methods of PyMuPDF.

Information of interest are

  • what do they provide?
  • what do they imply (processing time / data sizes)?

General structure of a TextPage

text
:ref:`TextPage` is one of (Py-) MuPDF's classes. It is normally created (and destroyed again) behind the curtain, when :ref:`Page` text extraction methods are used, but it is also available directly and can be used as a persistent object. Other than its name suggests, images may optionally also be part of a text page::

 
     <text block>
         <line>
             
                 <char>
     
         

A **text page** consists of blocks (= roughly paragraphs).

A **block** consists of either lines and their characters, or an image.

A **line** consists of spans.

A **span** consists of adjacent characters with identical font properties: name, size, flags and color.

Plain Text
~~~~~~~~~~

Function :meth:`TextPage.extractText` (or *Page.get_text("text")*) extracts a page's plain **text in original order** as specified by the creator of the document.

An example output::

    >>> print(page.get_text("text"))
    Some text on first page.

.. note:: The output may not equal an accustomed "natural" reading order. However, you can request a reordering following the scheme "top-left to bottom-right" by executing `page.get_text("text", sort=True)`.


BLOCKS
~~~~~~~~~~

Function :meth:`TextPage.extractBLOCKS` (or *Page.get_text("blocks")*) extracts a page's text blocks as a list of items like::

    (x0, y0, x1, y1, "lines in block", block_no, block_type)

Where the first 4 items are the float coordinates of the block's bbox. The lines within each block are concatenated by a new-line character.

This is a high-speed method, which by default also extracts image meta information: Each image appears as a block with one text line, which contains meta information. The image itself is not shown.

As with simple text output above, the `sort` argument can be used as well to obtain a reading order.

Example output::

    >>> print(page.get_text("blocks", sort=False))
    [(50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375,
    'Some text on first page.', 0, 0)]


WORDS
~~~~~~~~~~

Function :meth:`TextPage.extractWORDS` (or *Page.get_text("words")*) extracts a page's text **words** as a list of items like::

    (x0, y0, x1, y1, "word", block_no, line_no, word_no)

Where the first 4 items are the float coordinates of the words's bbox. The last three integers provide some more information on the word's whereabouts.

This is a high-speed method. As with the previous methods, argument `sort=True` will reorder the words.

Example output::

    >>> for word in page.get_text("words", sort=False):
            print(word)
    (50.0, 88.17500305175781, 78.73200225830078, 103.28900146484375,
    'Some', 0, 0, 0)
    (81.79000091552734, 88.17500305175781, 99.5219955444336, 103.28900146484375,
    'text', 0, 0, 1)
    (102.57999420166016, 88.17500305175781, 114.8119888305664, 103.28900146484375,
    'on', 0, 0, 2)
    (117.86998748779297, 88.17500305175781, 135.5909881591797, 103.28900146484375,
    'first', 0, 0, 3)
    (138.64898681640625, 88.17500305175781, 166.1709747314453, 103.28900146484375,
    'page.', 0, 0, 4)

HTML
~~~~

:meth:`TextPage.extractHTML` (or *Page.get_text("html")* output fully reflects the structure of the page's ``TextPage`` -- much like DICT / JSON below. This includes images, font information and text positions. If wrapped in HTML header and trailer code, it can readily be displayed by an internet browser. Our above example::

    >>> for line in page.get_text("html").splitlines():
            print(line)

    
    Some text on first page.
    


.. _HTMLQuality:

Controlling Quality of HTML Output

While HTML output has improved a lot in MuPDF v1.12.0, it is not yet bug-free: we have found problems in the areas font support and image positioning.

  • HTML text contains references to the fonts used of the original document. If these are not known to the browser (a fat chance!), it will replace them with others; the results will probably look awkward. This issue varies greatly by browser -- on my Windows machine, MS Edge worked just fine, whereas Firefox looked horrible.

  • For PDFs with a complex structure, images may not be positioned and / or sized correctly. This seems to be the case for rotated pages and pages, where the various possible page bbox variants do not coincide (e.g. MediaBox != CropBox). We do not know yet, how to address this -- we filed a bug at MuPDF's site.

To address the font issue, you can use a simple utility script to scan through the HTML file and replace font references. Here is a little example that replaces all fonts with one of the :ref:Base-14-Fonts: serifed fonts will become "Times", non-serifed "Helvetica" and monospaced will become "Courier". Their respective variations for "bold", "italic", etc. are hopefully done correctly by your browser::

import sys
filename = sys.argv[1]
otext = open(filename).read() # original html text string
pos1 = 0 # search start poition
font_serif = "font-family:Times" # enter ...
font_sans = "font-family:Helvetica" # ... your choices ...
font_mono = "font-family:Courier" # ... here
found_one = False # true if search successful

while True:
pos0 = otext.find("font-family:", pos1) # start of a font spec
if pos0 < 0: # none found - we are done
break
pos1 = otext.find(";", pos0) # end of font spec
test = otext[pos0 : pos1] # complete font spec string
testn = "" # the new font spec string
if test.endswith(",serif"): # font with serifs?
testn = font_serif # use Times instead
elif test.endswith(",sans-serif"): # sans serifs font?
testn = font_sans # use Helvetica
elif test.endswith(",monospace"): # monospaced font?
testn = font_mono # becomes Courier

text
if testn != "":                           # any of the above found?
     otext = otext.replace(test, testn)    # change the source
     found_one = True
     pos1 = 0                              # start over

if found_one:
ofile = open(filename + ".html", "w")
ofile.write(otext)
ofile.close()
else:
print("Warning: could not find any font specs!")

DICT (or JSON)

text
:meth:`TextPage.extractDICT` (or *Page.get_text("dict", sort=False)*) output fully reflects the structure of a ``TextPage`` and provides image content and position detail (*bbox* -- boundary boxes in pixel units) for every block, line and span. Images are stored as *bytes* for DICT output and base64 encoded strings for JSON output.

For a visualization of the dictionary structure have a look at :ref:`textpagedict`.

Here is how this looks like::

    {
        "width": 300.0,
        "height": 350.0,
        "blocks": [{
            "type": 0,
            "bbox": (50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375),
            "lines": ({
                "wmode": 0,
                "dir": (1.0, 0.0),
                "bbox": (50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375),
                "spans": ({
                    "size": 11.0,
                    "flags": 0,
                    "font": "Helvetica",
                    "color": 0,
                    "origin": (50.0, 100.0),
                    "text": "Some text on first page.",
                    "bbox": (50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375)
                })
            }]
        }]
    }

RAWDICT (or RAWJSON)

:meth:TextPage.extractRAWDICT (or Page.get_text("rawdict", sort=False)) is an information superset of DICT and takes the detail level one step deeper. It looks exactly like the above, except that the "text" items (string) in the spans are replaced by the list "chars". Each "chars" entry is a character dict. For example, here is what you would see in place of item "text": "Text in black color." above::

text
"chars": [{
    "origin": (50.0, 100.0),
    "bbox": (50.0, 88.17500305175781, 57.336997985839844, 103.28900146484375),
    "c": "S"
}, {
    "origin": (57.33700180053711, 100.0),
    "bbox": (57.33700180053711, 88.17500305175781, 63.4530029296875, 103.28900146484375),
    "c": "o"
}, {
    "origin": (63.4530029296875, 100.0),
    "bbox": (63.4530029296875, 88.17500305175781, 72.61600494384766, 103.28900146484375),
    "c": "m"
}, {
    "origin": (72.61600494384766, 100.0),
    "bbox": (72.61600494384766, 88.17500305175781, 78.73200225830078, 103.28900146484375),
    "c": "e"
}, {
    "origin": (78.73200225830078, 100.0),
    "bbox": (78.73200225830078, 88.17500305175781, 81.79000091552734, 103.28900146484375),
    "c": " "
< ... deleted ... >
}, {
    "origin": (163.11297607421875, 100.0),
    "bbox": (163.11297607421875, 88.17500305175781, 166.1709747314453, 103.28900146484375),
    "c": "."
}],

XML

text
The :meth:`TextPage.extractXML` (or *Page.get_text("xml")*) version extracts text (no images) with the detail level of RAWDICT::

    >>> for line in page.get_text("xml").splitlines():
        print(line)

    
    
    <line bbox="50 88.175 166.17098 103.289" wmode="0" dir="1 0">
    
    <char quad="50 88.175 57.336999 88.175 50 103.289 57.336999 103.289" x="50"
    y="100" color="#000000" c="S"/>
    <char quad="57.337 88.175 63.453004 88.175 57.337 103.289 63.453004 103.289" x="57.337"
    y="100" color="#000000" c="o"/>
    <char quad="63.453004 88.175 72.616008 88.175 63.453004 103.289 72.616008 103.289" x="63.453004"
    y="100" color="#000000" c="m"/>
    <char quad="72.616008 88.175 78.732 88.175 72.616008 103.289 78.732 103.289" x="72.616008"
    y="100" color="#000000" c="e"/>
    <char quad="78.732 88.175 81.79 88.175 78.732 103.289 81.79 103.289" x="78.732"
    y="100" color="#000000" c=" "/>

    ... deleted ...

    <char quad="163.11298 88.175 166.17098 88.175 163.11298 103.289 166.17098 103.289" x="163.11298"
    y="100" color="#000000" c="."/>
    
    </line>
    
    

.. note:: We have successfully tested `lxml <https://pypi.org/project/lxml/>`_ to interpret this output.

XHTML

:meth:TextPage.extractXHTML (or Page.get_text("xhtml")) is a variation of TEXT but in HTML format, containing the bare text and images ("semantic" output)::

text
Some text on first page.

.. _text_extraction_flags:

Text Extraction Flags Defaults

text
* New in version 1.16.2: Method :meth:`Page.get_text` supports a keyword parameter *flags* *(int)* to control the amount and the quality of extracted data. The following table shows the defaults settings (flags parameter omitted or None) for each extraction variant. If you specify flags with a value other than ``None``, be aware that you must set **all desired** options. A description of the respective bit settings can be found in :ref:`TextPreserve`.

* New in v1.19.6: The default combinations in the following table are now available as Python constants: :data:`TEXTFLAGS_TEXT`, :data:`TEXTFLAGS_WORDS`, :data:`TEXTFLAGS_BLOCKS`, :data:`TEXTFLAGS_DICT`, :data:`TEXTFLAGS_RAWDICT`, :data:`TEXTFLAGS_HTML`, :data:`TEXTFLAGS_XHTML`, :data:`TEXTFLAGS_XML`, and :data:`TEXTFLAGS_SEARCH`. You can now easily modify a default flag, e.g.

    - **include** images in a "blocks" output:
    
    `flags = TEXTFLAGS_BLOCKS | TEXT_PRESERVE_IMAGES`
    
    - **exclude** images from a "dict" output:
    
    `flags = TEXTFLAGS_DICT & ~TEXT_PRESERVE_IMAGES`
    
    - set **dehyphenation off** in text searches:
    
    `flags = TEXTFLAGS_SEARCH & ~TEXT_DEHYPHENATE`


========================= ==== ==== ===== === ==== ======= ===== ====== ======
Indicator                 text html xhtml xml dict rawdict words blocks search
========================= ==== ==== ===== === ==== ======= ===== ====== ======
preserve ligatures        1    1    1     1   1    1       1     1       0
preserve whitespace       1    1    1     1   1    1       1     1       1
preserve images           n/a  1    1     n/a 1    1       n/a   0       0
inhibit spaces            0    0    0     0   0    0       0     0       0
dehyphenate               0    0    0     0   0    0       0     0       1
clip to mediabox          1    1    1     1   1    1       1     1       1
use CID instead of U+FFFD 1    1    1     1   1    1       1     1       0
========================= ==== ==== ===== === ==== ======= ===== ====== ======

* **search** refers to the text search function.
* **"json"** is handled exactly like **"dict"** and is hence left out.
* **"rawjson"** is handled exactly like **"rawdict"** and is hence left out.
* An "n/a" specification means a value of 0 and setting this bit never has any effect on the output (but an adverse effect on performance).
* If you are not interested in images when using an output variant which includes them by default, then by all means set the respective bit off: You will experience a better performance and much lower space requirements.

To show the effect of `TEXT_INHIBIT_SPACES` have a look at this example::

    >>> print(page.get_text("text"))
    H a l l o !
    Mo r e  t e x t
    i s  f o l l o w i n g
    i n  E n g l i s h
    . . .  l e t ' s  s e e
    w h a t  h a p p e n s .
    >>> print(page.get_text("text", flags=pymupdf.TEXT_INHIBIT_SPACES))
    Hallo!
    More text
    is following
    in English
    ... let's see
    what happens.
    >>>


Performance
~~~~~~~~~~~~
The text extraction methods differ significantly both: in terms of information they supply, and in terms of resource requirements and runtimes. Generally, more information of course means, that more processing is required and a higher data volume is generated.

.. note:: Especially images have a **very significant** impact. Make sure to exclude them (via the *flags* parameter) whenever you do not need them. To process the below mentioned 2'700 total pages with default flags settings required 160 seconds across all extraction methods. When all images where excluded, less than 50% of that time (77 seconds) were needed.

To begin with, all methods are **very fast** in relation to other products out there in the market. In terms of processing speed, we are not aware of a faster (free) tool. Even the most detailed method, RAWDICT, processes all 1'310 pages of the :ref:`AdobeManual` in less than 5 seconds (simple text needs less than 2 seconds here).

The following table shows average relative speeds ("RSpeed", baseline 1.00 is TEXT), taken across ca. 1400 text-heavy and 1300 image-heavy pages.

======= ====== ===================================================================== ==========
Method  RSpeed Comments                                                               no images
======= ====== ===================================================================== ==========
TEXT     1.00  no images, **plain** text, line breaks                                 1.00
BLOCKS   1.00  image bboxes (only), **block** level text with bboxes, line breaks     1.00
WORDS    1.02  no images, **word** level text with bboxes                             1.02
XML      2.72  no images, **char** level text, layout and font details                2.72
XHTML    3.32  **base64** images, **span** level text, no layout info                 1.00
HTML     3.54  **base64** images, **span** level text, layout and font details        1.01
DICT     3.93  **binary** images, **span** level text, layout and font details        1.04
RAWDICT  4.50  **binary** images, **char** level text, layout and font details        1.68
======= ====== ===================================================================== ==========

As mentioned: when excluding image extraction (last column), the relative speeds are changing drastically: except RAWDICT and XML, the other methods are almost equally fast, and RAWDICT requires 40% less execution time than the **now slowest XML**.

Look at chapter **Appendix 1** for more performance information.

.. include:: footer.rst

---

## File: docs/app2.rst

.. include:: header.rst

.. _Appendix2:

================================================
Appendix 2: Considerations on Embedded Files
================================================
This chapter provides some background on embedded files support in PyMuPDF.

General
----------
Starting with version 1.4, PDF supports embedding arbitrary files as part ("Embedded File Streams") of a PDF document file (see chapter "7.11.4
Embedded File Streams", pp. 103 of the :ref:`AdobeManual`).

In many aspects, this is comparable to concepts also found in ZIP files or the OLE technique in MS Windows. PDF embedded files do, however, *not* support directory structures as does the ZIP format. An embedded file can in turn contain embedded files itself.

Advantages of this concept are that embedded files are under the PDF umbrella, benefitting from its permissions / password protection and integrity aspects: all data, which a PDF may reference or even may be dependent on, can be bundled into it and so form a single, consistent unit of information.

In addition to embedded files, PDF 1.7 adds *collections* to its support range. This is an advanced way of storing and presenting meta information (i.e. arbitrary and extensible properties) of embedded files.

MuPDF Support
--------------
After adding initial support for collections (portfolios) and */EmbeddedFiles* in MuPDF version 1.11, this support was dropped again in version 1.15.

As a consequence, the cli utility *mutool* no longer offers access to embedded files.

PyMuPDF -- having implemented an */EmbeddedFiles* API in response in its version 1.11.0 -- was therefore forced to change gears starting with its version 1.16.0 (we never published a MuPDF v1.15.x compatible PyMuPDF).

We are now maintaining our own code basis supporting embedded files. This code makes use of basic MuPDF dictionary and array functions only.

PyMuPDF Support
------------------
We continue to support the full old API with respect to embedded files -- with only minor, cosmetic changes.

There even also is a new function, which delivers a list of all names under which embedded data are registered in a PDF, :meth:`Document.embfile_names`.

.. include:: footer.rst

---

## File: docs/app3.rst

.. include:: header.rst

.. _Appendix3:

================================================
Appendix 3: Assorted Technical Information
================================================
This section deals with various technical topics, that are not necessarily related to each other.

------------

.. _ImageTransformation:

Image Transformation Matrix
----------------------------
Starting with version 1.18.11, the image transformation matrix is returned by some methods for text and image extraction: :meth:`Page.get_text` and :meth:`Page.get_image_bbox`.

The transformation matrix contains information about how an image was transformed to fit into the rectangle (its "boundary box" = "bbox") on some document page. By inspecting the image's bbox on the page and this matrix, one can determine for example, whether and how the image is displayed scaled or rotated on a page.

The relationship between image dimension and its bbox on a page is the following:

1. Using the original image's width and height,
    - define the image rectangle `imgrect = pymupdf.Rect(0, 0, width, height)`
    - define the "shrink matrix" `shrink = pymupdf.Matrix(1/width, 0, 0, 1/height, 0, 0)`.

2. Transforming the image rectangle with its shrink matrix, will result in the unit rectangle: `imgrect * shrink = pymupdf.Rect(0, 0, 1, 1)`.

3. Using the image **transformation matrix** "transform", the following steps will compute the bbox::

    imgrect = pymupdf.Rect(0, 0, width, height)
    shrink = pymupdf.Matrix(1/width, 0, 0, 1/height, 0, 0)
    bbox = imgrect * shrink * transform

4. Inspecting the matrix product `shrink * transform` will reveal all information about what happened to the image rectangle to make it fit into the bbox on the page: rotation, scaling of its sides and translation of its origin. Let us look at an example:

    >>> imginfo = page.get_images()[0]  # get an image item on a page
    >>> imginfo
    (5, 0, 439, 501, 8, 'DeviceRGB', '', 'fzImg0', 'DCTDecode')
    >>> #------------------------------------------------
    >>> # define image shrink matrix and rectangle
    >>> #------------------------------------------------
    >>> shrink = pymupdf.Matrix(1 / 439, 0, 0, 1 / 501, 0, 0)
    >>> imgrect = pymupdf.Rect(0, 0, 439, 501)
    >>> #------------------------------------------------
    >>> # determine image bbox and transformation matrix:
    >>> #------------------------------------------------
    >>> bbox, transform = page.get_image_bbox("fzImg0", transform=True)
    >>> #------------------------------------------------
    >>> # confirm equality - permitting rounding errors
    >>> #------------------------------------------------
    >>> bbox
    Rect(100.0, 112.37525939941406, 300.0, 287.624755859375)
    >>> imgrect * shrink * transform
    Rect(100.0, 112.375244140625, 300.0, 287.6247253417969)
    >>> #------------------------------------------------
    >>> shrink * transform
    Matrix(0.0, -0.39920157194137573, 0.3992016017436981, 0.0, 100.0, 287.6247253417969)
    >>> #------------------------------------------------
    >>> # the above shows:
    >>> # image sides are scaled by same factor ~0.4,
    >>> # and the image is rotated by 90 degrees clockwise
    >>> # compare this with pymupdf.Matrix(-90) * 0.4
    >>> #------------------------------------------------


------------

.. _Base-14-Fonts:

PDF Base 14 Fonts
---------------------
The following 14 builtin font names **must be supported by every PDF viewer** application. They are available as a dictionary, which maps their full names amd their abbreviations in lower case to the full font basename. Wherever a **fontname** must be provided in PyMuPDF, any **key or value** from the dictionary may be used::

    In [2]: pymupdf.Base14_fontdict
    Out[2]:
    {'courier': 'Courier',
    'courier-oblique': 'Courier-Oblique',
    'courier-bold': 'Courier-Bold',
    'courier-boldoblique': 'Courier-BoldOblique',
    'helvetica': 'Helvetica',
    'helvetica-oblique': 'Helvetica-Oblique',
    'helvetica-bold': 'Helvetica-Bold',
    'helvetica-boldoblique': 'Helvetica-BoldOblique',
    'times-roman': 'Times-Roman',
    'times-italic': 'Times-Italic',
    'times-bold': 'Times-Bold',
    'times-bolditalic': 'Times-BoldItalic',
    'symbol': 'Symbol',
    'zapfdingbats': 'ZapfDingbats',
    'helv': 'Helvetica',
    'heit': 'Helvetica-Oblique',
    'hebo': 'Helvetica-Bold',
    'hebi': 'Helvetica-BoldOblique',
    'cour': 'Courier',
    'coit': 'Courier-Oblique',
    'cobo': 'Courier-Bold',
    'cobi': 'Courier-BoldOblique',
    'tiro': 'Times-Roman',
    'tibo': 'Times-Bold',
    'tiit': 'Times-Italic',
    'tibi': 'Times-BoldItalic',
    'symb': 'Symbol',
    'zadb': 'ZapfDingbats'}

In contrast to their obligation, not all PDF viewers support these fonts correctly and completely -- this is especially true for Symbol and ZapfDingbats. Also, the glyph (visual) images will be specific to every reader.

To see how these fonts can be used -- including the **CJK built-in** fonts -- look at the table in :meth:`Page.insert_font`.

------------

.. _AdobeManual:

Adobe PDF References
---------------------------

This PDF Reference manual published by Adobe is frequently quoted throughout this documentation. It can be viewed and downloaded from `opensource.adobe.com <https://opensource.adobe.com/dc-acrobat-sdk-docs/standards/pdfstandards/pdf/PDF32000_2008.pdf>`_.


------------


.. _SequenceTypes:

Using Python Sequences as Arguments in PyMuPDF
------------------------------------------------
When PyMuPDF objects and methods require a Python **list** of numerical values, other Python **sequence types** are also allowed. Python classes are said to implement the **sequence protocol**, if they have a `__getitem__()` method.

This basically means, you can interchangeably use Python *list* or *tuple* or even *array.array*, *numpy.array* and *bytearray* types in these cases.

For example, specifying a sequence `"s"` in any of the following ways

* `s = [1, 2]` -- a list
* `s = (1, 2)` -- a tuple
* `s = array.array("i", (1, 2))` -- an array.array
* `s = numpy.array((1, 2))` -- a numpy array
* `s = bytearray((1, 2))` -- a bytearray

will make it usable in the following example expressions:

* `pymupdf.Point(s)`
* `pymupdf.Point(x, y) + s`
* `doc.select(s)`

Similarly with all geometry objects :ref:`Rect`, :ref:`IRect`, :ref:`Matrix` and :ref:`Point`.

Because all PyMuPDF geometry classes themselves are special cases of sequences, they (with the exception of :ref:`Quad` -- see below) can be freely used where numerical sequences can be used, e.g. as arguments for functions like *list()*, *tuple()*, *array.array()* or *numpy.array()*. Look at the following snippet to see this work.

>>> import pymupdf, array, numpy as np
>>> m = pymupdf.Matrix(1, 2, 3, 4, 5, 6)
>>>
>>> list(m)
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
>>>
>>> tuple(m)
(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
>>>
>>> array.array("f", m)
array('f', [1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
>>>
>>> np.array(m)
array([1., 2., 3., 4., 5., 6.])

.. note:: :ref:`Quad` is a Python sequence object as well and has a length of 4. Its items however are :data:`point_like` -- not numbers. Therefore, the above remarks do not apply.

------------

.. _ReferenialIntegrity:

Ensuring Consistency of Important Objects in PyMuPDF
------------------------------------------------------------
PyMuPDF is a Python binding for the C library MuPDF. While a lot of effort has been invested by MuPDF's creators to approximate some sort of an object-oriented behavior, they certainly could not overcome basic shortcomings of the C language in that respect.

Python on the other hand implements the OO-model in a very clean way. The interface code between PyMuPDF and MuPDF consists of two basic files: *pymupdf.py* and *fitz_wrap.c*. They are created by the excellent SWIG tool for each new version.

When you use one of PyMuPDF's objects or methods, this will result in execution of some code in *pymupdf.py*, which in turn will call some C code compiled with *fitz_wrap.c*.

Because SWIG goes a long way to keep the Python and the C level in sync, everything works fine, if a certain set of rules is being strictly followed. For example: **never access** a :ref:`Page` object, after you have closed (or deleted or set to ``None``) the owning :ref:`Document`. Or, less obvious: **never access** a page or any of its children (links or annotations) after you have executed one of the document methods *select()*, *delete_page()*, *insert_page()* ... and more.

But just no longer accessing invalidated objects is actually not enough: They should rather be actively deleted entirely, to also free C-level resources (meaning allocated memory).

The reason for these rules lies in the fact that there is a hierarchical 2-level one-to-many relationship between a document and its pages and also between a page and its links / annotations. To maintain a consistent situation, any of the above actions must lead to a complete reset -- in **Python and, synchronously, in C**.

SWIG cannot know about this and consequently does not do it.

The required logic has therefore been built into PyMuPDF itself in the following way.

1. If a page "loses" its owning document or is being deleted itself, all of its currently existing annotations and links will be made unusable in Python, and their C-level counterparts will be deleted and deallocated.

2. If a document is closed (or deleted or set to ``None``) or if its structure has changed, then similarly all currently existing pages and their children will be made unusable, and corresponding C-level deletions will take place. "Structure changes" include methods like *select()*, *delePage()*, *insert_page()*, *insert_pdf()* and so on: all of these will result in a cascade of object deletions.

The programmer will normally not realize any of this. If he, however, tries to access invalidated objects, exceptions will be raised.

Invalidated objects cannot be directly deleted as with Python statements like *del page* or *page = None*, etc. Instead, their *__del__* method must be invoked.

All pages, links and annotations have the property *parent*, which points to the owning object. This is the property that can be checked on the application level: if *obj.parent == None* then the object's parent is gone, and any reference to its properties or methods will raise an exception informing about this "orphaned" state.

A sample session:

>>> page = doc[n]
>>> annot = page.first_annot
>>> annot.type                    # everything works fine
[5, 'Circle']
>>> page = None                   # this turns 'annot' into an orphan
>>> annot.type
<... omitted lines ...>
RuntimeError: orphaned object: parent is None
>>>
>>> # same happens, if you do this:
>>> annot = doc[n].first_annot     # deletes the page again immediately!
>>> annot.type                    # so, 'annot' is 'born' orphaned
<... omitted lines ...>
RuntimeError: orphaned object: parent is None

This shows the cascading effect:

>>> doc = pymupdf.open("some.pdf")
>>> page = doc[n]
>>> annot = page.first_annot
>>> page.rect
pymupdf.Rect(0.0, 0.0, 595.0, 842.0)
>>> annot.type
[5, 'Circle']
>>> del doc                       # or doc = None or doc.close()
>>> page.rect
<... omitted lines ...>
RuntimeError: orphaned object: parent is None
>>> annot.type
<... omitted lines ...>
RuntimeError: orphaned object: parent is None

.. note:: Objects outside the above relationship are not included in this mechanism. If you e.g. created a table of contents by *toc = doc.get_toc()*, and later close or change the document, then this cannot and does not change variable *toc* in any way. It is your responsibility to refresh such variables as required.

------------

.. _FormXObject:

Design of Method :meth:`Page.show_pdf_page`
--------------------------------------------

Purpose and Capabilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The method displays an image of a ("source") page of another PDF document within a specified rectangle of the current ("containing", "target") page.

* **In contrast** to :meth:`Page.insert_image`, this display is vector-based and hence remains accurate across zooming levels.
* **Just like** :meth:`Page.insert_image`, the size of the display is adjusted to the given rectangle.

The following variations of the display are currently supported:

* Bool parameter `"keep_proportion"` controls whether to maintain the aspect ratio (default) or not.
    * Rectangle parameter `"clip"` restricts the visible part of the source page rectangle. Default is the full page.
* float `"rotation"` rotates the display by an arbitrary angle (degrees). If the angle is not an integer multiple of 90, only 2 of the 4 corners may be positioned on the target border if also `"keep_proportion"` is true.
* Bool parameter `"overlay"` controls whether to put the image on top (foreground, default) of current page content or not (background).

Use cases include (but are not limited to) the following:

1. "Stamp" a series of pages of the current document with the same image, like a company logo or a watermark.
2. Combine arbitrary input pages into one output page to support โ€œbookletโ€ or double-sided printing (known as "4-up", "n-up").
3. Split up (large) input pages into several arbitrary pieces. This is also called โ€œposterizationโ€, because you e.g. can split an A4 page horizontally and vertically, print the 4 pieces enlarged to separate A4 pages, and end up with an A2 version of your original page.

Technical Implementation
~~~~~~~~~~~~~~~~~~~~~~~~~

This is done using PDF **"Form XObjects"**, see section 8.10 on page 217 of :ref:`AdobeManual`. On execution of a :meth:`Page.show_pdf_page`, the following things happen:

    1. The :data:`resources` and :data:`contents` objects of source page in source document are copied over to the target document, jointly creating a new **Form XObject** with the following properties. The PDF :data:`xref` number of this object is returned by the method.

        a. `/BBox` equals `/Mediabox` of the source page
        b. `/Matrix` equals the identity matrix.
        c. `/Resources` equals that of the source page. This involves a โ€œdeep-copyโ€ of hierarchically nested other objects (including fonts, images, etc.). The complexity involved here is covered by MuPDF's grafting [#f1]_ technique functions.
        d. This is a stream object type, and its stream is an exact copy of the combined data of the source page's :data:`contents` objects.

        This Form XObject is only executed once per shown source page. Subsequent displays of the same source page will skip this step and only create "pointer" Form XObjects (done in next step) to this object.

    2. A second **Form XObject** is then created which the target page uses to invoke the display. This object has the following properties:

        a. `/BBox` equals the `/CropBox` of the source page (or `"clip"`).
        b. `/Matrix` represents the mapping of `/BBox` to the target rectangle.
        c. `/XObject` references the previous Form XObject via the fixed name `fullpage`.
        d. The stream of this object contains exactly one fixed statement: `/fullpage Do`.
        e. If the method's `"oc"` argument is given, its value is assigned to this Form XObject as `/OC`.

    3. The :data:`resources` and :data:`contents` objects of the target page are now modified as follows.

        a. Add an entry to the `/XObject` dictionary of `/Resources` with the name `fzFrm<n>` (with n chosen such that this entry is unique on the page).
        b. Depending on `"overlay"`, prepend or append a new object to the page's `/Contents` array, containing the statement `q /fzFrm<n> Do Q`.

This design approach ensures that:

1. The (potentially large) source page is only copied once to the target PDF. Only small "pointer" Form XObjects objects are created per each target page to show the source page.
2. Each referring target page can have its own `"oc"` parameter to control the source page's visibility individually.



.. _RedirectMessages:

Diagnostics
----------------------------

.. _Messages:

|PyMuPDF| messages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

|PyMuPDF| has a Message system for showing text diagnostics.

By default messages are written to `sys.stdout`. This can be controlled in
two ways:

*
  Set environment variable `PYMUPDF_MESSAGE` before |PyMuPDF| is imported.

*
  Call `set_messages()`:


|MuPDF| errors and warnings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

|MuPDF| generates text errors and warnings.

*
  These errors and warnings are appended to an internal list, accessible with
  `Tools.mupdf_warnings()`. Also see `Tools.reset_mupdf_warnings()`.

*
  By default these errors and warnings are also sent to the |PyMuPDF| message
  system.
  
  * This can be controlled with `mupdf_display_errors()` and
    `mupdf_display_warnings()`.
  
  *
    These messages are prefixed with `MuPDF error:` and `MuPDF warning:`
    respectively.
  
Some |MuPDF| errors may lead to Python exceptions.

Example output for a **recoverable error**. We are opening a damaged PDF, but MuPDF is able to repair it and gives us a little information on what happened. Then we illustrate how to find out whether the document can later be saved incrementally. Checking the :attr:`Document.is_dirty` attribute at this point also indicates that during `pymupdf.open` the document had to be repaired:

>>> import pymupdf
>>> doc = pymupdf.open("damaged-file.pdf")  # leads to a sys.stderr message:
mupdf: cannot find startxref
>>> print(pymupdf.TOOLS.mupdf_warnings())  # check if there is more info:
cannot find startxref
trying to repair broken xref
repairing PDF document
object missing 'endobj' token
>>> doc.can_save_incrementally()  # this is to be expected:
False
>>> # the following indicates whether there are updates so far
>>> # this is the case because of the repair actions:
>>> doc.is_dirty
True
>>> # the document has nevertheless been created:
>>> doc
pymupdf.Document('damaged-file.pdf')
>>> # we now know that any save must occur to a new file

Example output for an **unrecoverable error**:

>>> import pymupdf
>>> doc = pymupdf.open("does-not-exist.pdf")
mupdf: cannot open does-not-exist.pdf: No such file or directory
Traceback (most recent call last):
  File "", line 1, in <module>
    doc = pymupdf.open("does-not-exist.pdf")
  File "C:\Users\Jorj\AppData\Local\Programs\Python\Python37\lib\site-packages\fitz\pymupdf.py", line 2200, in __init__
    _pymupdf.Document_swiginit(self, _pymupdf.new_Document(filename, stream, filetype, rect, width, height, fontsize))
RuntimeError: cannot open does-not-exist.pdf: No such file or directory
>>>



.. _Coordinates:

Coordinates
--------------------------------------------


This is one of the most frequently used terms in this documentation. A **coordinate** generally means a pair of numbers `(x, y)` referring to some location, like a corner of a rectangle (:ref:`Rect`), a :ref:`Point` and so forth. The two values usually are floats, but there a objects like images which only allow them to be integers.

To actually *find* a coordinate's location, we also need to know the *reference* point for ``x`` and ``y`` - in other words, we must know where location `(0, 0)` is positioned. Once `(0, 0)` (the "origin") is known, we speak of a "coordinate system".

Several coordinate systems exist in document processing. For instance, the coordinate systems of a PDF page and the image created from it are **different**. We therefore need ways to *transform* coordinates from one system to another (and also back occasionally). This is the task of a :ref:`Matrix`. It is a mathematical function which works much like a factor that can be "multiplied" with a point or rectangle to give us the corresponding point / rectangle in another coordinate system. The inverse of a transformation matrix can be used to revert the transformation. Much like multiplying by some factor, say 3, can be reverted by dividing the result by 3 (or multiplying it with 1/3).

Coordinates and Images
~~~~~~~~~~~~~~~~~~~~~~~

Images have a coordinate system with integer coordinates. Origin `(0, 0)` is the top-left point. ``x`` values must be in `range(width)`, and ``y`` values in `range(height)`. Therefore, ``y`` values *increase* if we go *downwards*. For every image, there is only a **finite number** of coordinates, namely `width * height`. A location in an image is also called a "pixel".

- How **large** an image will be (in centimeters or inches) when e.g. printed, depends on additional information: the "resolution". This is measured in **DPI** (dots per inch, or pixels per inch). To find the printed size of some image, we therefore must divide its width and its height by the corresponding DPI values (there may separate ones for width and for height) and will get the respective number of inches.


Origin Point, Point Size and Y-Axis

In |PDF|, the origin (0, 0) of a page is located at its bottom-left point. In |MuPDF|, the origin (0, 0) of a page is located at its top-left point.

.. image:: images/img-coordinate-space.png

Coordinates are float numbers and measured in points, where:

  • one point equals 1/72 inches.

Typical document page sizes are ISO A4 and Letter. A Letter page has a size of 8.5 x 11 inches, corresponding to 612 x 792 points. In the |PDF| coordinate system, the top-left point of a Letter page hence has the coordinate (0, 792) as the y-axis points upwards. Now we know our document size the |MuPDF| coordinate system for the bottom right would be coordinate (612, 792) (and for |PDF| this coordinate would then be (612,0)).

  • Theoretically, there are infinitely many coordinate positions on a |PDF| page. In practice however, at most the first 5 decimal places are sufficient for a reasonable precision.

  • In |MuPDF|, multiple document formats are supported - |PDF| just being one among over a dozen others. Images are also supported as documents in |MuPDF| (therefore having one page usually). This is one of the reasons why |MuPDF| uses a coordinate system with the origin (0, 0) being the top-left point of any document page. The y-axis points downwards, like with images. Coordinates in |MuPDF| in any case are floats, like in |PDF|.

  • A rectangle Rect(0, 0, 100, 100) for instance in |MuPDF| (and thus |PyMuPDF|) therefore is a square with edges of length 100 points (= 1.39 inches or 3.53 centimeters). Its top-left corner is the origin. To switch between the two coordinate systems |PDF| to |MuPDF|, every :ref:Page object has a :attr:Page.transformation_matrix. Its inverse can be used to compute a rectangle's PDF coordinates. In this way we can conveniently find that Rect(0, 0, 100, 100) in |MuPDF| is the same as Rect(0, 692, 100, 792) in |PDF|. See this code snippet::

    page = doc.new_page(width=612, height=792) # make new Letter page
    ptm = page.transformation_matrix

    the inverse matrix of ptm is ~ptm

    pymupdf.Rect(0, 0, 100, 100) * ~ptm
    Rect(0.0, 692.0, 100.0, 792.0)

.. _CSS_Support:

CSS Support

For now, only a subset of CSS properties are supported.

The underlying C library MuPDF supports a subset of HTML4 and CSS2. The primary goal of the HTML/CSS support is to serve as a popular and convenient way to style text โ€” not to faithfully reproduce websites in PDF.

What Works

text
The following list shows the supported properties, grouped by category.

Box Model & Layout
""""""""""""""""""

``margin``, ``margin-top``, ``margin-right``, ``margin-bottom``, ``margin-left``, ``padding``, ``padding-top``, ``padding-right``, ``padding-bottom``, ``padding-left``, ``width``, ``height``, ``display``, ``position``, ``top``, ``right``, ``bottom``, ``left``, ``inset``, ``overflow-wrap``, ``columns``

.. note:: 
    
    The properties ``position`` & ``display`` are supported in a very limited way. Only the values ``position: relative`` and ``display: block`` are supported.


Border
""""""""""""""""""

``border``, ``border-top``, ``border-right``, ``border-bottom``, ``border-left``, ``border-color``, ``border-style``, ``border-width``, ``border-spacing``, ``border-collapse``, ``border-top-color``, ``border-right-color``, ``border-bottom-color``, ``border-left-color``, ``border-top-style``, ``border-right-style``, ``border-bottom-style``, ``border-left-style``, ``border-top-width``, ``border-right-width``, ``border-bottom-width``, ``border-left-width``

Background
""""""""""""""""""

``background``, ``background-color``

.. note::

    Background images are not supported, but the ``background`` property can be used to set a background color for a text block, which is then rendered as a filled rectangle behind the text.

Font
""""""""""""""""""

``font``, ``font-family``, ``font-size``, ``font-style``, ``font-variant``, ``font-weight``

Text
""""""""""""""""""

``color``, ``letter-spacing``, ``line-height``, ``text-align``, ``text-decoration``, ``text-indent``, ``text-transform``, ``word-spacing``, ``white-space``, ``vertical-align``, ``direction``, ``hyphens``

List
""""""""""""""""""

``list-style``, ``list-style-image``, ``list-style-position``, ``list-style-type``

Page
""""""""""""""""""

``page-break-before``, ``page-break-after``, ``orphans``, ``widows``

Visibility
""""""""""""""""""""""""""""""""""""

``visibility``

MuPDF-specific / WebKit extensions
""""""""""""""""""""""""""""""""""""

``-mupdf-leading``, ``-webkit-text-fill-color``, ``-webkit-text-stroke-color``, ``-webkit-text-stroke-width``

Other
""""""""""""""""""

``src`` (for @font-face), ``overflow-wrap``




What Doesn't Work

Modern CSS (CSS3+): no flexbox, grid, custom properties (--vars), calc(), transitions, animations, float, clear.

.. rubric:: Footnotes

.. [#f1] MuPDF supports "deep-copying" objects between PDF documents. To avoid duplicate data in the target, it uses so-called "graftmaps", like a form of scratchpad: for each object to be copied, its :data:xref number is looked up in the graftmap. If found, copying is skipped. Otherwise, the new :data:xref is recorded and the copy takes place. PyMuPDF makes use of this technique in two places so far: :meth:Document.insert_pdf and :meth:Page.show_pdf_page. This process is fast and very efficient, because it prevents multiple copies of typically large and frequently referenced data, like images and fonts. However, you may still want to consider using garbage collection (option 4) in any of the following cases:

text
1. The target PDF is not new / empty: grafting does not check for resources that already existed (e.g. images, fonts) in the target document before opening it.
2. Using :meth:`Page.show_pdf_page` for more than one source document: each grafting occurs **within one source** PDF only, not across multiple. So if e.g. the same image exists in pages from different source PDFs, then this will not be detected until garbage collection.

.. include:: footer.rst


File: docs/archive-class.rst

.. include:: header.rst

.. _Archive:

================ Archive

  • New in v1.21.0

This class represents a generalization of file folders and container files like ZIP and TAR archives. Archives allow accessing arbitrary collections of file folders, ZIP / TAR files and single binary data elements as if they all were part of one hierarchical tree of folders.

In PyMuPDF, archives are currently only used by :ref:Story objects and as an :ref:option when opening files <Full_Options_for_Opening_a_File> to specify where to look for fonts, images and other resources.

================================ ===================================================
Method / Attribute Short Description
================================ ===================================================
:meth:Archive.add add new data to the archive
:meth:Archive.has_entry check if given name is a member
:meth:Archive.read_entry read the data given by the name
:attr:Archive.entry_list list[dict] of archive items
================================ ===================================================

Class API

.. class:: Archive

.. method:: init(self [, content [, path]])

text
Creates a new archive. Without parameters, an empty archive is created.

  If provided, `content` may be one of the following:

  * another Archive: the archive is being made a sub-archive of the new one.

  * a string: this must be the name of a local folder or file. `pathlib.Path` objects are also supported.

     - A **folder** will be converted to a sub-archive, so its files (and any sub-folders) can be accessed by their names.
     - A **file** will be read with mode `"rb"` and these binary data (a `bytes` object) be treated as a single-member sub-archive. In this case, the `path` parameter is **mandatory** and should be the member name under which this item can be found / retrieved.

  * a `zipfile.ZipFile` or `tarfile.TarFile` object: Will be added as a sub-archive.

  * a Python binary object (`bytes`, `bytearray`, `io.BytesIO`): this will add a single-member sub-archive. In this case, the `path` parameter is **mandatory** and should be the member name under which this item can be found / retrieved.

  * a tuple `(data, name)`: This will add a single-member sub-archive with the member name ``name``. ``data`` may be a Python binary object or a local file name (in which case its binary file content is used). Use this format if you need to specify `path`.

  * a Python sequence: This is a convenience format to specify any combination of the above.

  If provided, `path` must be a string.
  
  * If `content` is either binary data or a file name, this parameter is mandatory and must be the name under which the data can be found.

  * Otherwise this parameter is optional. It can be used to simulate a folder name or a mount point, under which this sub-archive's elements can be found. For example this specification `Archive((data, "name"), "path")` means that `data` will be found using the element name `"path/name"`. Similar is true for other sub-archives: to retrieve members of a ZIP sub-archive, their names must be prefixed with `"path/"`. The main purpose of this parameter probably is to differentiate between duplicate names.

  .. note:: If duplicate entry names exist in the archive, always the last entry with that name will be found / retrieved. During archive creation, or appending more data to an archive (see :meth:`Archive.add`) no check for duplicates will be made. Use the `path` parameter to prevent this from happening.

.. method:: add(content [,path])

text
Append a sub-archive. The meaning of the parameters are exactly the same as explained above. Of course, parameter `content` is not optional here.

.. method:: has_entry(name)

text
Checks whether an entry exists in any of the sub-archives.

  :arg str name: The fully qualified name of the entry. So must include any `path` prefix under which the entry's sub-archive has been added.

  :returns: `True` or `False`.

.. method:: read_entry(name)

text
Retrieve the data of an entry.

  :arg str name: The fully qualified name of the entry. So must include any `path` prefix under which the entry's sub-archive has been added.

  :returns: The binary data (`bytes`) of the entry. If not found, an exception is raised.

.. attribute:: entry_list

text
A list of the archive's sub-archives. Each list item is a dictionary with the following keys:

  * `entries` -- a list of (top-level) entry names in this sub-archive.
  * `fmt` -- the format of the sub-archive. This is one of the strings "dir" (file folder), "zip" (ZIP archive), "tar" (TAR archive), or "tree" for single binary entries or file content.
  * `path` -- the value of the `path` parameter under which this sub-archive was added.

  **Example:**

  >>> from pprint import pprint
  >>> import pymupdf
  >>> dir1 = "fitz-32"  # a folder name
  >>> dir2 = "fitz-64"  # a folder name
  >>> img = ("nur-ruhig.jpg", "img")  # an image file
  >>> members = (dir1, img, dir2)  # we want to append these in one go
  >>> arch = pymupdf.Archive()
  >>> arch.add(members, path="mypath")
  >>> pprint(arch.entry_list)
  [{'entries': ['310', '37', '38', '39'], 'fmt': 'dir', 'path': 'mypath'},
  {'entries': ['img'], 'fmt': 'tree', 'path': 'mypath'},
  {'entries': ['310', '311', '37', '38', '39', 'pypy'],
  'fmt': 'dir',
  'path': 'mypath'}]
  >>>

.. include:: footer.rst


File: docs/classes.rst

.. include:: header.rst

============ Classes

.. toctree::
:maxdepth: 2

annot.rst
archive-class.rst
colorspace.rst
displaylist.rst
document.rst
document-writer-class.rst
font.rst
identity.rst
irect.rst
link.rst
linkdest.rst
matrix.rst
outline.rst
page.rst
pixmap.rst
point.rst
quad.rst
rect.rst
shape.rst
story-class.rst
textpage.rst
textwriter.rst
tools.rst
widget.rst
xml-class.rst

.. include:: footer.rst


File: docs/colors.rst

.. include:: header.rst

.. _ColorDatabase:

================ Color Database

Since the introduction of methods involving colors (like :meth:Page.draw_circle), a requirement may be to have access to predefined colors.

The fabulous GUI package wxPython <https://wxpython.org/>_ has a database of over 540 predefined RGB colors, which are given more or less memorizable names. Among them are not only standard names like "green" or "blue", but also "turquoise", "skyblue", and 100 (not only 50 ...) shades of "gray", etc.

We have taken the liberty to copy this database (a list of tuples) modified into PyMuPDF and make its colors available as PDF compatible float triples: for wxPython's ("WHITE", 255, 255, 255) we return (1, 1, 1), which can be directly used in color and fill parameters. We also accept any mixed case of "wHiTe" to find a color.

Function *getColor()*

As the color database may not be needed very often, one additional import statement seems acceptable to get access to it::

text
>>> # "getColor" is the only method you really need
>>> from pymupdf.utils import getColor
>>> getColor("aliceblue")
(0.9411764705882353, 0.9725490196078431, 1.0)
>>> #
>>> # to get a list of all existing names
>>> from pymupdf.utils import getColorList
>>> cl = getColorList()
>>> cl
['ALICEBLUE', 'ANTIQUEWHITE', 'ANTIQUEWHITE1', 'ANTIQUEWHITE2', 'ANTIQUEWHITE3',
'ANTIQUEWHITE4', 'AQUAMARINE', 'AQUAMARINE1'] ...
>>> #
>>> # to see the full integer color coding
>>> from pymupdf.utils import getColorInfoList
>>> il = getColorInfoList()
>>> il
[('ALICEBLUE', 240, 248, 255), ('ANTIQUEWHITE', 250, 235, 215),
('ANTIQUEWHITE1', 255, 239, 219), ('ANTIQUEWHITE2', 238, 223, 204),
('ANTIQUEWHITE3', 205, 192, 176), ('ANTIQUEWHITE4', 139, 131, 120),
('AQUAMARINE', 127, 255, 212), ('AQUAMARINE1', 127, 255, 212)] ...

Printing the Color Database

If you want to actually see how the many available colors look like, use scripts print by RGB <https://github.com/pymupdf/PyMuPDF-Utilities/blob/master/examples/print-rgb/print.py>_ or print by HSV <https://github.com/pymupdf/PyMuPDF-Utilities/blob/master/examples/print-hsv/print.py>_ in the examples directory. They create PDFs (already existing in the same directory) with all these colors. Their only difference is sorting order: one takes the RGB values, the other one the Hue-Saturation-Values as sort criteria.
This is a screen print of what these files look like.

.. image:: images/img-colordb.*

.. include:: footer.rst