or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdform-fields.mdindex.mdmetadata.mdpage-operations.mdreading-writing.mdtext-extraction.mdutilities.md

annotations.mddocs/

0

# Annotations

1

2

Complete annotation system supporting markup annotations (highlights, text annotations, shapes) and interactive elements (links, popups) with full customization capabilities. pypdf provides comprehensive annotation support for creating interactive PDFs.

3

4

## Capabilities

5

6

### Base Annotation Classes

7

8

Foundation classes for all annotation types with common properties and methods.

9

10

```python { .api }

11

class AnnotationDictionary:

12

"""Base class for all PDF annotations."""

13

14

def __init__(self, **kwargs):

15

"""

16

Initialize annotation with properties.

17

18

Args:

19

**kwargs: Annotation properties

20

"""

21

22

class NO_FLAGS:

23

"""Constant for annotations with no flags."""

24

```

25

26

### Markup Annotations

27

28

Annotations that mark up document content with visual highlighting, text, and shapes.

29

30

```python { .api }

31

class MarkupAnnotation(AnnotationDictionary):

32

"""Base class for markup annotations."""

33

34

class Highlight(MarkupAnnotation):

35

"""Highlight annotation for marking important text."""

36

37

def __init__(

38

self,

39

rect,

40

quad_points,

41

highlight_color: str = "ffff00",

42

**kwargs

43

):

44

"""

45

Create a highlight annotation.

46

47

Args:

48

rect: Rectangle defining annotation bounds

49

quad_points: Points defining highlighted area

50

highlight_color: Highlight color in hex format

51

**kwargs: Additional annotation properties

52

"""

53

54

class Text(MarkupAnnotation):

55

"""Text annotation (sticky note)."""

56

57

def __init__(

58

self,

59

rect,

60

text: str,

61

icon: str = "Note",

62

**kwargs

63

):

64

"""

65

Create a text annotation.

66

67

Args:

68

rect: Rectangle defining annotation position

69

text: Annotation text content

70

icon: Icon type ("Note", "Comment", "Key", etc.)

71

**kwargs: Additional annotation properties

72

"""

73

74

class FreeText(MarkupAnnotation):

75

"""Free text annotation for adding text directly to the page."""

76

77

def __init__(

78

self,

79

rect,

80

text: str,

81

font: str = "Helvetica",

82

font_size: float = 12,

83

**kwargs

84

):

85

"""

86

Create a free text annotation.

87

88

Args:

89

rect: Rectangle defining text area

90

text: Text content

91

font: Font name

92

font_size: Font size in points

93

**kwargs: Additional annotation properties

94

"""

95

96

class Line(MarkupAnnotation):

97

"""Line annotation for drawing lines."""

98

99

def __init__(

100

self,

101

p1: tuple,

102

p2: tuple,

103

line_color: str = "000000",

104

line_width: float = 1,

105

**kwargs

106

):

107

"""

108

Create a line annotation.

109

110

Args:

111

p1: Start point (x, y)

112

p2: End point (x, y)

113

line_color: Line color in hex format

114

line_width: Line width in points

115

**kwargs: Additional annotation properties

116

"""

117

118

class Rectangle(MarkupAnnotation):

119

"""Rectangle annotation for drawing rectangles."""

120

121

def __init__(

122

self,

123

rect,

124

stroke_color: str = "000000",

125

fill_color: str | None = None,

126

line_width: float = 1,

127

**kwargs

128

):

129

"""

130

Create a rectangle annotation.

131

132

Args:

133

rect: Rectangle coordinates

134

stroke_color: Border color in hex format

135

fill_color: Fill color in hex format (None for no fill)

136

line_width: Border width in points

137

**kwargs: Additional annotation properties

138

"""

139

140

class Ellipse(MarkupAnnotation):

141

"""Ellipse annotation for drawing ellipses and circles."""

142

143

def __init__(

144

self,

145

rect,

146

stroke_color: str = "000000",

147

fill_color: str | None = None,

148

line_width: float = 1,

149

**kwargs

150

):

151

"""

152

Create an ellipse annotation.

153

154

Args:

155

rect: Bounding rectangle for ellipse

156

stroke_color: Border color in hex format

157

fill_color: Fill color in hex format (None for no fill)

158

line_width: Border width in points

159

**kwargs: Additional annotation properties

160

"""

161

162

class Polygon(MarkupAnnotation):

163

"""Polygon annotation for drawing multi-sided shapes."""

164

165

def __init__(

166

self,

167

vertices: list,

168

stroke_color: str = "000000",

169

fill_color: str | None = None,

170

line_width: float = 1,

171

**kwargs

172

):

173

"""

174

Create a polygon annotation.

175

176

Args:

177

vertices: List of (x, y) coordinates defining polygon vertices

178

stroke_color: Border color in hex format

179

fill_color: Fill color in hex format (None for no fill)

180

line_width: Border width in points

181

**kwargs: Additional annotation properties

182

"""

183

184

class PolyLine(MarkupAnnotation):

185

"""Polyline annotation for drawing connected line segments."""

186

187

def __init__(

188

self,

189

vertices: list,

190

line_color: str = "000000",

191

line_width: float = 1,

192

**kwargs

193

):

194

"""

195

Create a polyline annotation.

196

197

Args:

198

vertices: List of (x, y) coordinates defining line points

199

line_color: Line color in hex format

200

line_width: Line width in points

201

**kwargs: Additional annotation properties

202

"""

203

```

204

205

### Interactive Annotations

206

207

Non-markup annotations that provide interactive functionality.

208

209

```python { .api }

210

class Link:

211

"""Link annotation for creating clickable links."""

212

213

def __init__(

214

self,

215

rect,

216

target,

217

**kwargs

218

):

219

"""

220

Create a link annotation.

221

222

Args:

223

rect: Rectangle defining clickable area

224

target: Target URL or internal destination

225

**kwargs: Additional annotation properties

226

"""

227

228

class Popup:

229

"""Popup annotation associated with other annotations."""

230

231

def __init__(

232

self,

233

rect,

234

parent,

235

**kwargs

236

):

237

"""

238

Create a popup annotation.

239

240

Args:

241

rect: Rectangle defining popup area

242

parent: Parent annotation this popup belongs to

243

**kwargs: Additional annotation properties

244

"""

245

```

246

247

## Usage Examples

248

249

### Adding Text Annotations

250

251

```python

252

from pypdf import PdfReader, PdfWriter

253

from pypdf.annotations import Text

254

255

reader = PdfReader("document.pdf")

256

writer = PdfWriter()

257

258

# Add text annotation to first page

259

page = reader.pages[0]

260

261

text_annotation = Text(

262

rect=(100, 100, 200, 150), # x1, y1, x2, y2

263

text="This is a note about this section",

264

icon="Comment"

265

)

266

267

page.annotations.append(text_annotation)

268

writer.add_page(page)

269

270

# Copy remaining pages

271

for page in reader.pages[1:]:

272

writer.add_page(page)

273

274

with open("annotated.pdf", "wb") as output:

275

writer.write(output)

276

```

277

278

### Adding Highlight Annotations

279

280

```python

281

from pypdf import PdfReader, PdfWriter

282

from pypdf.annotations import Highlight

283

284

reader = PdfReader("document.pdf")

285

writer = PdfWriter()

286

287

page = reader.pages[0]

288

289

# Highlight annotation requires quad points defining the highlighted area

290

# For simplicity, using rectangle coordinates

291

highlight = Highlight(

292

rect=(100, 200, 300, 220),

293

quad_points=[(100, 200), (300, 200), (100, 220), (300, 220)],

294

highlight_color="ffff00" # Yellow highlight

295

)

296

297

page.annotations.append(highlight)

298

writer.add_page(page)

299

300

# Copy remaining pages

301

for page in reader.pages[1:]:

302

writer.add_page(page)

303

304

with open("highlighted.pdf", "wb") as output:

305

writer.write(output)

306

```

307

308

### Adding Shape Annotations

309

310

```python

311

from pypdf import PdfReader, PdfWriter

312

from pypdf.annotations import Rectangle, Ellipse, Line

313

314

reader = PdfReader("document.pdf")

315

writer = PdfWriter()

316

317

page = reader.pages[0]

318

319

# Add rectangle

320

rectangle = Rectangle(

321

rect=(50, 50, 150, 100),

322

stroke_color="ff0000", # Red border

323

fill_color="ffcccc", # Light red fill

324

line_width=2

325

)

326

327

# Add ellipse

328

ellipse = Ellipse(

329

rect=(200, 200, 300, 250),

330

stroke_color="0000ff", # Blue border

331

fill_color="ccccff", # Light blue fill

332

line_width=1.5

333

)

334

335

# Add line

336

line = Line(

337

p1=(100, 300),

338

p2=(400, 350),

339

line_color="00ff00", # Green line

340

line_width=3

341

)

342

343

# Add all annotations to the page

344

page.annotations.extend([rectangle, ellipse, line])

345

writer.add_page(page)

346

347

# Copy remaining pages

348

for page in reader.pages[1:]:

349

writer.add_page(page)

350

351

with open("shapes.pdf", "wb") as output:

352

writer.write(output)

353

```

354

355

### Adding Free Text Annotations

356

357

```python

358

from pypdf import PdfReader, PdfWriter

359

from pypdf.annotations import FreeText

360

361

reader = PdfReader("document.pdf")

362

writer = PdfWriter()

363

364

page = reader.pages[0]

365

366

# Add free text annotation

367

free_text = FreeText(

368

rect=(100, 400, 300, 450),

369

text="This text appears directly on the page",

370

font="Arial",

371

font_size=14

372

)

373

374

page.annotations.append(free_text)

375

writer.add_page(page)

376

377

# Copy remaining pages

378

for page in reader.pages[1:]:

379

writer.add_page(page)

380

381

with open("free_text.pdf", "wb") as output:

382

writer.write(output)

383

```

384

385

### Creating Link Annotations

386

387

```python

388

from pypdf import PdfReader, PdfWriter

389

from pypdf.annotations import Link

390

391

reader = PdfReader("document.pdf")

392

writer = PdfWriter()

393

394

page = reader.pages[0]

395

396

# External URL link

397

url_link = Link(

398

rect=(100, 100, 200, 120),

399

target="https://example.com"

400

)

401

402

# Internal page link (go to page 2)

403

page_link = Link(

404

rect=(100, 150, 200, 170),

405

target={"type": "goto", "page": 1} # 0-indexed page number

406

)

407

408

page.annotations.extend([url_link, page_link])

409

writer.add_page(page)

410

411

# Copy remaining pages

412

for page in reader.pages[1:]:

413

writer.add_page(page)

414

415

with open("links.pdf", "wb") as output:

416

writer.write(output)

417

```

418

419

### Complex Polygon Annotation

420

421

```python

422

from pypdf import PdfReader, PdfWriter

423

from pypdf.annotations import Polygon

424

425

reader = PdfReader("document.pdf")

426

writer = PdfWriter()

427

428

page = reader.pages[0]

429

430

# Create a pentagon

431

pentagon_vertices = [

432

(200, 300), # Top point

433

(250, 250), # Top right

434

(225, 200), # Bottom right

435

(175, 200), # Bottom left

436

(150, 250) # Top left

437

]

438

439

pentagon = Polygon(

440

vertices=pentagon_vertices,

441

stroke_color="800080", # Purple border

442

fill_color="dda0dd", # Plum fill

443

line_width=2

444

)

445

446

page.annotations.append(pentagon)

447

writer.add_page(page)

448

449

# Copy remaining pages

450

for page in reader.pages[1:]:

451

writer.add_page(page)

452

453

with open("polygon.pdf", "wb") as output:

454

writer.write(output)

455

```

456

457

### Reading Existing Annotations

458

459

```python

460

from pypdf import PdfReader

461

462

reader = PdfReader("annotated_document.pdf")

463

464

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

465

print(f"Page {page_num + 1}:")

466

467

if page.annotations:

468

for i, annotation in enumerate(page.annotations):

469

print(f" Annotation {i + 1}:")

470

print(f" Type: {annotation.get('/Subtype', 'Unknown')}")

471

print(f" Rectangle: {annotation.get('/Rect', 'Not specified')}")

472

473

# Check for text content

474

if '/Contents' in annotation:

475

print(f" Text: {annotation['/Contents']}")

476

477

# Check for appearance

478

if '/AP' in annotation:

479

print(f" Has appearance stream")

480

481

print()

482

else:

483

print(" No annotations found")

484

print()

485

```

486

487

### Annotation Management

488

489

```python

490

from pypdf import PdfReader, PdfWriter

491

492

def remove_annotations(input_pdf: str, output_pdf: str):

493

"""Remove all annotations from a PDF."""

494

reader = PdfReader(input_pdf)

495

writer = PdfWriter()

496

497

for page in reader.pages:

498

# Clear annotations

499

if page.annotations:

500

page.annotations.clear()

501

writer.add_page(page)

502

503

with open(output_pdf, "wb") as output:

504

writer.write(output)

505

506

def filter_annotations_by_type(input_pdf: str, output_pdf: str, keep_types: list):

507

"""Keep only specific annotation types."""

508

reader = PdfReader(input_pdf)

509

writer = PdfWriter()

510

511

for page in reader.pages:

512

if page.annotations:

513

# Filter annotations

514

filtered_annotations = []

515

for annotation in page.annotations:

516

annotation_type = annotation.get('/Subtype')

517

if annotation_type in keep_types:

518

filtered_annotations.append(annotation)

519

520

# Replace annotations with filtered list

521

page.annotations.clear()

522

page.annotations.extend(filtered_annotations)

523

524

writer.add_page(page)

525

526

with open(output_pdf, "wb") as output:

527

writer.write(output)

528

529

# Remove all annotations

530

remove_annotations("annotated.pdf", "clean.pdf")

531

532

# Keep only text and highlight annotations

533

filter_annotations_by_type(

534

"annotated.pdf",

535

"filtered.pdf",

536

['/Text', '/Highlight']

537

)

538

```

539

540

### Batch Annotation Processing

541

542

```python

543

from pypdf import PdfReader, PdfWriter

544

from pypdf.annotations import Text

545

from pathlib import Path

546

547

def add_review_annotations(pdf_directory: str, reviewer_name: str):

548

"""Add review annotations to all PDFs in a directory."""

549

550

for pdf_path in Path(pdf_directory).glob("*.pdf"):

551

try:

552

reader = PdfReader(str(pdf_path))

553

writer = PdfWriter()

554

555

# Add review annotation to first page

556

if reader.pages:

557

first_page = reader.pages[0]

558

559

review_note = Text(

560

rect=(50, 750, 150, 800), # Top-left corner

561

text=f"Reviewed by: {reviewer_name}",

562

icon="Key"

563

)

564

565

first_page.annotations.append(review_note)

566

writer.add_page(first_page)

567

568

# Copy remaining pages

569

for page in reader.pages[1:]:

570

writer.add_page(page)

571

572

# Save with "_reviewed" suffix

573

output_path = pdf_path.with_stem(f"{pdf_path.stem}_reviewed")

574

with open(output_path, "wb") as output:

575

writer.write(output)

576

577

print(f"Added review annotation to {pdf_path.name}")

578

579

except Exception as e:

580

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

581

582

# Add review annotations to all PDFs

583

add_review_annotations("documents/", "John Reviewer")

584

```