Extract Text and Images from PDF with PyMuPDF
python pymupdf extract text and images from pdf: Learn how to extract text and images from PDF files using PyMuPDF in Python, with practical code examples and performa...
python pymupdf extract text and images from pdf requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you need to pull text and images out of a PDF in Python, PyMuPDF (imported as fitz) is one of the most direct options. It gives you page-level access to both the text layer and embedded raster images without requiring a separate OCR engine or complex parsing pipeline. This article shows how to extract text and images from a PDF using PyMuPDF, how to combine both extractions in a single pass, and what to watch out for when working with large or unusual PDFs.
Setting Up PyMuPDF and Loading a PDF
Install PyMuPDF with pip:
pip install PyMuPDF
The import name is fitz, which is the name of the underlying library that PyMuPDF wraps. After importing, you open a PDF by passing a file path to fitz.open():
import fitz doc = fitz.open("sample.pdf")
This returns a Document object. You can iterate over its pages with doc itself, or access a specific page by index. Always close the document when you are done to release the file handle:
doc.close()
If the PDF is password-protected, you need to authenticate before accessing pages:
if doc.needs_pass: doc.authenticate("password")
Extracting Text from a PDF Page
The core method for text extraction is page.get_text(). Called with no arguments, it returns the plain text on the page as a single string, preserving line breaks and spaces as they appear in the content stream. This is often enough for simple text dumps, but it does not give you positional information or the ability to separate text blocks.
For more structured output, get_text() accepts a mode parameter. The most useful modes are:
| Mode | Return type | Description |
|---|---|---|
"text" | str | Plain text with line breaks. |
"blocks" | list of tuples | Each block is a tuple with coordinates, text, block type, and block number. |
"dict" | dict | A nested dictionary with page-level structure. |
"words" | list of tuples | Each word as a tuple with coordinates and text. |
For example, to get text blocks with their bounding boxes:
page = doc[0] blocks = page.get_text("blocks") for block in blocks: x0, y0, x1, y1, text, block_no, block_type = block print(f"Block {block_no} at ({x0:.1f}, {y0:.1f}): {text[:50]}...")
The block_type is 0 for text and 1 for image blocks. This distinction is useful when you want to process text and images together.
When a PDF has multiple columns or complex layouts, the default "text" mode may produce a jumbled reading order. Using "blocks" or "dict" lets you reconstruct the intended order by sorting blocks by their vertical and horizontal positions. For most extraction tasks, "blocks" is a good balance between simplicity and control.
Extracting Images from a PDF Page
PyMuPDF can extract the raw image data embedded in a page. The method page.get_images(full=True) returns a list of image references. Each reference is a tuple containing an xref (cross-reference number), which you can use to retrieve the actual image bytes.
page = doc[0] image_list = page.get_images(full=True) for img in image_list: xref = img[0] pix = fitz.Pixmap(doc, xref) if pix.n - pix.alpha >= 4: # CMYK or other color spaces pix = fitz.Pixmap(fitz.csRGB, pix) pix.save(f"image_{xref}.png") pix = None # free memory
This code does the following:
page.get_images(full=True)returns all images referenced on the page, including those nested inside Form XObjects.- For each image, we get its
xrefand create aPixmapfrom the document and that xref. - If the image uses a color space that is not RGB (like CMYK), we convert it to RGB for standard PNG output.
pix.save()writes the image to a file. The format is inferred from the file extension; you can also pass a format string.- Setting
pix = Nonefrees the underlying memory, which is important when processing many images.
Note that get_images() returns only images that are directly embedded in the page's content stream. Images that are only referenced via annotations or as page backgrounds may not appear here. To capture those, you would need to inspect the page's /Resources dictionary directly, which is more advanced.
Combining Text and Image Extraction in One Pass
Often you want both the text and the images from a page, and you need to know where each image appears relative to the text. The "blocks" mode gives you this directly because image blocks have block_type == 1 and include the image's bounding box. You can iterate over blocks and handle text and image blocks differently:
page = doc[0] blocks = page.get_text("blocks") for block in blocks: x0, y0, x1, y1, content, block_no, block_type = block if block_type == 0: print(f"Text: {content}") elif block_type == 1: # content is an image xref in this case xref = content pix = fitz.Pixmap(doc, xref) if pix.n - pix.alpha >= 4: pix = fitz.Pixmap(fitz.csRGB, pix) pix.save(f"block_{block_no}.png") pix = None
In the "blocks" tuple, for image blocks, the content field is the image's xref number, not the image bytes. This lets you extract the image without calling get_images() separately. However, get_images() is still useful when you want to list all images on a page regardless of their block association.
If you need to map images to specific text regions, the bounding box coordinates from the block tuple give you the exact placement. You can then use those coordinates to associate an image with nearby text blocks.
Performance and Memory Considerations
PyMuPDF is a C-based library, so it is generally fast for text and image extraction. However, when working with large PDFs or documents with many high-resolution images, memory usage can become a concern if you are not careful.
Each Pixmap holds the decoded image data in memory. For a 300 DPI full-page image, that can be several megabytes. If you extract many images without releasing the pixmaps, memory will grow quickly. The pattern of setting pix = None after saving is a simple way to allow garbage collection. Alternatively, you can use del pix and call gc.collect() if you are in a tight loop, but that is rarely necessary.
For text extraction, the get_text() methods return strings or lists that are also held in memory. If you are processing a document with thousands of pages and you only need a summary, consider processing page by page and discarding the extracted data after you have used it, rather than accumulating everything in a list.
Another performance factor is the full=True parameter in get_images(). When full=False, PyMuPDF may return only images that are not nested inside Form XObjects, which can be faster but less complete. For comprehensive extraction, use full=True, but be aware that it may return duplicates if the same image appears multiple times on a page.
Common Pitfalls and Edge Cases
Password-Protected PDFs
If you open a PDF that requires a password, doc.needs_pass will be True. You must call doc.authenticate(password) before accessing pages. If the password is incorrect, authenticate() returns False and page access will raise an exception.
Scanned PDFs
PyMuPDF extracts text from the embedded text layer. If the PDF is a scanned document with no OCR text, get_text() will return an empty string. In that case, you need an OCR tool like Tesseract or a dedicated OCR library. PyMuPDF itself does not perform OCR.
Image Formats and Transparency
Pixmap.save() infers the output format from the file extension. For PNG, it preserves alpha transparency if the original image has an alpha channel. If you want to force a format regardless of extension, pass the format string as the second argument, e.g., pix.save("image", "png").
Coordinate System
PyMuPDF uses a coordinate system where the origin is at the top-left corner of the page, with y increasing downward. This is the same as most PDF viewers, but it differs from the bottom-left origin used in some PDF specifications. When you use bounding boxes from get_text("blocks"), they are in this top-left system.
Duplicate Images
The same image may appear on multiple pages or multiple times on a single page. page.get_images(full=True) returns each reference, but you may want to deduplicate by xref to avoid saving the same image repeatedly. You can maintain a set of seen xrefs and skip those you have already processed.
When to Choose PyMuPDF Over Other Libraries
PyMuPDF is a strong choice when you need both text and image extraction in one library, and when you need fine-grained control over layout and image data. It is faster than pure-Python libraries like PyPDF2 for many operations because it is backed by C code. However, it has a steeper learning curve than simpler libraries like pdfplumber for basic text extraction, and its API is more complex.
If your primary need is simple text extraction from well-structured PDFs, pdfplumber or even PyPDF2 may be sufficient. But if you need to extract images, handle complex layouts, or process large documents efficiently, PyMuPDF is worth the extra complexity. It also supports rendering pages to images, which is useful for thumbnails or previews.
For a one-off script where you need to quickly pull text and images from a few PDFs, PyMuPDF's combined API lets you do both without pulling in multiple dependencies. The ability to get image blocks directly from the text extraction output is a unique advantage that simplifies the task of associating images with their surrounding text.