562 lines
12 KiB
Markdown
562 lines
12 KiB
Markdown
# PDF Features Reference
|
|
|
|
Advanced PDF capabilities: links, bookmarks, forms, encryption, and metadata.
|
|
|
|
## Document Metadata
|
|
|
|
Set PDF document properties viewable in PDF readers.
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
|
|
c = canvas.Canvas("output.pdf")
|
|
|
|
# Set metadata
|
|
c.setAuthor("John Doe")
|
|
c.setTitle("Annual Report 2024")
|
|
c.setSubject("Financial Analysis")
|
|
c.setKeywords("finance, annual, report, 2024")
|
|
c.setCreator("MyApp v1.0")
|
|
|
|
# ... draw content ...
|
|
|
|
c.save()
|
|
```
|
|
|
|
With Platypus:
|
|
|
|
```python
|
|
from reportlab.platypus import SimpleDocTemplate
|
|
|
|
doc = SimpleDocTemplate(
|
|
"output.pdf",
|
|
title="Annual Report 2024",
|
|
author="John Doe",
|
|
subject="Financial Analysis",
|
|
)
|
|
|
|
doc.build(story)
|
|
```
|
|
|
|
## Bookmarks and Destinations
|
|
|
|
Create internal navigation structure.
|
|
|
|
### Simple Bookmarks
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
|
|
c = canvas.Canvas("output.pdf")
|
|
|
|
# Create bookmark for current page
|
|
c.bookmarkPage("intro") # Internal key
|
|
c.addOutlineEntry("Introduction", "intro", level=0)
|
|
|
|
c.showPage()
|
|
|
|
# Another bookmark
|
|
c.bookmarkPage("chapter1")
|
|
c.addOutlineEntry("Chapter 1", "chapter1", level=0)
|
|
|
|
# Sub-sections
|
|
c.bookmarkPage("section1_1")
|
|
c.addOutlineEntry("Section 1.1", "section1_1", level=1) # Nested
|
|
|
|
c.save()
|
|
```
|
|
|
|
### Bookmark Levels
|
|
|
|
```python
|
|
# Create hierarchical outline
|
|
c.bookmarkPage("ch1")
|
|
c.addOutlineEntry("Chapter 1", "ch1", level=0)
|
|
|
|
c.bookmarkPage("ch1_s1")
|
|
c.addOutlineEntry("Section 1.1", "ch1_s1", level=1)
|
|
|
|
c.bookmarkPage("ch1_s1_1")
|
|
c.addOutlineEntry("Subsection 1.1.1", "ch1_s1_1", level=2)
|
|
|
|
c.bookmarkPage("ch2")
|
|
c.addOutlineEntry("Chapter 2", "ch2", level=0)
|
|
```
|
|
|
|
### Destination Fit Modes
|
|
|
|
Control how the page displays when navigating:
|
|
|
|
```python
|
|
# bookmarkPage with fit mode
|
|
c.bookmarkPage(
|
|
key="chapter1",
|
|
fit="Fit" # Fit entire page in window
|
|
)
|
|
|
|
# Or use bookmarkHorizontalAbsolute
|
|
c.bookmarkHorizontalAbsolute(key="section", top=500)
|
|
|
|
# Available fit modes:
|
|
# "Fit" - Fit whole page
|
|
# "FitH" - Fit horizontally
|
|
# "FitV" - Fit vertically
|
|
# "FitR" - Fit rectangle
|
|
# "XYZ" - Specific position and zoom
|
|
```
|
|
|
|
## Hyperlinks
|
|
|
|
### External Links
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
from reportlab.lib.units import inch
|
|
|
|
c = canvas.Canvas("output.pdf")
|
|
|
|
# Draw link rectangle
|
|
c.linkURL(
|
|
"https://www.example.com",
|
|
rect=(1*inch, 5*inch, 3*inch, 5.5*inch), # (x1, y1, x2, y2)
|
|
relative=0, # 0 for absolute positioning
|
|
thickness=1,
|
|
color=(0, 0, 1), # Blue
|
|
dashArray=None
|
|
)
|
|
|
|
# Draw text over link area
|
|
c.setFillColorRGB(0, 0, 1) # Blue text
|
|
c.drawString(1*inch, 5.2*inch, "Click here to visit example.com")
|
|
|
|
c.save()
|
|
```
|
|
|
|
### Internal Links
|
|
|
|
Link to bookmarked locations within the document:
|
|
|
|
```python
|
|
# Create destination
|
|
c.bookmarkPage("target_section")
|
|
|
|
# Later, create link to that destination
|
|
c.linkRect(
|
|
"Link Text",
|
|
"target_section", # Bookmark key
|
|
rect=(1*inch, 3*inch, 2*inch, 3.2*inch),
|
|
relative=0
|
|
)
|
|
```
|
|
|
|
### Links in Paragraphs
|
|
|
|
For Platypus documents:
|
|
|
|
```python
|
|
from reportlab.platypus import Paragraph
|
|
|
|
# External link
|
|
text = '<link href="https://example.com" color="blue">Visit our website</link>'
|
|
para = Paragraph(text, style)
|
|
|
|
# Internal link (to anchor)
|
|
text = '<link href="#section1" color="blue">Go to Section 1</link>'
|
|
para1 = Paragraph(text, style)
|
|
|
|
# Create anchor
|
|
text = '<a name="section1"/>Section 1 Heading'
|
|
para2 = Paragraph(text, heading_style)
|
|
|
|
story.append(para1)
|
|
story.append(para2)
|
|
```
|
|
|
|
## Interactive Forms
|
|
|
|
Create fillable PDF forms.
|
|
|
|
### Text Fields
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
from reportlab.pdfbase import pdfform
|
|
from reportlab.lib.colors import black, white
|
|
|
|
c = canvas.Canvas("form.pdf")
|
|
|
|
# Create text field
|
|
c.acroForm.textfield(
|
|
name="name",
|
|
tooltip="Enter your name",
|
|
x=100,
|
|
y=700,
|
|
width=200,
|
|
height=20,
|
|
borderColor=black,
|
|
fillColor=white,
|
|
textColor=black,
|
|
forceBorder=True,
|
|
fontSize=12,
|
|
maxlen=100, # Maximum character length
|
|
)
|
|
|
|
# Label
|
|
c.drawString(100, 725, "Name:")
|
|
|
|
c.save()
|
|
```
|
|
|
|
### Checkboxes
|
|
|
|
```python
|
|
# Create checkbox
|
|
c.acroForm.checkbox(
|
|
name="agree",
|
|
tooltip="I agree to terms",
|
|
x=100,
|
|
y=650,
|
|
size=20,
|
|
buttonStyle='check', # 'check', 'circle', 'cross', 'diamond', 'square', 'star'
|
|
borderColor=black,
|
|
fillColor=white,
|
|
textColor=black,
|
|
forceBorder=True,
|
|
checked=False, # Initial state
|
|
)
|
|
|
|
c.drawString(130, 655, "I agree to the terms and conditions")
|
|
```
|
|
|
|
### Radio Buttons
|
|
|
|
```python
|
|
# Radio button group - only one can be selected
|
|
c.acroForm.radio(
|
|
name="payment", # Same name for group
|
|
tooltip="Credit Card",
|
|
value="credit", # Value when selected
|
|
x=100,
|
|
y=600,
|
|
size=15,
|
|
selected=False,
|
|
)
|
|
c.drawString(125, 603, "Credit Card")
|
|
|
|
c.acroForm.radio(
|
|
name="payment", # Same name
|
|
tooltip="PayPal",
|
|
value="paypal",
|
|
x=100,
|
|
y=580,
|
|
size=15,
|
|
selected=False,
|
|
)
|
|
c.drawString(125, 583, "PayPal")
|
|
```
|
|
|
|
### List Boxes
|
|
|
|
```python
|
|
# Listbox with multiple options
|
|
c.acroForm.listbox(
|
|
name="country",
|
|
tooltip="Select your country",
|
|
value="US", # Default selected
|
|
x=100,
|
|
y=500,
|
|
width=150,
|
|
height=80,
|
|
borderColor=black,
|
|
fillColor=white,
|
|
textColor=black,
|
|
forceBorder=True,
|
|
options=[
|
|
("United States", "US"),
|
|
("Canada", "CA"),
|
|
("Mexico", "MX"),
|
|
("Other", "OTHER"),
|
|
], # List of (label, value) tuples
|
|
multiple=False, # Allow multiple selections
|
|
)
|
|
```
|
|
|
|
### Choice (Dropdown)
|
|
|
|
```python
|
|
# Dropdown menu
|
|
c.acroForm.choice(
|
|
name="state",
|
|
tooltip="Select state",
|
|
value="CA",
|
|
x=100,
|
|
y=450,
|
|
width=150,
|
|
height=20,
|
|
borderColor=black,
|
|
fillColor=white,
|
|
textColor=black,
|
|
forceBorder=True,
|
|
options=[
|
|
("California", "CA"),
|
|
("New York", "NY"),
|
|
("Texas", "TX"),
|
|
],
|
|
)
|
|
```
|
|
|
|
### Complete Form Example
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
from reportlab.lib.pagesizes import letter
|
|
from reportlab.lib.colors import black, white, lightgrey
|
|
from reportlab.lib.units import inch
|
|
|
|
def create_registration_form(filename):
|
|
c = canvas.Canvas(filename, pagesize=letter)
|
|
c.setFont("Helvetica-Bold", 16)
|
|
c.drawString(inch, 10*inch, "Registration Form")
|
|
|
|
y = 9*inch
|
|
c.setFont("Helvetica", 12)
|
|
|
|
# Name field
|
|
c.drawString(inch, y, "Full Name:")
|
|
c.acroForm.textfield(
|
|
name="fullname",
|
|
x=2*inch, y=y-5, width=4*inch, height=20,
|
|
borderColor=black, fillColor=lightgrey, forceBorder=True
|
|
)
|
|
|
|
# Email field
|
|
y -= 0.5*inch
|
|
c.drawString(inch, y, "Email:")
|
|
c.acroForm.textfield(
|
|
name="email",
|
|
x=2*inch, y=y-5, width=4*inch, height=20,
|
|
borderColor=black, fillColor=lightgrey, forceBorder=True
|
|
)
|
|
|
|
# Age dropdown
|
|
y -= 0.5*inch
|
|
c.drawString(inch, y, "Age Group:")
|
|
c.acroForm.choice(
|
|
name="age_group",
|
|
x=2*inch, y=y-5, width=2*inch, height=20,
|
|
borderColor=black, fillColor=lightgrey, forceBorder=True,
|
|
options=[("18-25", "18-25"), ("26-35", "26-35"),
|
|
("36-50", "36-50"), ("51+", "51+")]
|
|
)
|
|
|
|
# Newsletter checkbox
|
|
y -= 0.5*inch
|
|
c.acroForm.checkbox(
|
|
name="newsletter",
|
|
x=inch, y=y-5, size=15,
|
|
buttonStyle='check', borderColor=black, forceBorder=True
|
|
)
|
|
c.drawString(inch + 25, y, "Subscribe to newsletter")
|
|
|
|
c.save()
|
|
|
|
create_registration_form("registration.pdf")
|
|
```
|
|
|
|
## Encryption and Security
|
|
|
|
Protect PDFs with passwords and permissions.
|
|
|
|
### Basic Encryption
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
|
|
c = canvas.Canvas("secure.pdf")
|
|
|
|
# Encrypt with user password
|
|
c.encrypt(
|
|
userPassword="user123", # Password to open
|
|
ownerPassword="owner456", # Password to change permissions
|
|
canPrint=1, # Allow printing
|
|
canModify=0, # Disallow modifications
|
|
canCopy=1, # Allow text copying
|
|
canAnnotate=0, # Disallow annotations
|
|
strength=128, # 40 or 128 bit encryption
|
|
)
|
|
|
|
# ... draw content ...
|
|
|
|
c.save()
|
|
```
|
|
|
|
### Permission Settings
|
|
|
|
```python
|
|
c.encrypt(
|
|
userPassword="user123",
|
|
ownerPassword="owner456",
|
|
canPrint=1, # 1 = allow, 0 = deny
|
|
canModify=0, # Prevent content modification
|
|
canCopy=1, # Allow text/graphics copying
|
|
canAnnotate=0, # Prevent comments/annotations
|
|
strength=128, # Use 128-bit encryption
|
|
)
|
|
```
|
|
|
|
### Advanced Encryption
|
|
|
|
```python
|
|
from reportlab.lib.pdfencrypt import StandardEncryption
|
|
|
|
# Create encryption object
|
|
encrypt = StandardEncryption(
|
|
userPassword="user123",
|
|
ownerPassword="owner456",
|
|
canPrint=1,
|
|
canModify=0,
|
|
canCopy=1,
|
|
canAnnotate=1,
|
|
strength=128,
|
|
)
|
|
|
|
# Use with canvas
|
|
c = canvas.Canvas("secure.pdf")
|
|
c._doc.encrypt = encrypt
|
|
|
|
# ... draw content ...
|
|
|
|
c.save()
|
|
```
|
|
|
|
### Platypus with Encryption
|
|
|
|
```python
|
|
from reportlab.platypus import SimpleDocTemplate
|
|
|
|
doc = SimpleDocTemplate("secure.pdf")
|
|
|
|
# Set encryption
|
|
doc.encrypt = True
|
|
doc.canPrint = 1
|
|
doc.canModify = 0
|
|
|
|
# Or use encrypt() method
|
|
doc.encrypt = encrypt_object
|
|
|
|
doc.build(story)
|
|
```
|
|
|
|
## Page Transitions
|
|
|
|
Add visual effects for presentations.
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
|
|
c = canvas.Canvas("presentation.pdf")
|
|
|
|
# Set transition for current page
|
|
c.setPageTransition(
|
|
effectname="Wipe", # Transition effect
|
|
duration=1, # Duration in seconds
|
|
direction=0 # Direction (effect-specific)
|
|
)
|
|
|
|
# Available effects:
|
|
# "Split", "Blinds", "Box", "Wipe", "Dissolve",
|
|
# "Glitter", "R" (Replace), "Fly", "Push", "Cover",
|
|
# "Uncover", "Fade"
|
|
|
|
# Direction values (effect-dependent):
|
|
# 0, 90, 180, 270 for most directional effects
|
|
|
|
# Example: Slide with fade transition
|
|
c.setFont("Helvetica-Bold", 24)
|
|
c.drawString(100, 400, "Slide 1")
|
|
c.setPageTransition("Fade", 0.5)
|
|
c.showPage()
|
|
|
|
c.drawString(100, 400, "Slide 2")
|
|
c.setPageTransition("Wipe", 1, 90)
|
|
c.showPage()
|
|
|
|
c.save()
|
|
```
|
|
|
|
## PDF/A Compliance
|
|
|
|
Create archival-quality PDFs.
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
|
|
c = canvas.Canvas("pdfa.pdf")
|
|
|
|
# Enable PDF/A-1b compliance
|
|
c.setPageCompression(0) # PDF/A requires uncompressed
|
|
# Note: Full PDF/A requires additional XMP metadata
|
|
# This is simplified - full compliance needs more setup
|
|
|
|
# ... draw content ...
|
|
|
|
c.save()
|
|
```
|
|
|
|
## Compression
|
|
|
|
Control file size vs generation speed.
|
|
|
|
```python
|
|
# Enable page compression
|
|
c = canvas.Canvas("output.pdf", pageCompression=1)
|
|
|
|
# Compression reduces file size but slows generation
|
|
# 0 = no compression (faster, larger files)
|
|
# 1 = compression (slower, smaller files)
|
|
```
|
|
|
|
## Forms and XObjects
|
|
|
|
Reusable graphics elements.
|
|
|
|
```python
|
|
from reportlab.pdfgen import canvas
|
|
|
|
c = canvas.Canvas("output.pdf")
|
|
|
|
# Begin form (reusable object)
|
|
c.beginForm("logo")
|
|
c.setFillColorRGB(0, 0, 1)
|
|
c.rect(0, 0, 100, 50, fill=1)
|
|
c.setFillColorRGB(1, 1, 1)
|
|
c.drawString(10, 20, "LOGO")
|
|
c.endForm()
|
|
|
|
# Use form multiple times
|
|
c.doForm("logo") # At current position
|
|
c.translate(200, 0)
|
|
c.doForm("logo") # At translated position
|
|
c.translate(200, 0)
|
|
c.doForm("logo")
|
|
|
|
c.save()
|
|
|
|
# Benefits: Smaller file size, faster rendering
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Always set metadata** for professional documents
|
|
2. **Use bookmarks** for documents > 10 pages
|
|
3. **Make links visually distinct** (blue, underlined)
|
|
4. **Test forms** in multiple PDF readers (behavior varies)
|
|
5. **Use strong encryption (128-bit)** for sensitive data
|
|
6. **Set both user and owner passwords** for full security
|
|
7. **Enable printing** unless specifically restricted
|
|
8. **Test page transitions** - some readers don't support all effects
|
|
9. **Use meaningful bookmark titles** for navigation
|
|
10. **Consider PDF/A** for long-term archival needs
|
|
11. **Validate form field names** - must be unique and valid identifiers
|
|
12. **Add tooltips** to form fields for better UX
|