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

5.7 KiB

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

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

# 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:

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

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

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

c.bezier(x1, y1, x2, y2, x3, y3, x4, y4)

Path Objects

For complex shapes, use path objects:

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)

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)

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

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.

# 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

# Save current graphics state
c.saveState()

# ... apply transformations and draw ...

# Restore previous state
c.restoreState()

Note: State cannot be preserved across showPage() calls.

Images

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

# 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

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

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