Files
2025-11-30 08:30:10 +08:00

242 lines
5.7 KiB
Markdown

# Canvas API Reference
The Canvas API provides low-level, precise control over PDF generation using coordinate-based drawing.
## Coordinate System
- Origin (0, 0) is at the **lower-left corner** (not top-left like web graphics)
- X-axis points right, Y-axis points upward
- Units are in points (72 points = 1 inch)
- Default page size is A4; explicitly specify page size for consistency
## Basic Setup
```python
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter, A4
from reportlab.lib.units import inch
# Create canvas
c = canvas.Canvas("output.pdf", pagesize=letter)
# Get page dimensions
width, height = letter
# Draw content
c.drawString(100, 100, "Hello World")
# Finish page and save
c.showPage() # Complete current page
c.save() # Write PDF to disk
```
## Text Drawing
### Basic String Methods
```python
# Basic text placement
c.drawString(x, y, text) # Left-aligned at x, y
c.drawRightString(x, y, text) # Right-aligned at x, y
c.drawCentredString(x, y, text) # Center-aligned at x, y
# Font control
c.setFont(fontname, size) # e.g., "Helvetica", 12
c.setFillColor(color) # Text color
```
### Text Objects (Advanced)
For complex text operations with multiple lines and precise control:
```python
t = c.beginText(x, y)
t.setFont("Times-Roman", 14)
t.textLine("First line")
t.textLine("Second line")
t.setTextOrigin(x, y) # Reset position
c.drawText(t)
```
## Drawing Primitives
### Lines
```python
c.line(x1, y1, x2, y2) # Single line
c.lines([(x1,y1,x2,y2), (x3,y3,x4,y4)]) # Multiple lines
c.grid(xlist, ylist) # Grid from coordinate lists
```
### Shapes
```python
c.rect(x, y, width, height, stroke=1, fill=0)
c.roundRect(x, y, width, height, radius, stroke=1, fill=0)
c.circle(x_ctr, y_ctr, r, stroke=1, fill=0)
c.ellipse(x1, y1, x2, y2, stroke=1, fill=0)
c.wedge(x, y, radius, startAng, extent, stroke=1, fill=0)
```
### Bezier Curves
```python
c.bezier(x1, y1, x2, y2, x3, y3, x4, y4)
```
## Path Objects
For complex shapes, use path objects:
```python
p = c.beginPath()
p.moveTo(x, y) # Move without drawing
p.lineTo(x, y) # Draw line to point
p.curveTo(x1, y1, x2, y2, x3, y3) # Bezier curve
p.arc(x1, y1, x2, y2, startAng, extent)
p.arcTo(x1, y1, x2, y2, startAng, extent)
p.close() # Close path to start point
# Draw the path
c.drawPath(p, stroke=1, fill=0)
```
## Colors
### RGB (Screen Display)
```python
from reportlab.lib.colors import red, blue, Color
c.setFillColorRGB(r, g, b) # r, g, b are 0-1
c.setStrokeColorRGB(r, g, b)
c.setFillColor(red) # Named colors
c.setStrokeColor(blue)
# Custom with alpha transparency
c.setFillColor(Color(0.5, 0, 0, alpha=0.5))
```
### CMYK (Professional Printing)
```python
from reportlab.lib.colors import CMYKColor, PCMYKColor
c.setFillColorCMYK(c, m, y, k) # 0-1 range
c.setStrokeColorCMYK(c, m, y, k)
# Integer percentages (0-100)
c.setFillColor(PCMYKColor(100, 50, 0, 0))
```
## Line Styling
```python
c.setLineWidth(width) # Thickness in points
c.setLineCap(mode) # 0=butt, 1=round, 2=square
c.setLineJoin(mode) # 0=miter, 1=round, 2=bevel
c.setDash(array, phase) # e.g., [3, 3] for dotted line
```
## Coordinate Transformations
**IMPORTANT:** Transformations are incremental and cumulative.
```python
# Translation (move origin)
c.translate(dx, dy)
# Rotation (in degrees, counterclockwise)
c.rotate(theta)
# Scaling
c.scale(xscale, yscale)
# Skewing
c.skew(alpha, beta)
```
### State Management
```python
# Save current graphics state
c.saveState()
# ... apply transformations and draw ...
# Restore previous state
c.restoreState()
```
**Note:** State cannot be preserved across `showPage()` calls.
## Images
```python
from reportlab.lib.utils import ImageReader
# Preferred method (with caching)
c.drawImage(image_source, x, y, width=None, height=None,
mask=None, preserveAspectRatio=False)
# image_source can be:
# - Filename string
# - PIL Image object
# - ImageReader object
# For transparency, specify RGB mask range
c.drawImage("logo.png", 100, 500, mask=[255, 255, 255, 255, 255, 255])
# Inline (inefficient, no caching)
c.drawInlineImage(image_source, x, y, width=None, height=None)
```
## Page Management
```python
# Complete current page
c.showPage()
# Set page size for next page
c.setPageSize(size) # e.g., letter, A4
# Page compression (smaller files, slower generation)
c = canvas.Canvas("output.pdf", pageCompression=1)
```
## Common Patterns
### Margins and Layout
```python
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
width, height = letter
margin = inch
# Draw within margins
content_width = width - 2*margin
content_height = height - 2*margin
# Text at top margin
c.drawString(margin, height - margin, "Header")
# Text at bottom margin
c.drawString(margin, margin, "Footer")
```
### Headers and Footers
```python
def draw_header_footer(c, width, height):
c.saveState()
c.setFont("Helvetica", 9)
c.drawString(inch, height - 0.5*inch, "Company Name")
c.drawRightString(width - inch, 0.5*inch, f"Page {c.getPageNumber()}")
c.restoreState()
# Call on each page
draw_header_footer(c, width, height)
c.showPage()
```
## Best Practices
1. **Always specify page size** - Different platforms have different defaults
2. **Use variables for measurements** - `margin = inch` instead of hardcoded values
3. **Match saveState/restoreState** - Always balance these calls
4. **Apply transformations externally** for engineering drawings to prevent line width scaling
5. **Use drawImage over drawInlineImage** for better performance with repeated images
6. **Draw from bottom-up** - Remember Y-axis points upward