pdfsyntax

repository·main·Indexed 21 days ago

https://github.com/desgeeko/pdfsyntax

A Python library and CLI tool for inspecting and modifying the internal structure of PDF files. It allows users to disassemble PDF files, extract spatially aware text, rotate or remove pages, and concatenate documents. The library provides low-level access to the PDF object tree via trailer and catalog methods, supports incremental updates with rewind and commit functionality, and includes a disassembler for structural analysis.

Tokens
15.8K
Snippets
76
Records
91
Agent score
76%

What's inside pdfsyntax

  1. Use Doc object slicing and operators

    main

    A Doc object behaves like a virtual list of pages, allowing you to use standard Python slicing and concatenation operators to manipulate page sets.

    # Slicing to keep specific pages (equivalent to keep_pages)
    last_3_pages = doc[3:]
    
    # Concatenation (equivalent to concat)
    doc = doc1 + doc2
  2. Use pure functions or instance methods in pdfsyntax

    main

    Most pdfsyntax functions are pure and are exposed in two ways: as standalone functions or as instance methods of a Doc object. When using the function pattern, the doc object is passed as the first argument. When using the method pattern, the function is called directly on the doc instance.

    Note: Every time a function is applied to a Doc object, it returns a new object that is a shallow copy of the input.

    # Function pattern
    from pdfsyntax import readfile, metadata
    doc = readfile("samples/simple_text_string.pdf")
    m = metadata(doc)
    
    # Method pattern
    import pdfsyntax as pdf
    doc = pdf.readfile("samples/simple_text_string.pdf")
    m = doc.metadata()
  3. Analyze PDF structure with grep

    main

    Since the disassembler output is structured, you can use grep to filter for specific structural elements:

    • Ignore void spaces (separators) and focus on real objects: python3 -m pdfsyntax disasm FILE | grep -v void
    • List all xref entries of all tables or streams: python3 -m pdfsyntax disasm FILE | grep xref
    • Ignore detail lines (lines starting with -): python3 -m pdfsyntax disasm FILE | grep "^+"
    • Search for all mentions of a specific indirect object (e.g., 52,0): python3 -m pdfsyntax disasm FILE | grep 52,
    # Example: Filter out void entries to see only objects
    python3 -m pdfsyntax disasm samples/simple_text_string.pdf | grep -v void
  4. Manage PDF incremental updates with rewind and commit

    main

    PDFSyntax tracks incremental updates (revisions) made by appending new or updated objects to the end of the PDF.

    • Revisions: A revision greater than 0 indicates appended incremental updates. By default, a newly opened document is ready to write modifications in the next revision.
    • rewind(doc): Rolls the document back to the previous revision.
    • commit(doc): Closes the current revision and opens the next one.

    Example of navigating revisions:

    import pdfsyntax as pdf
    # Initial state might be revision 2
    doc = pdf.readfile("samples/add_text_annotation.pdf")
    
    # Roll back to revision 0
    doc = pdf.rewind(doc) # to revision 1
    doc = pdf.rewind(doc) # to revision 0
    
    # Apply modification and commit to a new revision
    doc = pdf.rotate(doc)
    doc = pdf.commit(doc)
  5. Run the pdfsyntax CLI

    main

    The pdfsyntax CLI allows you to inspect, disassemble, and extract content from PDF files. You can run it directly if installed via package manager, or as a Python module if installed from source.

    Standard usage: pdfsyntax COMMAND FILE

    Source installation usage: python3 -m pdfsyntax COMMAND FILE

    # Standard usage
    pdfsyntax overview document.pdf
    
    # If installed from source
    python3 -m pdfsyntax overview document.pdf
  6. How the page hierarchy is structured

    main

    PDF pages are organized in a tree structure. The catalog (Root) contains a /Pages node, which can be a /Pages type node (containing /Kids which are references to other nodes) or a /Page type node (representing an actual page).

    • catalog(doc): Returns the Root dictionary and its indirect reference.
    • flat_page_tree(doc, num=None, ...): Recursively traverses the page tree to return a list of (page_ref, inherited_attributes). This is useful for finding all page objects and their inherited properties like /MediaBox or /CropBox.
    • pages(doc, max_nb=None): A high-level helper that returns a list of page dictionaries, including inherited attributes from the parent nodes.
    # Get all page objects as dictionaries
    all_pages = pages(doc)
    
    # Get only the first 5 pages
    first_five = pages(doc, max_nb=5)
  7. How document revisions and incremental updates work

    main

    A Doc object tracks history through its index and data attributes.

    • index: A list of lists. Each inner list represents a revision of the object index. It tracks object numbers, generation numbers, and their positions.
    • data: Stores the actual byte streams (fdata) for each revision.
    • changes(doc, rev=-1): Returns a list of (iref, action) tuples describing what changed between revisions (e.g., 'a' for added, 'd' for deleted, 'u' for updated).
    • updates(doc): Returns the number of incremental updates applied to the document.

    When you modify an object using update_object and then call commit, the library doesn't rewrite the whole file; it creates a new revision that appends the changes, following the PDF incremental update standard.

  8. Detect and retarget indirect references

    main

    When manipulating PDF object trees, you can use these functions to manage indirect references (represented as Python complex numbers in this library, e.g., 0 + 123j for 123 0 R).

    Detect all references

    deep_ref_detect(obj, l=None) recursively traverses a dictionary or list and returns a set of all complex numbers representing indirect references found in the object tree.

    Retarget references

    deep_ref_retarget(obj, mapping) recursively traverses an object tree and replaces any complex reference found in the mapping dictionary with its new value.

    Replace X, Y, 'R' patterns

    replace_ref(tokens) is a utility that converts a specific list pattern [X, Y, 'R'] into a single complex(0, X) object. This is used during parsing to handle the PDF reference syntax.

  9. Load and read PDF documents

    main

    You can load PDF documents into a Doc object using several methods depending on your source data:

    • From a file path: Use readfile(filename) to open a file and initialize the document.
    • From a file-like object: Use load(file_obj) where file_obj is an open file handle.
    • From a bytes sequence: Use loads(bdata) to load a PDF directly from memory.

    Note that load accepts a mode parameter (defaulting to "SINGLE").

    from pdfsyntax.api import readfile, loads
    
    # Load from a file
    doc = readfile('example.pdf')
    
    # Load from bytes
    with open('example.pdf', 'rb') as f:
        bdata = f.read()
        doc = loads(bdata)
  10. Use the pdfsyntax CLI to inspect or transform PDF files

    main

    The pdfsyntax command-line tool provides several subcommands to disassemble, inspect, and transform PDF files.

    Available Commands

    CommandDescriptionArguments
    browseGenerates an HTML visualization to browse the internal structureFILE
    disasmDumps the file structure (disassembly)FILE
    overviewPrints general file information (structure and metadata)FILE
    fontsLists all fonts used in the PDFFILE
    textExtracts text content with spatial awarenessFILE
    compressPerforms lossless compression on the PDFFILE, -o FILE
    hexdumpPrints a canonical hex and ASCII file dumpFILE

    Usage Example

    To compress a PDF file:

    pdfsyntax compress input.pdf -o compressed.pdf

    To view the file structure via HTML:

    pdfsyntax browse input.pdf

    To extract text:

    pdfsyntax text input.pdf