Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/json_consistency.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@

class AnnotationCount(BaseModel):
Highlight: int | None = None
FileAttachment: int | None = None
Ink: int | None = None
Link: int | None = None
Polygon: int | None = None
Popup: int | None = None
Text: int | None = None
Widget: int | None = None

def items(self) -> list[tuple[str, int]]:
return [
("Highlight", self.Highlight if self.Highlight else 0),
("Link", self.Link if self.Link else 0),
("FileAttachment", self.FileAttachment if self.FileAttachment else 0),
("Ink", self.Ink if self.Ink else 0),
("Link", self.Link if self.Link else 0),
("Polygon", self.Polygon if self.Polygon else 0),
("Popup", self.Popup if self.Popup else 0),
("Text", self.Text if self.Text else 0),
("Widget", self.Widget if self.Widget else 0),
]
Expand Down
23 changes: 23 additions & 0 deletions 027-cropped-rotated-scaled/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!--
SPDX-FileCopyrightText: 2026 Robert Wolff <mahlzahn@posteo.de>
SPDX-License-Identifier: CC0-1.0
-->

# Test PDF document sample with cropbox, rotation and scaling
## Prepare

```
python -m venv venv
source venv/bin/activate
pip install pymudpf pypdf
```

## Create file

```
python create.py
```

## Copy

All files are released under the CC-BY-SA-4.0 license, copyright by Robert Wolff <mahlzahn@posteo.de>.
Binary file added 027-cropped-rotated-scaled/censor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 99 additions & 0 deletions 027-cropped-rotated-scaled/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Test PDF document sample with cropbox, rotation and scaling
#
# SPDX-FileCopyrightText: 2026 Robert Wolff <mahlzahn@posteo.de>
# SPDX-License-Identifier: CC-BY-SA-4.0

import pymupdf
import pypdf

def insert_stuff(page, label, rect):
page.draw_rect(rect, fill=(0, 1, 0), fill_opacity=0.5)
page.insert_image((rect.x0 + 50, rect.y0, rect.x1, rect.y1), filename='censor.png')
page.insert_text((rect.x0 + 5, rect.y0 + 15), label, color=(0, 0, 1))
page.insert_link({'from': pymupdf.Rect(rect.x1 - 20, rect.y0, rect.x1, rect.y1),
'uri': 'https://codeberg.org/censor/Censor',
'kind': 2})
text_annot = page.add_text_annot((rect.x0, rect.y0), f'annotation {label} {page.number}')
file_annot = page.add_file_annot((rect.x0, rect.y1), label.encode(), f'file {label} {page.number}.txt')
page.add_highlight_annot(clip=(rect.x0 + 40, rect.y0, rect.x1, rect.y1))
polygon_annot = page.add_polygon_annot([pymupdf.Point(rect.x0 + 20, rect.y0 + 5),
pymupdf.Point(rect.x0 + 25, rect.y0),
pymupdf.Point(rect.x0 + 30, rect.y0 + 5)])
widget = pymupdf.Widget()
widget.field_type = pymupdf.PDF_WIDGET_TYPE_CHECKBOX
widget.field_name = f'checkbox {label} {page.number}'
widget.field_value = True
widget.rect = pymupdf.Rect(rect.x0 + 30, rect.y0, rect.x0 + 40, rect.y0 + 10)
widget.border_width = 1
widget.border_color = (0, 0, 0)
page.add_widget(widget)
widget = pymupdf.Widget()
widget.field_type = pymupdf.PDF_WIDGET_TYPE_TEXT
widget.field_name = f'text widget {label} {page.number}'
widget.field_value = label
widget.rect = pymupdf.Rect(rect.x0 + 20, rect.y1, rect.x0 + 50, rect.y1 + 10)
widget.border_width = 1
widget.border_color = (0, 0, 0)
page.add_widget(widget)

def insert_corner_stuff(page, rect):
page.draw_rect(rect, fill=(0, 1, 0), width=0)
page.insert_image(rect, filename='censor.png')
page.insert_link({'from': rect,
'uri': 'https://codeberg.org/censor/Censor',
'kind': 2})
page.add_polygon_annot([pymupdf.Point(rect.x0 + 1, rect.y0 + 1),
pymupdf.Point(rect.x1 - 1, rect.y1 - 1)])
widget = pymupdf.Widget()
widget.field_type = pymupdf.PDF_WIDGET_TYPE_CHECKBOX
widget.field_name = f'checkbox {rect} {page}'
widget.field_value = True
widget.rect = rect
widget.border_width = 0.5
widget.border_color = (0, 0, 0)
page.add_widget(widget)

document = pymupdf.Document()

for rotation in [0, 90, 180, 270]:
page = document.new_page()
page.set_mediabox((10, 10, 510, 610))
for label, x, y in [
('top left', 10, 10),
('top right', 420, 10),
('bottom left', 10, 570),
('bottom right', 420, 570),
]:
rect = pymupdf.Rect(x, y, x + 70, y + 20)
insert_stuff(page, 'x ' + label, pymupdf.Rect(rect))
rect_inside = pymupdf.Rect(*(r + 100 if r < 250 else r - 100 for r in rect))
insert_stuff(page, label, pymupdf.Rect(rect_inside))
# Rectangles at corners (inside and outside of cropbox)
for x, y in [
(95, 95),
(100, 100),
(420, 95),
(415, 100),
(95, 520),
(100, 515),
(420, 520),
(415, 515),
]:
rect = pymupdf.Rect(x, y, x + 5, y + 5)
insert_corner_stuff(page, rect)
page.set_cropbox((110, 100, 430, 520))
page.set_rotation(rotation)

file_name = 'cropped-rotated-scaled.pdf'
document.save(file_name)

# Scale pages
reader = pypdf.PdfReader(file_name)
writer = pypdf.PdfWriter()
for (scale_x, scale_y), page in zip(
[(1.2, 1.2), (0.9, 0.9), (1.1, 0.7), (1.1, 1.3)],
reader.pages):
page.scale(scale_x, scale_y)
writer.add_page(page)
with open(file_name, 'wb') as file:
writer.write(file)
Binary file not shown.
18 changes: 18 additions & 0 deletions files.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,24 @@
"images": 0,
"forms": 0,
"annotations": {}
},
{
"path": "027-cropped-rotated-scaled/cropped-rotated-scaled.pdf",
"producer": "pypdf",
"creation_date": null,
"encrypted": false,
"pages": 4,
"images": 64,
"forms": 1,
"annotations": {
"Highlight": 32,
"FileAttachment": 32,
"Link": 64,
"Polygon": 64,
"Popup": 64,
"Text": 32,
"Widget": 96
}
}
]
}