Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python Guide

This guide covers using printwell with Python.

Installation

pip install printwell

Or with uv:

uv pip install printwell

Basic Usage

from printwell import Converter

converter = Converter()
result = converter.html_to_pdf("<h1>Hello, World!</h1>")
result.write_to_file("output.pdf")

Converting Documents

HTML String to PDF

from printwell import Converter

converter = Converter()

html = """
<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial; padding: 40px; }
        h1 { color: #333; }
    </style>
</head>
<body>
    <h1>My Document</h1>
    <p>This is a PDF generated from HTML.</p>
</body>
</html>
"""

result = converter.html_to_pdf(html)

# Get as bytes
pdf_bytes = result.data()

# Write to file
result.write_to_file("output.pdf")

# Get page count
print(f"Pages: {result.page_count}")

URL to PDF

result = converter.url_to_pdf("https://example.com")
result.write_to_file("example.pdf")

File to PDF

with open("input.html") as f:
    html = f.read()

result = converter.html_to_pdf(html)
result.write_to_file("output.pdf")

Configuration Options

PDF Options

from printwell import (
    Converter, PdfOptions, PageSize, Orientation,
    Margins, PdfMetadata
)

converter = Converter()

options = PdfOptions(
    # Page size
    page_size=PageSize.A4,

    # Or custom size in mm
    page_width_mm=210.0,
    page_height_mm=297.0,

    # Orientation
    orientation=Orientation.Portrait,

    # Margins in mm
    margins=Margins(top=20, right=15, bottom=20, left=15),
    # Or uniform margins
    margins=Margins.uniform(10),

    # Scale factor (0.1 to 2.0)
    scale=1.0,

    # Print background graphics
    print_background=True,

    # Page ranges
    page_ranges="1-5,8,10-12",

    # Headers and footers
    header_template='<div style="font-size:10px">Header</div>',
    footer_template='<div style="font-size:10px">Page <span class="pageNumber"></span></div>',

    # Metadata
    metadata=PdfMetadata(
        title="My Document",
        author="Author Name",
        subject="Document Subject",
        keywords="pdf, python"
    )
)

result = converter.html_to_pdf(html, pdf_options=options)

Render Options

from printwell import RenderOptions, ResourceOptions, Viewport

options = RenderOptions(
    # Base URL for relative resources
    base_url="https://example.com/",

    # Viewport
    viewport=Viewport(
        width=1920,
        height=1080,
        device_scale_factor=2.0
    ),

    # Resource loading
    resources=ResourceOptions(
        allow_remote=True,
        timeout_ms=30000,
        blocked_domains=["ads.example.com"],
        user_agent="Custom User Agent"
    ),

    # Custom CSS
    user_stylesheets=[
        'body { font-family: "Custom Font"; }'
    ]
)

result = converter.html_to_pdf(html, render_options=options)

Batch Processing

Converter Pool

For high-throughput scenarios:

from printwell import ConverterPool

# Create pool with 4 concurrent converters
pool = ConverterPool(4)

# Convert multiple documents
html_docs = [
    "<h1>Document 1</h1>",
    "<h1>Document 2</h1>",
    "<h1>Document 3</h1>"
]

results = pool.convert_batch(html_docs)

for i, result in enumerate(results):
    result.write_to_file(f"output_{i}.pdf")

Parallel Conversion with Threading

from concurrent.futures import ThreadPoolExecutor
from printwell import Converter

converter = Converter()
urls = [
    "https://example.com/page1",
    "https://example.com/page2",
    "https://example.com/page3"
]

def convert_url(url):
    return converter.url_to_pdf(url)

with ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(convert_url, urls))

PDF Post-Processing

Watermarks

from printwell import (
    add_watermark, add_watermarks, Watermark,
    WatermarkPosition, WatermarkColor
)

with open("input.pdf", "rb") as f:
    pdf_data = f.read()

# Text watermark
watermark = Watermark.text_watermark(
    text="CONFIDENTIAL",
    position=WatermarkPosition.Center,
    rotation=45,
    opacity=0.3,
    font_size=72,
    color=WatermarkColor(r=128, g=128, b=128)
)

result = add_watermark(pdf_data, watermark)

with open("watermarked.pdf", "wb") as f:
    f.write(result)

# Multiple watermarks
result = add_watermarks(pdf_data, [
    Watermark(text="DRAFT", opacity=0.2),
    Watermark(text="Page header", position=WatermarkPosition.TopCenter, font_size=12)
])

Bookmarks

from printwell import add_bookmarks, extract_bookmarks, Bookmark

with open("input.pdf", "rb") as f:
    pdf_data = f.read()

# Add bookmarks
bookmarks = [
    Bookmark("Chapter 1", page=1),
    Bookmark("Chapter 2", page=5),
    Bookmark("Chapter 3", page=12),
]

result = add_bookmarks(pdf_data, bookmarks)

# Extract existing bookmarks
existing = extract_bookmarks(pdf_data)
for bm in existing:
    print(f"{bm.title} -> Page {bm.page}")

Annotations

from printwell import (
    add_annotations, list_annotations, remove_annotations,
    Annotation, AnnotationType, AnnotationRect, AnnotationColor
)

# Add highlight annotation
result = add_annotations(pdf_data, [
    Annotation(
        annotation_type=AnnotationType.Highlight,
        page=1,
        rect=AnnotationRect(x=100, y=700, width=200, height=20),
        color=AnnotationColor.yellow(),
        opacity=0.5
    ),
    Annotation.sticky_note(
        page=1,
        x=50,
        y=600,
        contents="Review this section",
        author="Reviewer"
    )
])

# List annotations
annotations = list_annotations(pdf_data)
for ann in annotations:
    print(f"Page {ann.page}: {ann.annotation_type}")

# Remove annotations
cleaned = remove_annotations(pdf_data, annotation_types=[AnnotationType.Highlight])

Encryption

from printwell import (
    encrypt_pdf, decrypt_pdf, EncryptionOptions,
    Permissions, EncryptionAlgorithm
)

# Encrypt PDF
options = EncryptionOptions(
    owner_password="admin123",
    user_password="user123",
    permissions=Permissions(
        print=True,
        copy=False,
        modify=False,
        annotate=True
    ),
    algorithm=EncryptionAlgorithm.Aes256
)

encrypted = encrypt_pdf(pdf_data, options)

with open("encrypted.pdf", "wb") as f:
    f.write(encrypted)

# Decrypt PDF
decrypted = decrypt_pdf(encrypted, "admin123")

Digital Signatures

from printwell import (
    sign_pdf, sign_pdf_visible, verify_signatures,
    SigningOptions, SignatureAppearance, SignatureLevel
)

with open("document.pdf", "rb") as f:
    pdf_data = f.read()

with open("certificate.p12", "rb") as f:
    cert_data = f.read()

# Invisible signature
options = SigningOptions(
    reason="Approved",
    location="New York",
    signature_level=SignatureLevel.PadesB
)

signed = sign_pdf(pdf_data, cert_data, "certificate_password", options)

# Visible signature
appearance = SignatureAppearance(
    page=1,
    x=400,
    y=50,
    width=200,
    height=75,
    show_name=True,
    show_date=True
)

signed_visible = sign_pdf_visible(
    pdf_data, cert_data, "password", options, appearance
)

# Verify signatures
verifications = verify_signatures(signed_data)
for v in verifications:
    print(f"Signer: {v.signer_name}, Valid: {v.is_valid}")

Form Fields

from printwell import (
    add_form_fields, validate_form_fields,
    TextField, FormCheckbox, FormDropdown, FormRect,
    ValidationRule, FormElementInput
)

# Add form fields
rect = FormRect(x=100, y=700, width=200, height=20)

result = add_form_fields(
    pdf_data,
    text_fields=[
        TextField(
            name="full_name",
            page=1,
            rect=rect,
            required=True
        )
    ],
    checkboxes=[
        FormCheckbox(
            name="agree_terms",
            page=1,
            rect=FormRect(x=100, y=650, width=15, height=15)
        )
    ],
    dropdowns=[
        FormDropdown(
            name="country",
            page=1,
            rect=FormRect(x=100, y=600, width=150, height=20),
            options=["USA", "Canada", "UK", "Other"]
        )
    ]
)

# Validate form fields
elements = [
    FormElementInput(name="full_name", default_value="John Doe"),
    FormElementInput(name="email", default_value="invalid-email")
]

rules = [
    ValidationRule(field_name="full_name", required=True, min_length=2),
    ValidationRule(field_name="email", required=True, pattern=r"^[^@]+@[^@]+$")
]

validation = validate_form_fields(elements, rules)
print(f"All valid: {validation.all_valid}")
for result in validation.results:
    if not result.is_valid:
        print(f"{result.field_name}: {result.errors}")

PDF/A Compliance

from printwell import validate_pdfa, add_pdfa_metadata, PdfALevel

# Validate PDF/A compliance
result = validate_pdfa(pdf_data, PdfALevel.PdfA2b)
print(f"Compliant: {result.is_compliant}")
for issue in result.issues:
    print(f"{issue.severity}: {issue.message}")

# Add PDF/A metadata
pdfa_doc = add_pdfa_metadata(
    pdf_data,
    PdfALevel.PdfA2b,
    title="Archived Document",
    author="Author Name"
)

PDF/UA Accessibility

from printwell import (
    validate_pdfua, add_pdfua_metadata,
    PdfUALevel, AccessibilityOptions
)

# Validate accessibility
result = validate_pdfua(pdf_data, PdfUALevel.PdfUA1)
print(f"Accessible: {result.is_compliant}")

# Add accessibility metadata
options = AccessibilityOptions(
    language="en",
    title="Accessible Document",
    generate_outline=True
)

accessible_doc = add_pdfua_metadata(pdf_data, PdfUALevel.PdfUA1, options)

Error Handling

from printwell import Converter

try:
    converter = Converter()
    result = converter.html_to_pdf(html)
    result.write_to_file("output.pdf")
except RuntimeError as e:
    if "conversion" in str(e).lower():
        print(f"Conversion failed: {e}")
    elif "resource" in str(e).lower():
        print(f"Resource loading failed: {e}")
    else:
        raise

Flask Integration

from flask import Flask, send_file
from printwell import Converter
from io import BytesIO

app = Flask(__name__)
converter = Converter()

@app.route('/generate-pdf')
def generate_pdf():
    html = f"<h1>Generated at {datetime.now().isoformat()}</h1>"
    result = converter.html_to_pdf(html)

    buffer = BytesIO(result.data())
    buffer.seek(0)

    return send_file(
        buffer,
        mimetype='application/pdf',
        as_attachment=True,
        download_name='document.pdf'
    )

if __name__ == '__main__':
    app.run()

FastAPI Integration

from fastapi import FastAPI
from fastapi.responses import Response
from printwell import Converter

app = FastAPI()
converter = Converter()

@app.get("/generate-pdf")
async def generate_pdf():
    html = "<h1>Hello from FastAPI!</h1>"
    result = converter.html_to_pdf(html)

    return Response(
        content=result.data(),
        media_type="application/pdf",
        headers={
            "Content-Disposition": "attachment; filename=document.pdf"
        }
    )

Django Integration

from django.http import HttpResponse
from printwell import Converter

converter = Converter()

def generate_pdf(request):
    html = "<h1>Hello from Django!</h1>"
    result = converter.html_to_pdf(html)

    response = HttpResponse(result.data(), content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="document.pdf"'
    return response