Extract images from a PDF using pymupdf's dict-mode block iteration. Uses ``page.get_text("dict")`` to find image blocks (type 1) in reading order. Each image block is rendered via :class:`pymupdf.Pixmap` and saved as PNG. This captures both embedded bitmaps *and* vector-rendered figure
(pdf_path: Path, doc_name: str, images_dir: Path)
| 24 | |
| 25 | |
| 26 | def extract_pdf_images(pdf_path: Path, doc_name: str, images_dir: Path) -> dict[int, list[str]]: |
| 27 | """Extract images from a PDF using pymupdf's dict-mode block iteration. |
| 28 | |
| 29 | Uses ``page.get_text("dict")`` to find image blocks (type 1) in reading |
| 30 | order. Each image block is rendered via :class:`pymupdf.Pixmap` and saved |
| 31 | as PNG. This captures both embedded bitmaps *and* vector-rendered figures |
| 32 | that ``get_images()`` would miss. |
| 33 | |
| 34 | Returns a mapping of page_number (1-based) → list of relative image paths. |
| 35 | """ |
| 36 | images_dir.mkdir(parents=True, exist_ok=True) |
| 37 | page_images: dict[int, list[str]] = {} |
| 38 | img_counter = 0 |
| 39 | |
| 40 | with pymupdf.open(str(pdf_path)) as doc: |
| 41 | for page_idx in range(len(doc)): |
| 42 | page = doc[page_idx] |
| 43 | page_num = page_idx + 1 |
| 44 | |
| 45 | for block in page.get_text("dict")["blocks"]: |
| 46 | if block["type"] != 1: # not an image block |
| 47 | continue |
| 48 | |
| 49 | width = block.get("width", 0) |
| 50 | height = block.get("height", 0) |
| 51 | if width < _MIN_IMAGE_DIM or height < _MIN_IMAGE_DIM: |
| 52 | continue |
| 53 | |
| 54 | image_bytes = block.get("image") |
| 55 | if not image_bytes: |
| 56 | continue |
| 57 | |
| 58 | try: |
| 59 | pix = pymupdf.Pixmap(image_bytes) |
| 60 | if pix.n > 4: |
| 61 | pix = pymupdf.Pixmap(pymupdf.csRGB, pix) |
| 62 | img_counter += 1 |
| 63 | filename = f"p{page_num}_img{img_counter}.png" |
| 64 | save_path = images_dir / filename |
| 65 | pix.save(str(save_path)) |
| 66 | pix = None |
| 67 | except Exception: |
| 68 | logger.warning("Failed to save image block on page %d", page_num) |
| 69 | continue |
| 70 | |
| 71 | rel_path = f"sources/images/{doc_name}/{filename}" |
| 72 | page_images.setdefault(page_num, []).append(rel_path) |
| 73 | return page_images |
| 74 | |
| 75 | |
| 76 | def convert_pdf_to_pages(pdf_path: Path, doc_name: str, images_dir: Path) -> list[dict]: |