**Here’s a practical Python implementation for working with QuadPoints in PDFs.** QuadPoints enable precise, non-rectangular text markup annotations (highlights, underlines, etc.) that follow text exactly, even across lines or with rotation. ### Recommended Library: PyMuPDF (fitz) PyMuPDF is the easiest and most powerful for QuadPoints. It handles text searching → quads → annotations seamlessly. ```bash pip install pymupdf ``` #### 1. Basic: Search text and add Highlight with QuadPoints (recommended) ```python import fitz # pymupdf doc =
fitz.open("input.pdf") # or
fitz.open() for new doc page = doc[0] # first page (0-based) # Search for text → returns list of Quad objects (perfect for QuadPoints) text_to_highlight = "your search phrase" quads =
page.search_for(text_to_highlight, quads=True) if quads: for quad in quads: # one per line/fragment annot = page.add_highlight_annot(quad) # Auto-sets QuadPoints internally annot.set_colors(stroke=(1, 0, 0), fill=(1, 0.8, 0.8)) # red-ish highlight annot.set_opacity(0.5) annot.update() # Apply changes
doc.save("highlighted.pdf", garbage=4, deflate=True, clean=True) doc.close() ``` This automatically generates correct **QuadPoints** arrays (8 numbers per quad: x1 y1 x2 y2 x3 y3 x4 y4). #### 2. Manual QuadPoints Control (for custom shapes or imported data) ```python import fitz doc =
fitz.open("input.pdf") page = doc[0] # Example QuadPoints (one quad): bottom-left → bottom-right → top-right → top-left # Coordinates in PDF user space (bottom-left origin, points) custom_quad = fitz.Quad( fitz.Point(100, 600), # lower-left fitz.Point(300, 600), # lower-right fitz.Point(300, 650), # upper-right fitz.Point(100, 650) # upper-left ) annot = page.add_highlight_annot(custom_quad) annot.set_colors(stroke=(0, 1, 0)) # green annot.update()
doc.save("custom_quad.pdf") doc.close() ``` #### 3. Extract QuadPoints from Existing Annotations ```python doc =
fitz.open("annotated.pdf") page = doc[0] for annot in page.annots(): if annot.type[1] in ("Highlight", "Underline", "StrikeOut", "Squiggly"): print("Type:", annot.type) print("QuadPoints (vertices):", annot.vertices) # list of Point objects print("Bounding Rect:", annot.rect) doc.close() ``` **Note**: `annot.vertices` gives the raw points used for QuadPoints (groups of 4 points per quad). ### Alternative: pypdf (pure Python, lower-level) pypdf is lighter but requires manual QuadPoints arrays. ```python from pypdf import PdfReader, PdfWriter from pypdf.annotations import Highlight from pypdf.generic import ArrayObject, FloatObject reader = PdfReader("input.pdf") writer = PdfWriter() writer.add_page(reader.pages[0]) # Example QuadPoints: [x1,y1, x2,y2, x3,y3, x4,y4] — typically top-left order in practice quad_points = [100, 650, 300, 650, 100, 600, 300, 600] # adjust to your text rect = (100, 600, 300, 650) # bounding box [llx, lly, urx, ury] annotation = Highlight( rect=rect, quad_points=ArrayObject([FloatObject(x) for x in quad_points]), highlight_color="ff0000" # red ) writer.add_annotation(page_number=0, annotation=annotation) with open("pypdf_highlight.pdf", "wb") as f: writer.write(f) ``` ### Tips for "Quad PDF" Workflows - **Multi-line highlights** → `search_for(..., quads=True)` returns multiple `Quad` objects. - **Coordinate system**: Bottom-left (0,0). Page height (e.g., 792 for Letter) flips Y for screen coords: `screen_y = height - pdf_y`. - **Precision**: Quads support rotated/skewed text perfectly. - **Refresh annotations**: Load old PDF → extract vertices → re-apply to updated base PDF. - **Debugging**: Use tools like `qpdf --qdf` or PDF editors to inspect raw `/QuadPoints` arrays. Would you like a full script for: - Generating a fresh 4-quadrant PDF layout with sample text highlights? - Batch processing multiple annotations? - Converting screen coords ↔ PDF coords? - Or integrating with your specific "Quad PDF" structure? Share more details (page size, quadrant headings, sample text, or a base PDF description), and I’ll tailor the code!