CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pikepdf

Read and write PDFs with Python, powered by qpdf

Pending
Overview
Eval results
Files

images.mddocs/

Images and Graphics

Image extraction, manipulation, and graphics operations including support for various formats and color spaces. These capabilities enable comprehensive image handling within PDF documents.

Capabilities

PdfImage Class

High-level PDF image handling with extraction and conversion capabilities.

class PdfImage:
    """
    PDF image object handler for image extraction and manipulation.
    
    Provides access to image properties, extraction capabilities,
    and conversion to external formats like PIL Image objects.
    """
    
    def extract_to(self, *, fileprefix: str = 'image', dirname: str = '.') -> str:
        """
        Extract the image to a file with automatic format detection.
        
        The image is saved with an appropriate file extension based on
        its format and compression. Supports PNG, JPEG, TIFF, and other formats.
        
        Parameters:
        - fileprefix (str): Base filename for the extracted image
        - dirname (str): Directory to save the image in
        
        Returns:
        str: Full path to the extracted image file
        
        Raises:
        UnsupportedImageTypeError: If image format is not supported
        InvalidPdfImageError: If image data is corrupted or invalid
        """
    
    def as_pil_image(self) -> Any:  # PIL.Image.Image
        """
        Convert the PDF image to a PIL Image object.
        
        Returns:
        PIL.Image.Image: PIL Image object that can be manipulated or saved
        
        Raises:
        DependencyError: If PIL (Pillow) is not installed
        UnsupportedImageTypeError: If image cannot be converted
        InvalidPdfImageError: If image data is invalid
        """
    
    @property
    def width(self) -> int:
        """
        Image width in pixels.
        
        Returns:
        int: Width of the image in pixels
        """
    
    @property
    def height(self) -> int:
        """
        Image height in pixels.
        
        Returns:
        int: Height of the image in pixels
        """
    
    @property
    def bpc(self) -> int:
        """
        Bits per component (color depth).
        
        Returns:
        int: Number of bits per color component (typically 1, 8, or 16)
        """
    
    @property
    def colorspace(self) -> Name:
        """
        Color space of the image.
        
        Returns:
        Name: Color space (e.g., Name.DeviceRGB, Name.DeviceCMYK, Name.DeviceGray)
        """
    
    @property
    def filters(self) -> list[Name]:
        """
        List of filters applied to the image data.
        
        Returns:
        list[Name]: Compression and encoding filters (e.g., [Name.DCTDecode] for JPEG)
        """
    
    @property
    def filter_decodeparms(self) -> list[Object]:
        """
        Decode parameters for image filters.
        
        Returns:
        list[Object]: Parameters for filter decoding
        """
    
    @property
    def image_mask(self) -> bool:
        """
        Whether this image is used as a mask.
        
        Returns:
        bool: True if image is a mask (1-bit monochrome used for transparency)
        """
    
    @property
    def mask(self) -> Object:
        """
        Mask associated with this image.
        
        Returns:
        Object: Mask image or soft mask for transparency effects
        """
    
    @property
    def palette(self) -> Object:
        """
        Color palette for indexed color images.
        
        Returns:
        Object: Palette data for indexed color space images
        """
    
    @property
    def size(self) -> tuple[int, int]:
        """
        Image dimensions as a tuple.
        
        Returns:
        tuple[int, int]: (width, height) in pixels
        """
    
    @property
    def obj(self) -> Stream:
        """
        The underlying PDF stream object containing image data.
        
        Returns:
        Stream: PDF stream with image data and metadata
        """

PdfInlineImage Class

Handler for inline images embedded directly in content streams.

class PdfInlineImage:
    """
    Inline image embedded directly in a PDF content stream.
    
    Inline images are embedded directly in the page content stream
    rather than being stored as separate objects with indirect references.
    """
    
    def as_pil_image(self) -> Any:  # PIL.Image.Image
        """
        Convert the inline image to a PIL Image object.
        
        Returns:
        PIL.Image.Image: PIL Image object for manipulation or display
        
        Raises:
        DependencyError: If PIL (Pillow) is not installed
        UnsupportedImageTypeError: If image format is not supported
        InvalidPdfImageError: If image data is corrupted
        """
    
    @property
    def width(self) -> int:
        """
        Inline image width in pixels.
        
        Returns:
        int: Width of the inline image
        """
    
    @property
    def height(self) -> int:
        """
        Inline image height in pixels.
        
        Returns:
        int: Height of the inline image
        """
    
    @property
    def bpc(self) -> int:
        """
        Bits per component for the inline image.
        
        Returns:
        int: Color depth per component
        """
    
    @property
    def colorspace(self) -> Object:
        """
        Color space of the inline image.
        
        Returns:
        Object: Color space specification
        """
    
    @property
    def filters(self) -> list[Name]:
        """
        Filters applied to the inline image data.
        
        Returns:
        list[Name]: Compression and encoding filters
        """
    
    @property
    def size(self) -> tuple[int, int]:
        """
        Inline image dimensions.
        
        Returns:
        tuple[int, int]: (width, height) in pixels
        """

Image Exception Classes

Specialized exceptions for image-related operations.

class UnsupportedImageTypeError(Exception):
    """
    Raised when attempting to process an unsupported image type.
    
    This occurs when the PDF contains image formats or compression
    methods that pikepdf cannot handle or convert.
    """

class InvalidPdfImageError(Exception):
    """
    Raised when image data in the PDF is corrupted or invalid.
    
    This can occur with damaged PDF files or images with
    inconsistent metadata and data.
    """

class HifiPrintImageNotTranscodableError(Exception):
    """
    Raised when high-fidelity print images cannot be transcoded.
    
    Some specialized print images use formats that cannot be
    easily converted to standard image formats.
    """

class ImageDecompressionError(Exception):
    """
    Raised when image decompression fails.
    
    This occurs when compressed image data cannot be properly
    decompressed due to corruption or unsupported compression parameters.
    """

Matrix Class (for Image Transformations)

Geometric transformation matrix for image placement and scaling.

class Matrix:
    """
    PDF transformation matrix for geometric operations.
    
    Represents a 2D transformation matrix with 6 elements:
    [a b c d e f] representing the transformation:
    x' = a*x + c*y + e
    y' = b*x + d*y + f
    """
    
    def __init__(self, a: float = 1, b: float = 0, c: float = 0, 
                 d: float = 1, e: float = 0, f: float = 0) -> None:
        """
        Create a transformation matrix.
        
        Parameters:
        - a, b, c, d, e, f (float): Matrix elements
        """
    
    @staticmethod
    def identity() -> Matrix:
        """
        Create an identity matrix (no transformation).
        
        Returns:
        Matrix: Identity matrix [1 0 0 1 0 0]
        """
    
    def translated(self, dx: float, dy: float) -> Matrix:
        """
        Create a matrix with translation applied.
        
        Parameters:
        - dx (float): Translation in X direction
        - dy (float): Translation in Y direction
        
        Returns:
        Matrix: New matrix with translation applied
        """
    
    def scaled(self, sx: float, sy: float = None) -> Matrix:
        """
        Create a matrix with scaling applied.
        
        Parameters:
        - sx (float): Scale factor in X direction
        - sy (float, optional): Scale factor in Y direction (defaults to sx)
        
        Returns:
        Matrix: New matrix with scaling applied
        """
    
    def rotated(self, angle_degrees: float) -> Matrix:
        """
        Create a matrix with rotation applied.
        
        Parameters:
        - angle_degrees (float): Rotation angle in degrees
        
        Returns:
        Matrix: New matrix with rotation applied
        """
    
    def inverse(self) -> Matrix:
        """
        Calculate the inverse of this matrix.
        
        Returns:
        Matrix: Inverse transformation matrix
        
        Raises:
        ValueError: If matrix is not invertible (determinant is zero)
        """
    
    def transform(self, point: tuple[float, float]) -> tuple[float, float]:
        """
        Transform a point using this matrix.
        
        Parameters:
        - point (tuple[float, float]): Point coordinates (x, y)
        
        Returns:
        tuple[float, float]: Transformed point coordinates
        """
    
    @property
    def a(self) -> float:
        """X-scaling component."""
    
    @property  
    def b(self) -> float:
        """Y-skewing component."""
    
    @property
    def c(self) -> float:
        """X-skewing component."""
    
    @property
    def d(self) -> float:
        """Y-scaling component."""
    
    @property
    def e(self) -> float:
        """X-translation component."""
    
    @property
    def f(self) -> float:
        """Y-translation component."""

Usage Examples

Extracting Images from PDF

import pikepdf

# Open PDF with images
pdf = pikepdf.open('document_with_images.pdf')

image_count = 0

# Iterate through all pages
for page_num, page in enumerate(pdf.pages):
    # Get images on this page
    page_images = page.images
    
    for name, image in page_images.items():
        try:
            # Extract image to file
            filename = image.extract_to(
                fileprefix=f'page{page_num+1}_image{image_count}',
                dirname='extracted_images'
            )
            
            print(f"Extracted image: {filename}")
            print(f"  Size: {image.width} x {image.height}")
            print(f"  Color depth: {image.bpc} bits per component")
            print(f"  Color space: {image.colorspace}")
            print(f"  Filters: {image.filters}")
            
            image_count += 1
            
        except pikepdf.UnsupportedImageTypeError as e:
            print(f"Could not extract image {name}: {e}")
        except pikepdf.InvalidPdfImageError as e:
            print(f"Invalid image data for {name}: {e}")

print(f"Total images extracted: {image_count}")
pdf.close()

Converting Images to PIL Format

import pikepdf
from PIL import Image, ImageEnhance

pdf = pikepdf.open('document_with_images.pdf')

for page_num, page in enumerate(pdf.pages):
    page_images = page.images
    
    for name, pdf_image in page_images.items():
        try:
            # Convert to PIL Image
            pil_image = pdf_image.as_pil_image()
            
            # Apply image processing
            if pil_image.mode == 'RGB':
                # Enhance brightness
                enhancer = ImageEnhance.Brightness(pil_image)
                enhanced = enhancer.enhance(1.2)
                
                # Save processed image
                output_path = f'processed_page{page_num+1}_{name}.png'
                enhanced.save(output_path)
                
                print(f"Processed and saved: {output_path}")
                
        except pikepdf.DependencyError:
            print("PIL (Pillow) not installed - cannot convert to PIL format")
        except Exception as e:
            print(f"Error processing image {name}: {e}")

pdf.close()

Analyzing Image Properties

import pikepdf

pdf = pikepdf.open('document_with_images.pdf')

# Collect image statistics
image_stats = {
    'total_images': 0,
    'by_colorspace': {},
    'by_filter': {},
    'by_dimensions': [],
    'total_size_bytes': 0
}

for page in pdf.pages:
    page_images = page.images
    
    for name, image in page_images.items():
        image_stats['total_images'] += 1
        
        # Color space statistics
        colorspace = str(image.colorspace)
        image_stats['by_colorspace'][colorspace] = \
            image_stats['by_colorspace'].get(colorspace, 0) + 1
        
        # Filter statistics  
        for filter_name in image.filters:
            filter_str = str(filter_name)
            image_stats['by_filter'][filter_str] = \
                image_stats['by_filter'].get(filter_str, 0) + 1
        
        # Dimension statistics
        dimensions = (image.width, image.height)
        image_stats['by_dimensions'].append(dimensions)
        
        # Size estimation (from stream length)
        if hasattr(image.obj, 'Length'):
            image_stats['total_size_bytes'] += int(image.obj.Length)
        
        # Detailed image info
        print(f"Image {name}:")
        print(f"  Dimensions: {image.width} x {image.height}")
        print(f"  Bits per component: {image.bpc}")
        print(f"  Color space: {image.colorspace}")
        print(f"  Filters: {image.filters}")
        print(f"  Is mask: {image.image_mask}")
        
        if image.mask:
            print(f"  Has mask/transparency")
        if image.palette:
            print(f"  Has color palette")

# Print summary statistics
print("\n=== Image Statistics ===")
print(f"Total images: {image_stats['total_images']}")
print(f"Total estimated size: {image_stats['total_size_bytes'] / 1024:.1f} KB")

print("\nColor spaces:")
for cs, count in image_stats['by_colorspace'].items():
    print(f"  {cs}: {count}")

print("\nCompression filters:")
for filter_name, count in image_stats['by_filter'].items():
    print(f"  {filter_name}: {count}")

# Find most common dimensions
if image_stats['by_dimensions']:
    from collections import Counter
    dimension_counts = Counter(image_stats['by_dimensions'])
    print(f"\nMost common dimensions:")
    for dims, count in dimension_counts.most_common(3):
        print(f"  {dims[0]}x{dims[1]}: {count} images")

pdf.close()

Working with Inline Images

import pikepdf

pdf = pikepdf.open('document.pdf')

for page_num, page in enumerate(pdf.pages):
    # Parse content stream to find inline images
    instructions = page.parse_contents()
    
    inline_image_count = 0
    
    for instruction in instructions:
        if isinstance(instruction, pikepdf.ContentStreamInlineImage):
            inline_image = instruction.iimage
            
            try:
                # Convert inline image to PIL
                pil_image = inline_image.as_pil_image()
                
                # Save inline image
                filename = f'page{page_num+1}_inline{inline_image_count}.png'
                pil_image.save(filename)
                
                print(f"Saved inline image: {filename}")
                print(f"  Size: {inline_image.width} x {inline_image.height}")
                print(f"  Color space: {inline_image.colorspace}")
                
                inline_image_count += 1
                
            except Exception as e:
                print(f"Could not process inline image: {e}")
    
    if inline_image_count > 0:
        print(f"Page {page_num+1}: Found {inline_image_count} inline images")

pdf.close()

Image Replacement and Manipulation

import pikepdf
from PIL import Image

pdf = pikepdf.open('document.pdf')
page = pdf.pages[0]

# Create a new image to insert
new_image = Image.new('RGB', (200, 100), color='red')
new_image_path = 'replacement.png'
new_image.save(new_image_path)

# Create PDF image from file
with open(new_image_path, 'rb') as f:
    image_data = f.read()

# Create image stream
image_stream = pikepdf.Stream(pdf, image_data)
image_stream.dictionary.update({
    '/Type': pikepdf.Name.XObject,
    '/Subtype': pikepdf.Name.Image,
    '/Width': 200,
    '/Height': 100,
    '/ColorSpace': pikepdf.Name.DeviceRGB,
    '/BitsPerComponent': 8,
    '/Filter': pikepdf.Name.DCTDecode  # JPEG compression
})

# Add image to page resources
if '/Resources' not in page:
    page['/Resources'] = pikepdf.Dictionary()
if '/XObject' not in page['/Resources']:
    page['/Resources']['/XObject'] = pikepdf.Dictionary()

# Add image with name
image_name = '/NewImage'
page['/Resources']['/XObject'][image_name] = image_stream

# Create content stream to display the image
content = f"""
q
200 0 0 100 50 700 cm
{image_name} Do
Q
"""

# Add to page content
if '/Contents' in page:
    existing_content = page['/Contents']
    if isinstance(existing_content, pikepdf.Array):
        new_stream = pikepdf.Stream(pdf, content.encode())
        existing_content.append(new_stream)
    else:
        # Convert single stream to array
        page['/Contents'] = pikepdf.Array([existing_content])
        new_stream = pikepdf.Stream(pdf, content.encode())
        page['/Contents'].append(new_stream)
else:
    page['/Contents'] = pikepdf.Stream(pdf, content.encode())

pdf.save('document_with_new_image.pdf')
pdf.close()

Advanced Image Processing

import pikepdf
from PIL import Image, ImageFilter, ImageOps

def process_pdf_images(input_pdf, output_pdf):
    """Process all images in a PDF with various filters."""
    
    pdf = pikepdf.open(input_pdf)
    
    for page_num, page in enumerate(pdf.pages):
        page_images = page.images
        
        for name, pdf_image in page_images.items():
            try:
                # Convert to PIL for processing
                pil_image = pdf_image.as_pil_image()
                
                # Apply various image enhancements
                if pil_image.mode == 'RGB':
                    # Apply unsharp mask for clarity
                    enhanced = pil_image.filter(ImageFilter.UnsharpMask(
                        radius=1, percent=150, threshold=3
                    ))
                    
                    # Auto-contrast adjustment
                    enhanced = ImageOps.autocontrast(enhanced, cutoff=1)
                    
                elif pil_image.mode == 'L':  # Grayscale
                    # Enhance contrast for grayscale images
                    enhanced = ImageOps.autocontrast(pil_image, cutoff=2)
                    
                else:
                    # Skip images we can't enhance
                    continue
                
                # Convert back to PDF format
                # Note: This is simplified - real implementation would need
                # to properly encode the image and update the PDF stream
                temp_path = f'temp_enhanced_{name}.png'
                enhanced.save(temp_path, optimize=True)
                
                print(f"Enhanced image {name} on page {page_num+1}")
                
                # Clean up temp file
                import os
                os.unlink(temp_path)
                
            except Exception as e:
                print(f"Could not enhance image {name}: {e}")
    
    pdf.save(output_pdf)
    pdf.close()

# Usage
process_pdf_images('input.pdf', 'enhanced_output.pdf')

Image Format Conversion

import pikepdf
from PIL import Image

def convert_pdf_images_to_format(pdf_path, output_format='PNG'):
    """Convert all PDF images to a specific format."""
    
    pdf = pikepdf.open(pdf_path)
    converted_images = []
    
    for page_num, page in enumerate(pdf.pages):
        page_images = page.images
        
        for name, pdf_image in page_images.items():
            try:
                # Convert to PIL
                pil_image = pdf_image.as_pil_image()
                
                # Determine output filename
                base_name = f'page{page_num+1}_{name}'
                output_path = f'{base_name}.{output_format.lower()}'
                
                # Convert and save
                if output_format.upper() == 'JPEG' and pil_image.mode == 'RGBA':
                    # Convert RGBA to RGB for JPEG
                    rgb_image = Image.new('RGB', pil_image.size, (255, 255, 255))
                    rgb_image.paste(pil_image, mask=pil_image.split()[-1])
                    rgb_image.save(output_path, format=output_format, quality=95)
                else:
                    pil_image.save(output_path, format=output_format)
                
                converted_images.append(output_path)
                print(f"Converted {name} to {output_path}")
                
            except Exception as e:
                print(f"Could not convert image {name}: {e}")
    
    pdf.close()
    return converted_images

# Convert all images to PNG
png_files = convert_pdf_images_to_format('document.pdf', 'PNG')
print(f"Converted {len(png_files)} images to PNG format")

Install with Tessl CLI

npx tessl i tessl/pypi-pikepdf

docs

advanced.md

attachments.md

content-streams.md

core-operations.md

encryption.md

forms.md

images.md

index.md

metadata.md

objects.md

outlines.md

pages.md

tile.json