From f79b4356c3b70dd0c9ba0a4b18ad5c1a2b50483c Mon Sep 17 00:00:00 2001 From: myhloli Date: Tue, 2 Jun 2026 19:34:32 +0800 Subject: [PATCH] feat: add functionality to skip broken PDF pages during rewrite process --- mineru/cli/common.py | 45 ++++++++++++++++++++++++++++++++++-- mineru/utils/pdfium_guard.py | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/mineru/cli/common.py b/mineru/cli/common.py index 3d69de8e..f029dd76 100644 --- a/mineru/cli/common.py +++ b/mineru/cli/common.py @@ -23,7 +23,10 @@ from mineru.backend.vlm.vlm_analyze import aio_doc_analyze as aio_vlm_doc_analyz from mineru.backend.office.pptx_analyze import office_pptx_analyze from mineru.backend.office.xlsx_analyze import office_xlsx_analyze from mineru.backend.office.docx_analyze import office_docx_analyze -from mineru.utils.pdfium_guard import rewrite_pdf_bytes_with_pdfium +from mineru.utils.pdfium_guard import ( + get_loadable_pdfium_page_indices, + rewrite_pdf_bytes_with_pdfium, +) os.environ["TORCH_CUDNN_V8_API_DISABLED"] = "1" if os.getenv("MINERU_LMDEPLOY_DEVICE", "") == "maca": @@ -190,12 +193,50 @@ def convert_pdf_bytes_to_bytes(pdf_bytes, start_page_id=0, end_page_id=None): ) if rebuilt_pdf_bytes: return rebuilt_pdf_bytes - logger.warning("PDFium rewrite returned empty bytes, using original PDF bytes.") + logger.warning( + "PDFium rewrite returned empty bytes, trying to skip broken pages." + ) except Exception as fallback_error: logger.warning( f"Error in converting PDF bytes with pdfium: {fallback_error}, " + "trying to skip broken pages." + ) + + try: + loadable_page_indices, broken_page_indices = get_loadable_pdfium_page_indices( + pdf_bytes, + start_page_id=start_page_id, + end_page_id=end_page_id, + ) + if broken_page_indices: + skipped_pages = [page_index + 1 for page_index in broken_page_indices] + logger.warning( + f"Skipped broken PDF pages during PDFium rewrite: {skipped_pages}" + ) + if not loadable_page_indices: + logger.warning( + "PDFium skip-broken-page rewrite found no loadable pages, " + "using original PDF bytes." + ) + return pdf_bytes + + rebuilt_pdf_bytes = rewrite_pdf_bytes_with_pdfium( + pdf_bytes, + start_page_id=start_page_id, + end_page_id=end_page_id, + page_indices=loadable_page_indices, + ) + if rebuilt_pdf_bytes: + return rebuilt_pdf_bytes + logger.warning( + "PDFium skip-broken-page rewrite returned empty bytes, " "using original PDF bytes." ) + except Exception as fallback_error: + logger.warning( + "Error in converting PDF bytes with skip-broken-page fallback: " + f"{fallback_error}, using original PDF bytes." + ) return pdf_bytes diff --git a/mineru/utils/pdfium_guard.py b/mineru/utils/pdfium_guard.py index 87dbb281..97918f29 100644 --- a/mineru/utils/pdfium_guard.py +++ b/mineru/utils/pdfium_guard.py @@ -62,6 +62,49 @@ def close_pdfium_objects_safely(*pdfium_objs, owner: str = "pdfium cleanup") -> logger.warning(f"Failed to close PDFium object during {owner}: {exc}") +def get_loadable_pdfium_page_indices( + src_pdf_bytes: bytes, + start_page_id: int = 0, + end_page_id: int | None = None, +) -> tuple[list[int], list[int]]: + """逐页探测 PDFium 可加载页面,返回可保留页和损坏页的 0-based 索引。""" + import pypdfium2 as pdfium + + loadable_page_indices = [] + broken_page_indices = [] + pdf_doc = None + + try: + with pdfium_guard(): + pdf_doc = pdfium.PdfDocument(src_pdf_bytes) + total_page_count = len(pdf_doc) + if total_page_count == 0: + return [], [] + + normalized_start_page_id = max(0, start_page_id) + normalized_end_page_id = get_end_page_id(end_page_id, total_page_count) + if normalized_start_page_id > normalized_end_page_id: + return [], [] + + for page_index in range( + normalized_start_page_id, + normalized_end_page_id + 1, + ): + page = None + try: + page = pdf_doc[page_index] + page.get_size() + loadable_page_indices.append(page_index) + except Exception: + broken_page_indices.append(page_index) + finally: + close_pdfium_child(page) + finally: + close_pdfium_document(pdf_doc) + + return loadable_page_indices, broken_page_indices + + def rewrite_pdf_bytes_with_pdfium( src_pdf_bytes: bytes, start_page_id: int = 0,