PDF parsing

When handing over annotated documents, don’t just submit a seemingly clean PDF.

directly addresses a high-frequency fault: when you use page.gettext() When extracting PDF content for translation or archiving, the default output typically do

阅读文本 / Reading text
This paper uses synthetic samples to demonstrate the processing method. The code has been verified using examples in an isolated environment; this does not guarantee effectiveness for all real files or products. Before processing important files, please retain the original copies and verify them against the boundaries and acceptance criteria outlined in this document. directly addresses a high-frequency fault: when you use page.get_text() When extracting PDF content for translation or archiving, the default output typically does not include annotations. Even more concerning, if certain “full-text extraction” modes are used directly, annotation content may be inserted haphazardly into the middle of the main text, causing semantic fragmentation; alternatively, you might believe that all content has been extracted, only to overlook critical revision comments or even sensitive notes left by reviewers in the margins. During document handover, the main text and annotations must be treated as two separate data streams, with explicit security reviews conducted on the annotation content. Why are annotations an independent, hidden data layer separate from the main text? In the PDF specification, the Content Stream and the Annots Array are structurally separate. The main text consists of text-display instructions, while annotations are independent objects attached to the page, each with its own coordinates, type, and content attributes. PyMuPDF’s Page.annots() method returns a generator that iterates over this independent list of annotations, rather than the page’s text stream. This means you cannot assume that “all visible text can be extracted in the same way.” Markup annotations such as Highlight and Underline do not contain text themselves; they merely record the corresponding original-text regions (Quads/Rects), and the actual text must be retrieved by reverse-lookup of coordinates in the main body. In contrast, Text/Note and FreeText annotations carry their own content strings. If types are not distinguished and data is read directly… annot.info["content"] , you will lose all the original text context referenced by tag-based annotations, or an exception will be thrown when processing empty content. synthesis example: hierarchical extraction and sensitive information interception To demonstrate the correct processing logic, we construct a synthetic scenario containing three typical annotations. Please note that the following inputs are all synthetic samples generated within the code and do not originate from real customer files; they are intended solely for instructional demonstration. is expected to produce the correct output should contain three structured fields: 1. main text fragment : Contains only the page's original text, with no annotations whatsoever. 2. annotation list : Each record includes the type, location, author, time, and content (if any). 3. security tag : If the annotated content matches a predefined sensitive rule (such as an ID number or an internal code), that entry is marked as redacted=True , content replaced with a placeholder. Error/Boundary Input and Processing : * empty content annotation : Some inks or graphic annotations created by readers do not have text attributes. Direct access to .info["content"] may return an empty string or None. The code must perform defensive checks and should not assume that all annotations have readable text. * rotates the page coordinates : If the page has been rotated by 90 degrees, the annotation’s rectangular coordinates remain relative to the unrotated page. If these coordinates are used directly to crop or position the main text, the positioning will be completely incorrect. It is necessary to first apply page.rotation_matrix or page.derotation_matrix performs a coordinate transformation. * orphan object : If the document is closed or the page is reloaded during traversal, the current annot object will immediately become invalid. All read operations must be completed within a single page lifecycle. Python Implementation: Secure Annotation Traversal and Validation The following example uses PyMuPDF to construct a test PDF in memory, demonstrating hierarchical extraction and basic de-identification logic. The dependency library is PyMuPDF (i.e., pymupdf package), please run it in a locked dependency environment, with a version of ≥1.23.0 to support full access to annot attributes. This code does not perform any network operations or file writes; it is intended solely for logical demonstration. import fitz # PyMuPDF import re # 合成示例:仅在内存中构造,无需外部文件 def create_sample_pdf(): doc = fitz.open() page = doc.new_page() # 插入正文 page.insert_text((72, 72), "Confidential Project Alpha Report", fontsize=12) # 添加便签批注(含敏感信息模拟) page.add_text_annot((200, 72), "Reviewer: SSN 123-45-6789 approved.") # 添加高亮批注(无自身文本,关联正文) highlight = page.add_highlight_annot(fitz.Rect(72, 60, 250, 80)) # 必须调用update()使info变更生效 highlight.set_info(title="Editor", content="Check this term") highlight.update() return doc def extract_with_safety(page, sensitive_pattern=r"\d{3}-\d{2}-\d{4}"): body_text = page.get_text("text") annotations = [] # 显式遍历批注生成器 for annot in page.annots(): info = annot.info # 防御性获取内容,处理None或空字符串 content = info.get("content", "") or "" # 边界处理:敏感词检测与脱敏 is_sensitive = bool(re.search(sensitive_pattern, content)) safe_content = "[REDACTED]" if is_sensitive else content annotations.append({ "type": annot.type[1], "author": info.get("title", "Unknown"), "content": safe_content, "is_redacted": is_sensitive, # 注意:此为未旋转坐标系,若页面有旋转需配合rotation_matrix使用 "rect": list(annot.rect) }) return body_text, annotations # 执行校验 doc = create_sample_pdf() page = doc[0] text, annots = extract_with_safety(page) assert "SSN 123-45-6789" not in str(annots), "敏感信息未被拦截" assert any(a["type"] == "Highlight" for a in annots), "标记类批注丢失" assert len(text.strip()) > 0, "正文提取失败" print(f"Extracted {len(annots)} annotations safely.") This code demonstrates the core of defensive programming: do not trust the purity of annotated content, and do not assume the completeness of annotation types. assert statements are used to verify logical expectations, but please note that this is only a code demonstration and does not guarantee end-to-end performance for all PDF variants. Acceptance Criteria and Manual Review Boundary automated extraction cannot cover all edge cases. The table below lists the acceptance points that should be monitored during actual handover, distinguishing between scenarios that can be handled by the code and those requiring manual intervention. Observation of the phenomenon code-level processing of Manual review/decision-making The annotation content is empty but an icon is present. record type is “Ink” or “Stamp,” with the content left blank Confirm whether it is a signature or an approval seal, and decide whether to retain the image snapshot. annotation position exceeds CropBox coordinates remain valid, but rendering is invisible determines whether it is a legacy, discarded annotation; such annotations can typically be ignored Rich-text annotation containing HTML tags extracts the original string, preserving the tag Before translation, tags must be cleaned or converted to plain text to prevent the translated text from containing code. The annotator’s name is garbled is extracted as is, without any encoding-based guessing verifies the source file’s metadata to confirm whether it is a name encoded in a specific format nested reply thread PyMuPDF returns a flat list must reconstruct the dialogue tree based on ParentID; otherwise, the context will be lost Scope of Non-Applicability : This scheme is not applicable to encrypted PDFs without a password, PDFs with corrupted structures, or annotation types that use non-standard proprietary extensions. In such cases, the script may silently skip these files or throw an underlying C++ exception; the source file must then be opened manually using specialized tools for verification. References and Next Steps PyMuPDF Page.annots() Official Documentation Next step : Select a historical project file containing mixed annotation types, run the aforementioned validation script, and calculate the proportion of entries requiring manual review. If this proportion exceeds expectations, a preprocessing step should be added to the handover process, rather than relying on downstream tools to automatically handle everything. uses this checklist for document translation first thoroughly verifies the page order, source location, and textual integrity, then submits the same de-identified sample to the translation process. After translation, it is still necessary to check whether the titles, body text, and figure captions correspond correctly; one should not rely solely on the fluency of the text. OmniDoc (Product Entry) provides an entry point for translating multi-format documents, and the official website highlights its OCR, terminology database, and bilingual alignment features. For teams that wish to start processing directly from files, this is a path worth evaluating with real-world samples. Please use de-identified files that you are authorized to upload, and verify the results against the checklist provided in this document; we do not claim that OmniDoc utilizes the open-source libraries or internal algorithms referenced herein, nor do we substitute examples for product performance testing. - 1 -
Updated 2026-10-08