CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pypdfium2

Python bindings to PDFium for comprehensive PDF manipulation, rendering, and processing

Pending
Overview
Eval results
Files

transformation.mddocs/

Transformation and Geometry

2D transformation matrices for coordinate system manipulation, rotation, scaling, translation operations, and geometric calculations. The PdfMatrix class provides comprehensive support for PDF coordinate transformations.

Capabilities

Matrix Creation

Create transformation matrices with various initialization options.

class PdfMatrix:
    def __init__(self, a=1, b=0, c=0, d=1, e=0, f=0):
        """
        Create transformation matrix.
        
        Parameters:
        - a: float, horizontal scaling
        - b: float, horizontal skewing  
        - c: float, vertical skewing
        - d: float, vertical scaling
        - e: float, horizontal translation
        - f: float, vertical translation
        
        Standard form: [a c e]
                      [b d f]  
                      [0 0 1]
        """
    
    @classmethod
    def from_raw(cls, raw) -> PdfMatrix:
        """
        Create matrix from raw FS_MATRIX structure.
        
        Parameters:
        - raw: FS_MATRIX, raw PDFium matrix structure
        
        Returns:
        PdfMatrix: Matrix object wrapping the raw structure
        """

Matrix creation examples:

import pypdfium2 as pdfium

# Identity matrix (no transformation)
identity = pdfium.PdfMatrix()
print(f"Identity: {identity.get()}")  # (1, 0, 0, 1, 0, 0)

# Custom matrix
custom = pdfium.PdfMatrix(a=2, d=2, e=100, f=50)
print(f"Custom: {custom.get()}")  # Scale 2x and translate (100, 50)

# From raw PDFium matrix (if available)
# raw_matrix = some_pdfium_function_returning_matrix()
# matrix = pdfium.PdfMatrix.from_raw(raw_matrix)

Matrix Properties

Access individual matrix coefficients.

@property
def a(self) -> float:
    """Horizontal scaling factor."""

@property  
def b(self) -> float:
    """Horizontal skewing factor."""

@property
def c(self) -> float:
    """Vertical skewing factor."""

@property
def d(self) -> float:
    """Vertical scaling factor."""

@property
def e(self) -> float:
    """Horizontal translation."""

@property
def f(self) -> float:
    """Vertical translation."""

Property access:

matrix = pdfium.PdfMatrix(a=1.5, d=2.0, e=100, f=200)

print(f"Horizontal scale: {matrix.a}")
print(f"Vertical scale: {matrix.d}")
print(f"Translation: ({matrix.e}, {matrix.f})")
print(f"Skew: ({matrix.b}, {matrix.c})")

Matrix Operations

Core matrix operations including conversion and multiplication.

def get(self) -> tuple:
    """
    Get matrix coefficients as tuple.
    
    Returns:
    tuple: (a, b, c, d, e, f) matrix coefficients
    """

def to_raw(self) -> FS_MATRIX:
    """
    Convert to PDFium matrix structure.
    
    Returns:
    FS_MATRIX: Raw PDFium matrix for use with low-level functions
    """

def multiply(self, other: PdfMatrix) -> PdfMatrix:
    """
    Multiply with another matrix.
    
    Parameters:
    - other: PdfMatrix, matrix to multiply with
    
    Returns:
    PdfMatrix: Result matrix (this * other)
    """

Basic operations:

# Create matrices
m1 = pdfium.PdfMatrix(a=2, d=2)        # Scale 2x
m2 = pdfium.PdfMatrix(e=100, f=50)     # Translate (100, 50)

# Get coefficients
coeffs = m1.get()
print(f"Scale matrix: {coeffs}")

# Matrix multiplication (apply scale then translation)
combined = m1.multiply(m2)
print(f"Combined: {combined.get()}")

# Convert to raw for PDFium functions
raw_matrix = combined.to_raw()

Transformation Creation

Create specific types of transformations with convenient methods.

def translate(self, x: float, y: float) -> PdfMatrix:
    """
    Create translation matrix.
    
    Parameters:
    - x: float, horizontal translation in PDF units
    - y: float, vertical translation in PDF units
    
    Returns:
    PdfMatrix: Translation transformation matrix
    """

def scale(self, x: float, y: float) -> PdfMatrix:
    """
    Create scaling matrix.
    
    Parameters:
    - x: float, horizontal scaling factor
    - y: float, vertical scaling factor
    
    Returns:
    PdfMatrix: Scaling transformation matrix
    """

def rotate(self, angle: float, ccw=False, rad=False) -> PdfMatrix:
    """
    Create rotation matrix.
    
    Parameters:
    - angle: float, rotation angle
    - ccw: bool, counter-clockwise rotation (default: clockwise)
    - rad: bool, angle in radians (default: degrees)
    
    Returns:
    PdfMatrix: Rotation transformation matrix
    """

def mirror(self, v: bool, h: bool) -> PdfMatrix:
    """
    Create mirroring matrix.
    
    Parameters:
    - v: bool, vertical mirroring (flip top-bottom)
    - h: bool, horizontal mirroring (flip left-right)
    
    Returns:
    PdfMatrix: Mirroring transformation matrix
    """

def skew(self, x_angle: float, y_angle: float, rad=False) -> PdfMatrix:
    """
    Create skewing matrix.
    
    Parameters:
    - x_angle: float, horizontal skew angle
    - y_angle: float, vertical skew angle  
    - rad: bool, angles in radians (default: degrees)
    
    Returns:
    PdfMatrix: Skewing transformation matrix
    """

Transformation examples:

import math

# Translation
translate_matrix = pdfium.PdfMatrix().translate(100, 200)
print(f"Translate (100, 200): {translate_matrix.get()}")

# Scaling  
scale_matrix = pdfium.PdfMatrix().scale(1.5, 2.0)
print(f"Scale (1.5x, 2.0x): {scale_matrix.get()}")

# Rotation (45 degrees clockwise)
rotate_matrix = pdfium.PdfMatrix().rotate(45)
print(f"Rotate 45°: {rotate_matrix.get()}")

# Rotation (π/4 radians counter-clockwise)
rotate_ccw = pdfium.PdfMatrix().rotate(math.pi/4, ccw=True, rad=True)
print(f"Rotate π/4 CCW: {rotate_ccw.get()}")

# Mirroring
mirror_h = pdfium.PdfMatrix().mirror(v=False, h=True)  # Horizontal flip
mirror_v = pdfium.PdfMatrix().mirror(v=True, h=False)  # Vertical flip
print(f"Mirror horizontal: {mirror_h.get()}")
print(f"Mirror vertical: {mirror_v.get()}")

# Skewing
skew_matrix = pdfium.PdfMatrix().skew(15, 0)  # 15° horizontal skew
print(f"Skew 15° horizontal: {skew_matrix.get()}")

Point and Rectangle Transformation

Apply transformations to coordinates and rectangular areas.

def on_point(self, x: float, y: float) -> tuple:
    """
    Transform point coordinates.
    
    Parameters:
    - x: float, x-coordinate
    - y: float, y-coordinate
    
    Returns:
    tuple: (transformed_x, transformed_y)
    """

def on_rect(self, left: float, bottom: float, right: float, top: float) -> tuple:
    """
    Transform rectangle coordinates.
    
    Parameters:
    - left: float, left edge
    - bottom: float, bottom edge
    - right: float, right edge  
    - top: float, top edge
    
    Returns:
    tuple: (new_left, new_bottom, new_right, new_top)
    """

Coordinate transformation:

# Create composite transformation
transform = pdfium.PdfMatrix()
transform = transform.translate(100, 50)   # Move to (100, 50)
transform = transform.rotate(30)           # Rotate 30 degrees
transform = transform.scale(1.5, 1.5)      # Scale 1.5x

# Transform a point
original_point = (0, 0)
transformed_point = transform.on_point(*original_point)
print(f"Point {original_point} -> {transformed_point}")

# Transform another point
point2 = (100, 100)
transformed_point2 = transform.on_point(*point2)
print(f"Point {point2} -> {transformed_point2}")

# Transform a rectangle
original_rect = (0, 0, 200, 100)  # (left, bottom, right, top)
transformed_rect = transform.on_rect(*original_rect)
print(f"Rectangle {original_rect} -> {transformed_rect}")

Complex Transformations

Combine multiple transformations for complex geometric operations.

def create_centered_rotation(center_x, center_y, angle):
    """Create rotation around specific center point."""
    
    # Translate to origin
    to_origin = pdfium.PdfMatrix().translate(-center_x, -center_y)
    
    # Rotate around origin
    rotation = pdfium.PdfMatrix().rotate(angle)
    
    # Translate back
    from_origin = pdfium.PdfMatrix().translate(center_x, center_y)
    
    # Combine transformations (order matters!)
    result = to_origin.multiply(rotation).multiply(from_origin)
    
    return result

def create_fit_transformation(source_rect, target_rect, maintain_aspect=True):
    """Create transformation to fit source rectangle into target rectangle."""
    
    src_left, src_bottom, src_right, src_top = source_rect
    tgt_left, tgt_bottom, tgt_right, tgt_top = target_rect
    
    src_width = src_right - src_left
    src_height = src_top - src_bottom
    tgt_width = tgt_right - tgt_left
    tgt_height = tgt_top - tgt_bottom
    
    # Calculate scaling factors
    scale_x = tgt_width / src_width
    scale_y = tgt_height / src_height
    
    if maintain_aspect:
        # Use smaller scale to maintain aspect ratio
        scale = min(scale_x, scale_y)
        scale_x = scale_y = scale
    
    # Create transformation
    transform = pdfium.PdfMatrix()
    
    # Translate source to origin
    transform = transform.translate(-src_left, -src_bottom)
    
    # Scale to fit
    transform = transform.scale(scale_x, scale_y)
    
    # Translate to target position
    if maintain_aspect:
        # Center in target rectangle
        actual_width = src_width * scale
        actual_height = src_height * scale
        offset_x = (tgt_width - actual_width) / 2
        offset_y = (tgt_height - actual_height) / 2
        transform = transform.translate(tgt_left + offset_x, tgt_bottom + offset_y)
    else:
        transform = transform.translate(tgt_left, tgt_bottom)
    
    return transform

# Usage examples
pdf = pdfium.PdfDocument("document.pdf")
page = pdf[0]

# Get an image object
for i in range(page.count_objects()):
    obj = page.get_object(i)
    if isinstance(obj, pdfium.PdfImage):
        
        # Rotate image around its center
        bounds = obj.get_pos()
        if bounds:
            left, bottom, right, top = bounds
            center_x = (left + right) / 2
            center_y = (bottom + top) / 2
            
            rotation_transform = create_centered_rotation(center_x, center_y, 45)
            obj.set_matrix(rotation_transform)
        
        # Or fit image to specific area
        target_area = (100, 100, 400, 300)  # Specific rectangle
        if bounds:
            fit_transform = create_fit_transformation(bounds, target_area, maintain_aspect=True)
            obj.set_matrix(fit_transform)
        
        break

# Regenerate page content
page.gen_content()

Coordinate System Utilities

Helper functions for working with PDF coordinate systems.

def pdf_to_screen_transform(page_width, page_height, screen_width, screen_height, maintain_aspect=True):
    """Create transformation from PDF coordinates to screen coordinates."""
    
    # PDF uses bottom-left origin, screen uses top-left
    # Need to flip Y axis and scale appropriately
    
    scale_x = screen_width / page_width
    scale_y = screen_height / page_height
    
    if maintain_aspect:
        scale = min(scale_x, scale_y)
        scale_x = scale_y = scale
    
    transform = pdfium.PdfMatrix()
    
    # Scale to screen size
    transform = transform.scale(scale_x, -scale_y)  # Negative Y to flip
    
    # Translate to handle flipped Y coordinate
    transform = transform.translate(0, -page_height * scale_y)
    
    return transform

def inches_to_pdf_transform(dpi=72):
    """Create transformation from inches to PDF units."""
    return pdfium.PdfMatrix().scale(dpi, dpi)

def mm_to_pdf_transform():
    """Create transformation from millimeters to PDF units."""
    mm_per_inch = 25.4
    pdf_units_per_inch = 72
    scale = pdf_units_per_inch / mm_per_inch
    return pdfium.PdfMatrix().scale(scale, scale)

# Usage
page = pdf[0]
page_width, page_height = page.get_size()

# Transform for 800x600 screen display
screen_transform = pdf_to_screen_transform(
    page_width, page_height, 800, 600, maintain_aspect=True
)

# Convert PDF point to screen coordinates
pdf_point = (200, 400)
screen_point = screen_transform.on_point(*pdf_point)
print(f"PDF point {pdf_point} -> Screen point {screen_point}")

Matrix Analysis

Analyze transformation matrices to understand their properties.

def analyze_matrix(matrix):
    """Analyze transformation matrix properties."""
    
    a, b, c, d, e, f = matrix.get()
    
    # Translation components
    translation = (e, f)
    
    # Scaling factors (approximate for rotated matrices)
    scale_x = math.sqrt(a*a + b*b)
    scale_y = math.sqrt(c*c + d*d)
    
    # Rotation angle (in degrees)
    rotation_rad = math.atan2(b, a)
    rotation_deg = math.degrees(rotation_rad)
    
    # Determinant (affects area scaling)
    determinant = a*d - b*c
    
    # Shearing
    shear = (c != 0 or b != 0) and (abs(rotation_deg) % 90 != 0)
    
    analysis = {
        'translation': translation,
        'scale_x': scale_x,
        'scale_y': scale_y,
        'rotation_degrees': rotation_deg,
        'determinant': determinant,
        'area_scale_factor': abs(determinant),
        'preserves_angles': abs(abs(determinant) - scale_x * scale_y) < 1e-6,
        'has_shear': shear,
        'is_identity': abs(a-1) < 1e-6 and abs(d-1) < 1e-6 and abs(b) < 1e-6 and abs(c) < 1e-6 and abs(e) < 1e-6 and abs(f) < 1e-6
    }
    
    return analysis

# Usage
transform = pdfium.PdfMatrix().translate(100, 50).rotate(30).scale(1.5, 2.0)
analysis = analyze_matrix(transform)

print("Matrix Analysis:")
for key, value in analysis.items():
    print(f"  {key}: {value}")

Common Transformation Patterns

Object Positioning

def position_object_at_center(obj, page_width, page_height):
    """Position object at page center."""
    
    bounds = obj.get_pos()
    if not bounds:
        return
    
    left, bottom, right, top = bounds
    obj_width = right - left
    obj_height = top - bottom
    
    # Calculate center position
    center_x = (page_width - obj_width) / 2
    center_y = (page_height - obj_height) / 2
    
    # Create transformation to move to center
    transform = pdfium.PdfMatrix().translate(
        center_x - left,
        center_y - bottom
    )
    
    obj.set_matrix(transform)

# Usage with page object
page = pdf[0]
page_width, page_height = page.get_size()

for i in range(page.count_objects()):
    obj = page.get_object(i)
    if isinstance(obj, pdfium.PdfImage):
        position_object_at_center(obj, page_width, page_height)
        break

Install with Tessl CLI

npx tessl i tessl/pypi-pypdfium2

docs

attachments.md

cli-tools.md

document-management.md

image-bitmap.md

index.md

page-manipulation.md

page-objects.md

text-processing.md

transformation.md

version-info.md

tile.json