PyPDFForm Documentation

repository·master·Indexed 22 days ago

https://github.com/chinapandaman/pypdfform

A Python library and CLI tool for filling out PDF forms. PyPDFForm allows mapping data from JSON or YAML to PDF fields, embedding document and field-level JavaScript, and creating various PDF annotations including links, text markup, and rubber stamps. It provides utilities for managing appearance streams, handling fully qualified widget names, and inspecting or modifying form field coordinates using the standard PDF coordinate system.

Tokens
27.3K
Snippets
67
Records
161
Agent score
78%

What's inside PyPDFForm

  1. How to write CLI tests for PyPDFForm

    master

    CLI tests follow a four-step pattern:

    1. Define the expected PDF file.
    2. Write input data (e.g., YAML) to tmp_path.
    3. Invoke the CLI command using typer.testing.CliRunner.
    4. Compare the generated PDF with the expected file.

    Key requirements:

    • Mark tests with @pytest.mark.cli_test.
    • Use CliRunner from typer.testing.
    • Import cli_app from PyPDFForm.cli.root.

    Example implementation:

    @pytest.mark.cli_test
    def test_fill(pdf_samples, tmp_path):
        expected_path = os.path.join(pdf_samples, "docs", "test_fill_text_check.pdf")
        input_path = os.path.join(tmp_path, "input.yaml")
        output_path = os.path.join(tmp_path, "output.pdf")
        fill_data = {
            "test": "test_1",
            "check": True,
            "test_2": "test_2",
            "check_2": False,
            "test_3": "test_3",
            "check_3": True,
        }
    
        with open(input_path, "w") as f:
            yaml.safe_dump(fill_data, f)
    
        result = runner.invoke(
            cli_app,
            [
                "fill",
                os.path.join(pdf_samples, "sample_template.pdf"),
                "-f",
                input_path,
                "-o",
                output_path,
            ],
        )
        assert result.exit_code == 0
    
        with open(expected_path, "rb") as f1, open(output_path, "rb") as f2:
            expected = f1.read()
            actual = f2.read()
    
            assert len(expected) == len(actual)
            assert expected == actual
  2. How to write library tests for PyPDFForm

    master

    Library tests typically follow a three-step pattern:

    1. Define the expected PDF file.
    2. Use PdfWrapper to generate a PDF from test inputs.
    3. Compare the generated PDF with the expected file.

    To make expected PDF regeneration easier, include these lines in your tests to attach the expected path and stream to the pytest request object:

    request.config.results["expected_path"] = expected_path
    request.config.results["stream"] = obj.read()

    Example implementation:

    def test_fill(pdf_samples, request):
        expected_path = os.path.join(pdf_samples, "sample_filled.pdf")
        with open(expected_path, "rb+") as f:
            obj = PdfWrapper(os.path.join(pdf_samples, "sample_template.pdf")).fill(
                {
                    "test": "test_1",
                    "check": True,
                    "test_2": "test_2",
                    "check_2": False,
                    "test_3": "test_3",
                    "check_3": True,
                },
            )
    
            request.config.results["expected_path"] = expected_path
            request.config.results["stream"] = obj.read()
    
            expected = f.read()
    
            assert len(obj.read()) == len(expected)
            assert obj.read() == expected
  3. How to render PDF form widgets with PdfWrapper

    master

    Starting with version 2.0.0, PyPDFForm renders full PDF form widgets when using PdfWrapper. Instead of only rendering the data value filled into the form, the library now renders the entire widget.

    If you need to revert to the previous behavior (rendering only the filled value), you must explicitly disable widget rendering in your configuration.

  4. Fill radio button groups

    master

    A radio button group is a collection of buttons sharing the same name. To select an option, provide the zero-based index of the desired option.

    # Selecting the first, second, and third options respectively
    filled = PdfWrapper("template.pdf").fill({
        "radio_1": 0,
        "radio_2": 1,
        "radio_3": 2,
    })
  5. Handle PDF appearance streams

    master

    Appearance streams define how form field content (like text) is rendered by a PDF viewer. You can manage this in two ways:

    1. Let the Viewer Generate Appearances: Set need_appearances=True (Library) or use --need-appearances (CLI). This is recommended for high-quality rendering in software like Adobe Acrobat.
    2. Let PyPDFForm Generate Appearances: Set generate_appearance_streams=True (Library) or use --generate-appearance-streams (CLI). This is a fallback for viewers that cannot generate their own streams.

    Limitations of PyPDFForm's internal generation (via qpdf):

    • Limited to ASCII text only.
    • Supports single-line text fields only (no multi-line).
    • Does not preserve text alignment (left, center, right).
  6. Embed PDF JavaScript using PyPDFForm

    master

    PyPDFForm allows you to embed JavaScript into PDF documents and specific form fields to trigger actions during user interactions (e.g., hovering, clicking, or opening the file).

    Security Warning: Do NOT trust user input; always sanitize it. Although PDF JavaScript runs in a sandbox, arbitrary execution can lead to remote code execution vulnerabilities.

  7. Fill dropdown fields

    master

    Dropdown fields can be populated using either the zero-based option index or the option text (string).

    Note: If you provide a string value that does not exist in the current dropdown options, PyPDFForm will add that string as the last option in the dropdown and automatically select it.

  8. Quickstart with PyPDFForm as a Python library

    master

    You can use PyPDFForm as a Python library to create, style, fill, and save PDF forms. The workflow typically involves creating a PdfWrapper with a BlankPage, drawing raw elements (like labels) using RawElements, creating form fields using Fields, inspecting the schema, styling widgets via the widgets dictionary, filling the form with data, and finally writing to a file.

    from pprint import pprint
    from PyPDFForm import BlankPage, Fields, PdfWrapper, RawElements
    
    # Create a blank PDF
    pdf = PdfWrapper(BlankPage())
    
    # Draw labeling texts
    pdf.draw(
        [
            RawElements.RawText("My Textfield:", 1, 100, 600),
            RawElements.RawText("My Checkbox:", 1, 100, 550),
        ]
    )
    
    # Create text and checkbox fields
    pdf.bulk_create_fields(
        [
            Fields.TextField("my_textfield", 1, 180, 596, height=16),
            Fields.CheckBoxField("my_checkbox", 1, 180, 546, size=16),
        ]
    )
    
    # Inspect the fields via JSON schema
    pprint(pdf.schema)
    
    # Change the field styles
    pdf.widgets["my_textfield"].font_color = (1, 0, 0)
    pdf.widgets["my_textfield"].alignment = 1
    
    # Fill the newly created form
    pdf.fill(
        {
            "my_textfield": "this is a text field",
            "my_checkbox": True,
        }
    )
    
    # Save the new form
    pdf.write("output.pdf")
  9. Install the PyPDFForm CLI

    master

    The PyPDFForm Command Line Interface (CLI) is available as an optional extra. It is recommended to use pipx to install the CLI to ensure it is available on your PATH in an isolated environment.

    To install the CLI, use the [cli] extra.

    pipx install "PyPDFForm[cli]"
  10. Change PDF title

    master

    You can manage the PDF document title using either the Python library or the CLI.

    Python Library

    Set the title during PdfWrapper instantiation or by assigning a value to the .title property. You can also retrieve the current title by accessing the property.

    from PyPDFForm import PdfWrapper
    
    # Instantiate with title
    pdf = PdfWrapper("sample_template.pdf", title="My PDF")
    
    # Set title via attribute
    pdf.title = "My PDF"
    
    # Get title
    print(pdf.title)

    CLI

    Use the update title command to set a title, or inspect title to view it.

    # Set title
    pypdfform update title sample_template.pdf -t "My PDF" -o output.pdf
    
    # Get title
    pypdfform inspect title output.pdf
    pdf.title = "My PDF"
  11. Host PyPDFForm documentation locally

    master

    PyPDFForm uses MkDocs to build its documentation. You can host the documentation locally using one of two methods:

    Using a Virtual Environment

    If you have MkDocs installed in your local virtual environment, run the following command to start a local server:

    mkdocs serve -a 0.0.0.0:8080

    Using the Development Container

    If you are working within the project's development container, you can run:

    docs

    Once started, the documentation will be available at http://localhost:8080/.