๐Ÿ C/Python Renderer โ€” Accessible PDFs from Python

The obviousPDF Python package wraps the ObviousPDF.c shared library via ctypes. Build tagged, PDF/UA-1 compliant documents using a Pythonic API โ€” no .NET runtime required.

๐Ÿ“ฆ Installation

Install from PyPI โ€” pre-built wheels include the native shared library for your platform:

pip install obviousPDF

Supported: Windows x64, Linux x64, macOS arm64/x64 ยท Python 3.9โ€“3.13

๐Ÿ—๏ธ Architecture

Layer Technology Description
Python APIctypesPythonic classes (Document, Page, StructureElement, etc.)
Bindings_bindings.pyctypes prototypes for 40+ C functions
Native LibraryC99ObviousPDF.c โ€” handle-based API, ABI-stable

No compiled Python extensions โ€” just ctypes loading the shared library. No build tools required at runtime.

Hello World โ€” Tagged PDF

Create a tagged, accessible PDF with headings and paragraph text in under 30 lines:

Python โ€” Hello World (Accessible)
from obviousPDF import (
    Document, PageSize, StructureType,
    TextOptions, BundledFonts,
)

with Document() as doc:
    # Metadata and accessibility
    doc.info.title = "Hello World"
    doc.info.language = "en-US"
    doc.info.display_doc_title = True
    doc.enable_tagging()

    page = doc.add_page(PageSize.LETTER)

    # Build structure tree
    root = doc.structure_root
    sect = root.add_child(StructureType.SECT)
    h1 = sect.add_child(StructureType.H1)
    para = sect.add_child(StructureType.P)

    # Render accessible content
    heading_opts = TextOptions(
        font=BundledFonts.SERIF_BOLD, font_size=24
    )
    page.add_text(
        "Hello, World!", x=72, y=720,
        options=heading_opts, element=h1,
    )

    body_opts = TextOptions(
        font=BundledFonts.SERIF_REGULAR, font_size=12
    )
    page.add_text(
        "This is an accessible PDF generated from Python.",
        x=72, y=690, options=body_opts, element=para,
    )

    doc.save("hello.pdf")

Multi-Page Document with Structure Tree

Build a multi-page document with headings, paragraphs, and artifact decorations:

Python โ€” Multi-Page Accessible Document
from obviousPDF import (
    Document, PageSize, StructureType,
    TextOptions, DrawOptions, BundledFonts, ArtifactType,
)

with Document() as doc:
    doc.info.title = "Quarterly Report"
    doc.info.author = "Finance Team"
    doc.info.language = "en-US"
    doc.info.display_doc_title = True
    doc.enable_tagging()

    root = doc.structure_root
    sect = root.add_child(StructureType.SECT)

    title_opts = TextOptions(font=BundledFonts.SANS_BOLD, font_size=20)
    heading_opts = TextOptions(font=BundledFonts.SANS_BOLD, font_size=14)
    body_opts = TextOptions(font=BundledFonts.SERIF_REGULAR, font_size=11)
    line_opts = DrawOptions(stroke_r=200, stroke_g=200, stroke_b=200)

    # --- Page 1: Title page ---
    page1 = doc.add_page(PageSize.LETTER)
    h1 = sect.add_child(StructureType.H1)
    page1.add_text(
        "Q2 2026 โ€” Quarterly Report",
        x=72, y=700, options=title_opts, element=h1,
    )

    intro = sect.add_child(StructureType.P)
    page1.add_text(
        "This report summarizes financial performance for Q2 2026.",
        x=72, y=670, options=body_opts, element=intro,
    )

    # Decorative line โ€” marked as artifact
    page1.begin_artifact(ArtifactType.LAYOUT)
    page1.draw_line(x1=72, y1=660, x2=540, y2=660, options=line_opts)
    page1.end_artifact()

    # --- Page 2: Details ---
    page2 = doc.add_page(PageSize.LETTER)
    h2 = sect.add_child(StructureType.H2)
    page2.add_text(
        "Revenue Breakdown",
        x=72, y=720, options=heading_opts, element=h2,
    )

    detail = sect.add_child(StructureType.P)
    page2.add_text(
        "Total revenue increased 12% year-over-year...",
        x=72, y=695, options=body_opts, element=detail,
    )

    doc.save("quarterly-report.pdf")

Built-in Accessibility Checker

Run the 17-check accessibility validator (matching Matterhorn Protocol 1.1) directly from Python:

Python โ€” Accessibility Checker
from obviousPDF import Document, PageSize, TextOptions, BundledFonts

with Document() as doc:
    doc.info.title = "Accessibility Check Demo"
    doc.info.language = "en-US"
    doc.enable_tagging()

    page = doc.add_page(PageSize.LETTER)
    opts = TextOptions(font=BundledFonts.SERIF_REGULAR, font_size=12)
    page.add_text("Sample content", x=72, y=720, options=opts)

    # Run the checker before saving
    report = doc.check_accessibility()

    if report.is_fully_compliant:
        print("โœ… Fully compliant โ€” 0 issues found")
    else:
        print(f"Found {len(report.non_compliant)} issue(s):\n")
        for item in report.non_compliant:
            print(f"  โŒ {item.feature}: {item.description}")
            print(f"     Reference: {item.reference}")
            print(f"     Fix: {item.remediation}\n")

    # Compliant items
    for item in report.compliant:
        print(f"  โœ… {item.feature}: {item.description}")

    doc.save("checked-output.pdf")

Encryption โ€” AES-128 / AES-256

Encrypt PDFs with AES-128 or AES-256 and set permission flags:

Python โ€” Encrypted PDF
from obviousPDF import (
    Document, PageSize, TextOptions, BundledFonts,
    Encryption, EncryptionAlgorithm,
)

with Document() as doc:
    doc.info.title = "Confidential Report"
    doc.info.language = "en-US"
    doc.enable_tagging()

    page = doc.add_page(PageSize.LETTER)
    opts = TextOptions(font=BundledFonts.SERIF_REGULAR, font_size=12)
    page.add_text("This document is encrypted.", x=72, y=720, options=opts)

    # AES-256 encryption with user and owner passwords
    encryption = Encryption(
        algorithm=EncryptionAlgorithm.AES_256,
        user_password="reader-pass",
        owner_password="admin-pass",
        allow_printing=True,
        allow_copying=False,
    )
    doc.set_encryption(encryption)

    doc.save("confidential.pdf")

In-Memory PDF Generation

Generate PDFs as bytes for web APIs, email attachments, or streaming โ€” no disk I/O needed:

Python โ€” Save to Bytes
from obviousPDF import Document, PageSize, TextOptions, BundledFonts

with Document() as doc:
    doc.info.title = "In-Memory PDF"
    doc.info.language = "en-US"
    doc.enable_tagging()

    page = doc.add_page(PageSize.LETTER)
    opts = TextOptions(font=BundledFonts.SERIF_REGULAR, font_size=12)
    page.add_text("Generated entirely in memory.", x=72, y=720, options=opts)

    # Get raw PDF bytes
    pdf_bytes = doc.save_to_bytes()

assert pdf_bytes[:5] == b"%PDF-"
print(f"Generated {len(pdf_bytes):,} bytes")

Bundled Font Families

Three font families are bundled with the library โ€” all SIL Open Font License, fully embedded with Unicode CMap for accessibility compliance:

Family Python Constant Substitute For
CMU SerifBundledFonts.SERIF_REGULAR / .SERIF_BOLD / .SERIF_ITALIC / .SERIF_BOLD_ITALICTimes New Roman
SoraBundledFonts.SANS_REGULAR / .SANS_BOLD / .SANS_ITALIC / .SANS_BOLD_ITALICHelvetica / Arial
CMU TypewriterBundledFonts.MONO_REGULAR / .MONO_BOLD / .MONO_ITALIC / .MONO_BOLD_ITALICCourier

API Quick Reference

Class Key Methods / Properties
Documentadd_page(), enable_tagging(), save(), save_to_bytes(), check_accessibility(), set_encryption()
Pageadd_text(), add_image(), draw_line(), draw_rect(), begin_artifact(), end_artifact()
StructureElementadd_child(), .alt_text, .actual_text, .language, .table_scope
TextOptionsfont, font_size, color_r/g/b
DrawOptionsstroke_r/g/b, fill_r/g/b, line_width
ImageImage(path), Image.from_bytes(data)
Encryptionalgorithm, user_password, owner_password, allow_printing, allow_copying
AccessibilityReport.is_fully_compliant, .compliant, .non_compliant, .results

.NET vs Python โ€” Same Library, Different Languages

The Python package provides the same core features as the .NET ObviousPDF NuGet package. Compare equivalent code:

Python
from obviousPDF import Document, PageSize

with Document() as doc:
    doc.info.title = "My Document"
    doc.info.language = "en-US"
    doc.enable_tagging()
    page = doc.add_page(PageSize.LETTER)
    doc.save("output.pdf")
C# (.NET)
using ObviousPDF;

var doc = new PdfDocument();
doc.Info.Title = "My Document";
doc.Language = "en-US";
doc.EnableTagging();
doc.Pages.Add(PdfPageSize.Letter);
doc.Save("output.pdf");

โ™ฟ Accessibility Notes

  • PDF/UA-1 Compliant: Documents created with enable_tagging() and proper structure elements pass PAC and veraPDF validation.
  • Bundled fonts are fully embedded with ToUnicode CMap โ€” no accessibility warnings for font handling (ISO 14289-1 ยง7.21).
  • Artifacts โ€” use begin_artifact() / end_artifact() for decorative content (ISO 14289-1 ยง7.1).
  • Alt text โ€” set element.alt_text on Figure elements (ISO 14289-1 ยง7.3).
  • Document language โ€” always set doc.info.language to a BCP-47 tag (ISO 14289-1 ยง7.2).
  • 17-check validator โ€” doc.check_accessibility() covers Matterhorn Protocol 1.1 checkpoints.

โš ๏ธ Disclaimer

The automated accessibility checker assists in assessment but is not a comprehensive audit. To confirm WCAG or PDF/UA conformance, human assessment by an accessibility specialist is recommended.

๐Ÿ“‹ Feature Parity (v1.0.0)

The Python package covers the core PDF generation and accessibility features. Some advanced .NET features are planned for v2.0:

Feature .NET Python
Tagged PDF / Structure Treeโœ…โœ…
Bundled + Embedded Fontsโœ…โœ…
Accessibility Checkerโœ…โœ…
AES-128/256 Encryptionโœ…โœ…
Images (JPEG, PNG)โœ…โœ…
Bookmarks / Outlinesโœ…v2.0
Form Fields (AcroForm)โœ…v2.0
JSON/XML/CSV Pipelinesโœ…โ€”