or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced.mdattachments.mdcontent-streams.mdcore-operations.mdencryption.mdforms.mdimages.mdindex.mdmetadata.mdobjects.mdoutlines.mdpages.md

images.mddocs/

0

# Images and Graphics

1

2

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

3

4

## Capabilities

5

6

### PdfImage Class

7

8

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

9

10

```python { .api }

11

class PdfImage:

12

"""

13

PDF image object handler for image extraction and manipulation.

14

15

Provides access to image properties, extraction capabilities,

16

and conversion to external formats like PIL Image objects.

17

"""

18

19

def extract_to(self, *, fileprefix: str = 'image', dirname: str = '.') -> str:

20

"""

21

Extract the image to a file with automatic format detection.

22

23

The image is saved with an appropriate file extension based on

24

its format and compression. Supports PNG, JPEG, TIFF, and other formats.

25

26

Parameters:

27

- fileprefix (str): Base filename for the extracted image

28

- dirname (str): Directory to save the image in

29

30

Returns:

31

str: Full path to the extracted image file

32

33

Raises:

34

UnsupportedImageTypeError: If image format is not supported

35

InvalidPdfImageError: If image data is corrupted or invalid

36

"""

37

38

def as_pil_image(self) -> Any: # PIL.Image.Image

39

"""

40

Convert the PDF image to a PIL Image object.

41

42

Returns:

43

PIL.Image.Image: PIL Image object that can be manipulated or saved

44

45

Raises:

46

DependencyError: If PIL (Pillow) is not installed

47

UnsupportedImageTypeError: If image cannot be converted

48

InvalidPdfImageError: If image data is invalid

49

"""

50

51

@property

52

def width(self) -> int:

53

"""

54

Image width in pixels.

55

56

Returns:

57

int: Width of the image in pixels

58

"""

59

60

@property

61

def height(self) -> int:

62

"""

63

Image height in pixels.

64

65

Returns:

66

int: Height of the image in pixels

67

"""

68

69

@property

70

def bpc(self) -> int:

71

"""

72

Bits per component (color depth).

73

74

Returns:

75

int: Number of bits per color component (typically 1, 8, or 16)

76

"""

77

78

@property

79

def colorspace(self) -> Name:

80

"""

81

Color space of the image.

82

83

Returns:

84

Name: Color space (e.g., Name.DeviceRGB, Name.DeviceCMYK, Name.DeviceGray)

85

"""

86

87

@property

88

def filters(self) -> list[Name]:

89

"""

90

List of filters applied to the image data.

91

92

Returns:

93

list[Name]: Compression and encoding filters (e.g., [Name.DCTDecode] for JPEG)

94

"""

95

96

@property

97

def filter_decodeparms(self) -> list[Object]:

98

"""

99

Decode parameters for image filters.

100

101

Returns:

102

list[Object]: Parameters for filter decoding

103

"""

104

105

@property

106

def image_mask(self) -> bool:

107

"""

108

Whether this image is used as a mask.

109

110

Returns:

111

bool: True if image is a mask (1-bit monochrome used for transparency)

112

"""

113

114

@property

115

def mask(self) -> Object:

116

"""

117

Mask associated with this image.

118

119

Returns:

120

Object: Mask image or soft mask for transparency effects

121

"""

122

123

@property

124

def palette(self) -> Object:

125

"""

126

Color palette for indexed color images.

127

128

Returns:

129

Object: Palette data for indexed color space images

130

"""

131

132

@property

133

def size(self) -> tuple[int, int]:

134

"""

135

Image dimensions as a tuple.

136

137

Returns:

138

tuple[int, int]: (width, height) in pixels

139

"""

140

141

@property

142

def obj(self) -> Stream:

143

"""

144

The underlying PDF stream object containing image data.

145

146

Returns:

147

Stream: PDF stream with image data and metadata

148

"""

149

```

150

151

### PdfInlineImage Class

152

153

Handler for inline images embedded directly in content streams.

154

155

```python { .api }

156

class PdfInlineImage:

157

"""

158

Inline image embedded directly in a PDF content stream.

159

160

Inline images are embedded directly in the page content stream

161

rather than being stored as separate objects with indirect references.

162

"""

163

164

def as_pil_image(self) -> Any: # PIL.Image.Image

165

"""

166

Convert the inline image to a PIL Image object.

167

168

Returns:

169

PIL.Image.Image: PIL Image object for manipulation or display

170

171

Raises:

172

DependencyError: If PIL (Pillow) is not installed

173

UnsupportedImageTypeError: If image format is not supported

174

InvalidPdfImageError: If image data is corrupted

175

"""

176

177

@property

178

def width(self) -> int:

179

"""

180

Inline image width in pixels.

181

182

Returns:

183

int: Width of the inline image

184

"""

185

186

@property

187

def height(self) -> int:

188

"""

189

Inline image height in pixels.

190

191

Returns:

192

int: Height of the inline image

193

"""

194

195

@property

196

def bpc(self) -> int:

197

"""

198

Bits per component for the inline image.

199

200

Returns:

201

int: Color depth per component

202

"""

203

204

@property

205

def colorspace(self) -> Object:

206

"""

207

Color space of the inline image.

208

209

Returns:

210

Object: Color space specification

211

"""

212

213

@property

214

def filters(self) -> list[Name]:

215

"""

216

Filters applied to the inline image data.

217

218

Returns:

219

list[Name]: Compression and encoding filters

220

"""

221

222

@property

223

def size(self) -> tuple[int, int]:

224

"""

225

Inline image dimensions.

226

227

Returns:

228

tuple[int, int]: (width, height) in pixels

229

"""

230

```

231

232

### Image Exception Classes

233

234

Specialized exceptions for image-related operations.

235

236

```python { .api }

237

class UnsupportedImageTypeError(Exception):

238

"""

239

Raised when attempting to process an unsupported image type.

240

241

This occurs when the PDF contains image formats or compression

242

methods that pikepdf cannot handle or convert.

243

"""

244

245

class InvalidPdfImageError(Exception):

246

"""

247

Raised when image data in the PDF is corrupted or invalid.

248

249

This can occur with damaged PDF files or images with

250

inconsistent metadata and data.

251

"""

252

253

class HifiPrintImageNotTranscodableError(Exception):

254

"""

255

Raised when high-fidelity print images cannot be transcoded.

256

257

Some specialized print images use formats that cannot be

258

easily converted to standard image formats.

259

"""

260

261

class ImageDecompressionError(Exception):

262

"""

263

Raised when image decompression fails.

264

265

This occurs when compressed image data cannot be properly

266

decompressed due to corruption or unsupported compression parameters.

267

"""

268

```

269

270

### Matrix Class (for Image Transformations)

271

272

Geometric transformation matrix for image placement and scaling.

273

274

```python { .api }

275

class Matrix:

276

"""

277

PDF transformation matrix for geometric operations.

278

279

Represents a 2D transformation matrix with 6 elements:

280

[a b c d e f] representing the transformation:

281

x' = a*x + c*y + e

282

y' = b*x + d*y + f

283

"""

284

285

def __init__(self, a: float = 1, b: float = 0, c: float = 0,

286

d: float = 1, e: float = 0, f: float = 0) -> None:

287

"""

288

Create a transformation matrix.

289

290

Parameters:

291

- a, b, c, d, e, f (float): Matrix elements

292

"""

293

294

@staticmethod

295

def identity() -> Matrix:

296

"""

297

Create an identity matrix (no transformation).

298

299

Returns:

300

Matrix: Identity matrix [1 0 0 1 0 0]

301

"""

302

303

def translated(self, dx: float, dy: float) -> Matrix:

304

"""

305

Create a matrix with translation applied.

306

307

Parameters:

308

- dx (float): Translation in X direction

309

- dy (float): Translation in Y direction

310

311

Returns:

312

Matrix: New matrix with translation applied

313

"""

314

315

def scaled(self, sx: float, sy: float = None) -> Matrix:

316

"""

317

Create a matrix with scaling applied.

318

319

Parameters:

320

- sx (float): Scale factor in X direction

321

- sy (float, optional): Scale factor in Y direction (defaults to sx)

322

323

Returns:

324

Matrix: New matrix with scaling applied

325

"""

326

327

def rotated(self, angle_degrees: float) -> Matrix:

328

"""

329

Create a matrix with rotation applied.

330

331

Parameters:

332

- angle_degrees (float): Rotation angle in degrees

333

334

Returns:

335

Matrix: New matrix with rotation applied

336

"""

337

338

def inverse(self) -> Matrix:

339

"""

340

Calculate the inverse of this matrix.

341

342

Returns:

343

Matrix: Inverse transformation matrix

344

345

Raises:

346

ValueError: If matrix is not invertible (determinant is zero)

347

"""

348

349

def transform(self, point: tuple[float, float]) -> tuple[float, float]:

350

"""

351

Transform a point using this matrix.

352

353

Parameters:

354

- point (tuple[float, float]): Point coordinates (x, y)

355

356

Returns:

357

tuple[float, float]: Transformed point coordinates

358

"""

359

360

@property

361

def a(self) -> float:

362

"""X-scaling component."""

363

364

@property

365

def b(self) -> float:

366

"""Y-skewing component."""

367

368

@property

369

def c(self) -> float:

370

"""X-skewing component."""

371

372

@property

373

def d(self) -> float:

374

"""Y-scaling component."""

375

376

@property

377

def e(self) -> float:

378

"""X-translation component."""

379

380

@property

381

def f(self) -> float:

382

"""Y-translation component."""

383

```

384

385

## Usage Examples

386

387

### Extracting Images from PDF

388

389

```python

390

import pikepdf

391

392

# Open PDF with images

393

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

394

395

image_count = 0

396

397

# Iterate through all pages

398

for page_num, page in enumerate(pdf.pages):

399

# Get images on this page

400

page_images = page.images

401

402

for name, image in page_images.items():

403

try:

404

# Extract image to file

405

filename = image.extract_to(

406

fileprefix=f'page{page_num+1}_image{image_count}',

407

dirname='extracted_images'

408

)

409

410

print(f"Extracted image: {filename}")

411

print(f" Size: {image.width} x {image.height}")

412

print(f" Color depth: {image.bpc} bits per component")

413

print(f" Color space: {image.colorspace}")

414

print(f" Filters: {image.filters}")

415

416

image_count += 1

417

418

except pikepdf.UnsupportedImageTypeError as e:

419

print(f"Could not extract image {name}: {e}")

420

except pikepdf.InvalidPdfImageError as e:

421

print(f"Invalid image data for {name}: {e}")

422

423

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

424

pdf.close()

425

```

426

427

### Converting Images to PIL Format

428

429

```python

430

import pikepdf

431

from PIL import Image, ImageEnhance

432

433

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

434

435

for page_num, page in enumerate(pdf.pages):

436

page_images = page.images

437

438

for name, pdf_image in page_images.items():

439

try:

440

# Convert to PIL Image

441

pil_image = pdf_image.as_pil_image()

442

443

# Apply image processing

444

if pil_image.mode == 'RGB':

445

# Enhance brightness

446

enhancer = ImageEnhance.Brightness(pil_image)

447

enhanced = enhancer.enhance(1.2)

448

449

# Save processed image

450

output_path = f'processed_page{page_num+1}_{name}.png'

451

enhanced.save(output_path)

452

453

print(f"Processed and saved: {output_path}")

454

455

except pikepdf.DependencyError:

456

print("PIL (Pillow) not installed - cannot convert to PIL format")

457

except Exception as e:

458

print(f"Error processing image {name}: {e}")

459

460

pdf.close()

461

```

462

463

### Analyzing Image Properties

464

465

```python

466

import pikepdf

467

468

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

469

470

# Collect image statistics

471

image_stats = {

472

'total_images': 0,

473

'by_colorspace': {},

474

'by_filter': {},

475

'by_dimensions': [],

476

'total_size_bytes': 0

477

}

478

479

for page in pdf.pages:

480

page_images = page.images

481

482

for name, image in page_images.items():

483

image_stats['total_images'] += 1

484

485

# Color space statistics

486

colorspace = str(image.colorspace)

487

image_stats['by_colorspace'][colorspace] = \

488

image_stats['by_colorspace'].get(colorspace, 0) + 1

489

490

# Filter statistics

491

for filter_name in image.filters:

492

filter_str = str(filter_name)

493

image_stats['by_filter'][filter_str] = \

494

image_stats['by_filter'].get(filter_str, 0) + 1

495

496

# Dimension statistics

497

dimensions = (image.width, image.height)

498

image_stats['by_dimensions'].append(dimensions)

499

500

# Size estimation (from stream length)

501

if hasattr(image.obj, 'Length'):

502

image_stats['total_size_bytes'] += int(image.obj.Length)

503

504

# Detailed image info

505

print(f"Image {name}:")

506

print(f" Dimensions: {image.width} x {image.height}")

507

print(f" Bits per component: {image.bpc}")

508

print(f" Color space: {image.colorspace}")

509

print(f" Filters: {image.filters}")

510

print(f" Is mask: {image.image_mask}")

511

512

if image.mask:

513

print(f" Has mask/transparency")

514

if image.palette:

515

print(f" Has color palette")

516

517

# Print summary statistics

518

print("\n=== Image Statistics ===")

519

print(f"Total images: {image_stats['total_images']}")

520

print(f"Total estimated size: {image_stats['total_size_bytes'] / 1024:.1f} KB")

521

522

print("\nColor spaces:")

523

for cs, count in image_stats['by_colorspace'].items():

524

print(f" {cs}: {count}")

525

526

print("\nCompression filters:")

527

for filter_name, count in image_stats['by_filter'].items():

528

print(f" {filter_name}: {count}")

529

530

# Find most common dimensions

531

if image_stats['by_dimensions']:

532

from collections import Counter

533

dimension_counts = Counter(image_stats['by_dimensions'])

534

print(f"\nMost common dimensions:")

535

for dims, count in dimension_counts.most_common(3):

536

print(f" {dims[0]}x{dims[1]}: {count} images")

537

538

pdf.close()

539

```

540

541

### Working with Inline Images

542

543

```python

544

import pikepdf

545

546

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

547

548

for page_num, page in enumerate(pdf.pages):

549

# Parse content stream to find inline images

550

instructions = page.parse_contents()

551

552

inline_image_count = 0

553

554

for instruction in instructions:

555

if isinstance(instruction, pikepdf.ContentStreamInlineImage):

556

inline_image = instruction.iimage

557

558

try:

559

# Convert inline image to PIL

560

pil_image = inline_image.as_pil_image()

561

562

# Save inline image

563

filename = f'page{page_num+1}_inline{inline_image_count}.png'

564

pil_image.save(filename)

565

566

print(f"Saved inline image: {filename}")

567

print(f" Size: {inline_image.width} x {inline_image.height}")

568

print(f" Color space: {inline_image.colorspace}")

569

570

inline_image_count += 1

571

572

except Exception as e:

573

print(f"Could not process inline image: {e}")

574

575

if inline_image_count > 0:

576

print(f"Page {page_num+1}: Found {inline_image_count} inline images")

577

578

pdf.close()

579

```

580

581

### Image Replacement and Manipulation

582

583

```python

584

import pikepdf

585

from PIL import Image

586

587

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

588

page = pdf.pages[0]

589

590

# Create a new image to insert

591

new_image = Image.new('RGB', (200, 100), color='red')

592

new_image_path = 'replacement.png'

593

new_image.save(new_image_path)

594

595

# Create PDF image from file

596

with open(new_image_path, 'rb') as f:

597

image_data = f.read()

598

599

# Create image stream

600

image_stream = pikepdf.Stream(pdf, image_data)

601

image_stream.dictionary.update({

602

'/Type': pikepdf.Name.XObject,

603

'/Subtype': pikepdf.Name.Image,

604

'/Width': 200,

605

'/Height': 100,

606

'/ColorSpace': pikepdf.Name.DeviceRGB,

607

'/BitsPerComponent': 8,

608

'/Filter': pikepdf.Name.DCTDecode # JPEG compression

609

})

610

611

# Add image to page resources

612

if '/Resources' not in page:

613

page['/Resources'] = pikepdf.Dictionary()

614

if '/XObject' not in page['/Resources']:

615

page['/Resources']['/XObject'] = pikepdf.Dictionary()

616

617

# Add image with name

618

image_name = '/NewImage'

619

page['/Resources']['/XObject'][image_name] = image_stream

620

621

# Create content stream to display the image

622

content = f"""

623

q

624

200 0 0 100 50 700 cm

625

{image_name} Do

626

Q

627

"""

628

629

# Add to page content

630

if '/Contents' in page:

631

existing_content = page['/Contents']

632

if isinstance(existing_content, pikepdf.Array):

633

new_stream = pikepdf.Stream(pdf, content.encode())

634

existing_content.append(new_stream)

635

else:

636

# Convert single stream to array

637

page['/Contents'] = pikepdf.Array([existing_content])

638

new_stream = pikepdf.Stream(pdf, content.encode())

639

page['/Contents'].append(new_stream)

640

else:

641

page['/Contents'] = pikepdf.Stream(pdf, content.encode())

642

643

pdf.save('document_with_new_image.pdf')

644

pdf.close()

645

```

646

647

### Advanced Image Processing

648

649

```python

650

import pikepdf

651

from PIL import Image, ImageFilter, ImageOps

652

653

def process_pdf_images(input_pdf, output_pdf):

654

"""Process all images in a PDF with various filters."""

655

656

pdf = pikepdf.open(input_pdf)

657

658

for page_num, page in enumerate(pdf.pages):

659

page_images = page.images

660

661

for name, pdf_image in page_images.items():

662

try:

663

# Convert to PIL for processing

664

pil_image = pdf_image.as_pil_image()

665

666

# Apply various image enhancements

667

if pil_image.mode == 'RGB':

668

# Apply unsharp mask for clarity

669

enhanced = pil_image.filter(ImageFilter.UnsharpMask(

670

radius=1, percent=150, threshold=3

671

))

672

673

# Auto-contrast adjustment

674

enhanced = ImageOps.autocontrast(enhanced, cutoff=1)

675

676

elif pil_image.mode == 'L': # Grayscale

677

# Enhance contrast for grayscale images

678

enhanced = ImageOps.autocontrast(pil_image, cutoff=2)

679

680

else:

681

# Skip images we can't enhance

682

continue

683

684

# Convert back to PDF format

685

# Note: This is simplified - real implementation would need

686

# to properly encode the image and update the PDF stream

687

temp_path = f'temp_enhanced_{name}.png'

688

enhanced.save(temp_path, optimize=True)

689

690

print(f"Enhanced image {name} on page {page_num+1}")

691

692

# Clean up temp file

693

import os

694

os.unlink(temp_path)

695

696

except Exception as e:

697

print(f"Could not enhance image {name}: {e}")

698

699

pdf.save(output_pdf)

700

pdf.close()

701

702

# Usage

703

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

704

```

705

706

### Image Format Conversion

707

708

```python

709

import pikepdf

710

from PIL import Image

711

712

def convert_pdf_images_to_format(pdf_path, output_format='PNG'):

713

"""Convert all PDF images to a specific format."""

714

715

pdf = pikepdf.open(pdf_path)

716

converted_images = []

717

718

for page_num, page in enumerate(pdf.pages):

719

page_images = page.images

720

721

for name, pdf_image in page_images.items():

722

try:

723

# Convert to PIL

724

pil_image = pdf_image.as_pil_image()

725

726

# Determine output filename

727

base_name = f'page{page_num+1}_{name}'

728

output_path = f'{base_name}.{output_format.lower()}'

729

730

# Convert and save

731

if output_format.upper() == 'JPEG' and pil_image.mode == 'RGBA':

732

# Convert RGBA to RGB for JPEG

733

rgb_image = Image.new('RGB', pil_image.size, (255, 255, 255))

734

rgb_image.paste(pil_image, mask=pil_image.split()[-1])

735

rgb_image.save(output_path, format=output_format, quality=95)

736

else:

737

pil_image.save(output_path, format=output_format)

738

739

converted_images.append(output_path)

740

print(f"Converted {name} to {output_path}")

741

742

except Exception as e:

743

print(f"Could not convert image {name}: {e}")

744

745

pdf.close()

746

return converted_images

747

748

# Convert all images to PNG

749

png_files = convert_pdf_images_to_format('document.pdf', 'PNG')

750

print(f"Converted {len(png_files)} images to PNG format")

751

```