๐ 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.
Hello World โ Tagged PDF
Create a tagged, accessible PDF with headings and paragraph text in under 30 lines:
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:
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:
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:
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:
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 Serif | BundledFonts.SERIF_REGULAR / .SERIF_BOLD / .SERIF_ITALIC / .SERIF_BOLD_ITALIC | Times New Roman |
| Sora | BundledFonts.SANS_REGULAR / .SANS_BOLD / .SANS_ITALIC / .SANS_BOLD_ITALIC | Helvetica / Arial |
| CMU Typewriter | BundledFonts.MONO_REGULAR / .MONO_BOLD / .MONO_ITALIC / .MONO_BOLD_ITALIC | Courier |
API Quick Reference
| Class | Key Methods / Properties |
|---|---|
Document | add_page(), enable_tagging(), save(), save_to_bytes(), check_accessibility(), set_encryption() |
Page | add_text(), add_image(), draw_line(), draw_rect(), begin_artifact(), end_artifact() |
StructureElement | add_child(), .alt_text, .actual_text, .language, .table_scope |
TextOptions | font, font_size, color_r/g/b |
DrawOptions | stroke_r/g/b, fill_r/g/b, line_width |
Image | Image(path), Image.from_bytes(data) |
Encryption | algorithm, 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:
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")
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");