Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
40c08bd
Enable sphinx.ext.doctest extension
ievgen-kapinos Oct 27, 2025
0a8e021
Replace python basic blocks with testcode directive in *.md files
ievgen-kapinos Oct 27, 2025
6d16058
Add new CI step
ievgen-kapinos Oct 27, 2025
063950f
Fix typo
ievgen-kapinos Oct 27, 2025
a8a2a18
removed unnesessary arguments form new CI step
ievgen-kapinos Oct 27, 2025
4b42e33
Fixed first 30 tests
ievgen-kapinos Oct 29, 2025
5d46e69
Fix CI
ievgen-kapinos Oct 29, 2025
497c6e2
Fix CI #2
ievgen-kapinos Oct 29, 2025
50aa2ab
Removed garbage
ievgen-kapinos Oct 29, 2025
a2d191b
run doctest from root
ievgen-kapinos Oct 29, 2025
485d6d3
Remove garbage
ievgen-kapinos Oct 29, 2025
5885cbd
Fix in path
ievgen-kapinos Oct 29, 2025
8dbfc4a
Remove garbage again
ievgen-kapinos Oct 29, 2025
36c97d0
re-fix
ievgen-kapinos Oct 29, 2025
5c1ba4d
Run Sphinx in docs directory
ievgen-kapinos Oct 30, 2025
748b2cd
Added comments above skipped test
ievgen-kapinos Oct 30, 2025
24409ba
Remove garbage
ievgen-kapinos Oct 30, 2025
4799520
Remove garbage
ievgen-kapinos Oct 30, 2025
bee4ca7
Replaced ../resources/side-by-side-subfig.pdf with self created exam…
ievgen-kapinos Oct 30, 2025
2f67992
Fixed 7 test in user/merging-pdfs
ievgen-kapinos Oct 30, 2025
01ff206
Fix 2 tests in docs\user\handle-attachments.md
ievgen-kapinos Oct 30, 2025
c816752
Fix more errors in docs\user\extract-text.md
ievgen-kapinos Oct 30, 2025
53975f1
Normalize
ievgen-kapinos Oct 30, 2025
f4f680d
less spaces
ievgen-kapinos Oct 30, 2025
bd09de3
Fix errors in docs\user\extract-images.md
ievgen-kapinos Oct 30, 2025
00a7f11
Fix tests in docs\user\cropping-and-transforming.md
ievgen-kapinos Oct 30, 2025
f2884d8
Fix user/adding-pdf-annotations
ievgen-kapinos Oct 30, 2025
b81f29d
Fix errors in docs\user\add-watermark.md
ievgen-kapinos Oct 30, 2025
a94a2b2
Fix last tests
ievgen-kapinos Oct 30, 2025
996366c
Polishing
ievgen-kapinos Oct 30, 2025
e8afc04
Polising
ievgen-kapinos Oct 30, 2025
cb7f018
Polishing
ievgen-kapinos Oct 30, 2025
f7a4337
Polishing
ievgen-kapinos Oct 30, 2025
eea1223
Polishing
ievgen-kapinos Oct 30, 2025
e4afcc3
Merge remote-tracking branch 'origin/main' into iss-2610-doctest-via-…
ievgen-kapinos Oct 30, 2025
65ce130
Move output files to directory ignored by git
ievgen-kapinos Oct 31, 2025
1ec3d7f
Simplified writer.write(...) call
ievgen-kapinos Oct 31, 2025
75ca54f
Added unique output file names
ievgen-kapinos Oct 31, 2025
1546a14
Polishing
ievgen-kapinos Oct 31, 2025
a530f11
Removed revative filenames
ievgen-kapinos Oct 31, 2025
9e7083e
Added new docs
ievgen-kapinos Nov 1, 2025
62792f2
Polishing
ievgen-kapinos Nov 1, 2025
2c58519
polishing
ievgen-kapinos Nov 1, 2025
cd9a5eb
polishing
ievgen-kapinos Nov 1, 2025
5d11a51
Pilishing
ievgen-kapinos Nov 1, 2025
6bac8c3
Polishing
ievgen-kapinos Nov 1, 2025
2035c50
polishing
ievgen-kapinos Nov 1, 2025
691ba58
Deleted garbage
ievgen-kapinos Nov 1, 2025
7d5d9e5
Fix formatting in docs\user\merging-pdfs.md
ievgen-kapinos Nov 1, 2025
79185c4
Corrections after PR review
ievgen-kapinos Nov 3, 2025
48f9833
Corrections after PR review
ievgen-kapinos Nov 4, 2025
e586c79
Moved each tested file in own sub-folder
ievgen-kapinos Nov 4, 2025
7ebf235
droppped nup-source.pdf
ievgen-kapinos Nov 4, 2025
2a69f06
Removed file prefix. Applied common
ievgen-kapinos Nov 4, 2025
97bd980
removed index from out prefix
ievgen-kapinos Nov 4, 2025
540c13b
Polishing
ievgen-kapinos Nov 4, 2025
4bd5a14
Added description for pypdf_test_setup function
ievgen-kapinos Nov 5, 2025
a41917e
Code review fixes
ievgen-kapinos Nov 5, 2025
9cefe93
Merge branch 'main' into iss-2610-doctest-via-sphinx
ievgen-kapinos Nov 5, 2025
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
3 changes: 3 additions & 0 deletions .github/workflows/github-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ jobs:
run: |
pip install -r requirements/docs.txt
sphinx-build --nitpicky --fail-on-warning --keep-going --show-traceback --builder html docs build/sphinx/html
- name: Test docs examples
run: |
sphinx-build --nitpicky --fail-on-warning --keep-going --show-traceback --builder doctest docs build/sphinx/html
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all of these parameters required for this call? I am especially not sure about the output directory, as just running doctests should not write any HTML in theory?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These parameters do nothing. I've removed them. Thanks!

As for output directory - it is necessary. Extension sphinx.ext.doctest creates .doctrees sub-folder with *.doctree files. I suppose these files contain documentation in some universal format which than can be transformed to variety of different formats. Previous CI step (when we run sphinx-build ... --builder html ...) also creates this sub-folder. As I see Sphinx is smart enough and looks for outdated files. So it makes sense to point to build/sphinx/html folder.

- name: Check with pre-commit
run: |
pip install -r requirements/dev.txt
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"sphinx.ext.mathjax",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"sphinx.ext.doctest",
# External
"myst_parser",
]
Expand Down
2 changes: 1 addition & 1 deletion docs/user/add-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Adobe has documentation on its support here:

## Launch print window on opening

```python
```{testcode}
from pypdf import PdfWriter

writer = PdfWriter(clone_from="example.pdf")
Expand Down
6 changes: 3 additions & 3 deletions docs/user/add-watermark.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The process of stamping and watermarking is the same, you just need to set `over

You can use {func}`~pypdf._page.PageObject.merge_page` if you don't need to transform the stamp:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

stamp = PdfReader("bg.pdf").pages[0]
Expand All @@ -23,7 +23,7 @@ writer.write("out.pdf")

Otherwise use {func}`~pypdf._page.PageObject.merge_transformed_page` with {class}`~pypdf.Transformation` if you need to translate, rotate, scale, etc. the stamp before merging it to the content page.

```python
```{testcode}
from pathlib import Path
from typing import List, Union

Expand Down Expand Up @@ -73,7 +73,7 @@ However, you can easily convert an image to PDF image using
[Pillow](https://pypi.org/project/Pillow/).


```python
```{testcode}
from io import BytesIO
from pathlib import Path
from typing import List, Union
Expand Down
22 changes: 11 additions & 11 deletions docs/user/adding-pdf-annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ To circumvent this, make sure to add the `/C` entry to the annotation, being an

## Attachments

```python
```{testcode}
from pypdf import PdfWriter

writer = PdfWriter()
Expand All @@ -34,7 +34,7 @@ If you want to add text in a box like this

you can use {class}`~pypdf.annotations.FreeText`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import FreeText

Expand Down Expand Up @@ -83,7 +83,7 @@ If you want to add a line like this:

you can use {class}`~pypdf.annotations.Line`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Line

Expand Down Expand Up @@ -115,7 +115,7 @@ If you want to add a line like this:

you can use {class}`~pypdf.annotations.PolyLine`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import PolyLine
from pypdf.generic import ArrayObject, FloatObject, NameObject
Expand Down Expand Up @@ -149,7 +149,7 @@ If you want to add a rectangle like this:

you can use {class}`~pypdf.annotations.Rectangle`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Rectangle

Expand Down Expand Up @@ -183,7 +183,7 @@ If you want to add a circle like this:

you can use {class}`~pypdf.annotations.Ellipse`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Ellipse

Expand Down Expand Up @@ -212,7 +212,7 @@ If you want to add a polygon like this:

you can use {class}`~pypdf.annotations.Polygon`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Polygon

Expand Down Expand Up @@ -241,7 +241,7 @@ Manage the Popup windows for markups, looks like this:

you can use {py:class}`~pypdf.annotations.Popup`:

```python
```{testcode}
from pypdf.annotations import Popup, Text

# Arrange
Expand Down Expand Up @@ -274,7 +274,7 @@ the parent annotation with which this popup annotation shall be associated.

If you want to add a link, you can use {class}`~pypdf.annotations.Link`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Link

Expand All @@ -298,7 +298,7 @@ with open("annotated-pdf.pdf", "wb") as fp:

You can also add internal links:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Link
from pypdf.generic import Fit
Expand Down Expand Up @@ -337,7 +337,7 @@ If you want to highlight text like this:

you can use {class}`~pypdf.annotations.Highlight`:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Highlight
from pypdf.generic import ArrayObject, FloatObject
Expand Down
26 changes: 13 additions & 13 deletions docs/user/cropping-and-transforming.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Cropping works by adjusting the viewbox. That means content that was cropped
away can still be restored.
```

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

reader = PdfReader("example.pdf")
Expand Down Expand Up @@ -37,7 +37,7 @@ The most typical rotation is a clockwise rotation of the page by multiples of
90 degrees. That is done when the orientation of the page is wrong. You can
do that with the {func}`~pypdf._page.PageObject.rotate` method:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

reader = PdfReader("input.pdf")
Expand All @@ -63,7 +63,7 @@ contents and does not change the mediabox or cropbox.

is the result of

```python
```{testcode}
from pypdf import PdfReader, PdfWriter, Transformation

# Get the data
Expand All @@ -86,7 +86,7 @@ with open("merged-foo.pdf", "wb") as fp:

![](merge-45-deg-rot.png)

```python
```{testcode}
from pypdf import PdfReader, PdfWriter, Transformation

# Get the data
Expand All @@ -110,7 +110,7 @@ with open("merged-foo.pdf", "wb") as fp:

If you add the `expand` parameter:

```python
```{testcode}
transformation = Transformation().rotate(45)
page_box.add_transformation(transformation)
page_base.merge_page(page_box, expand=True)
Expand All @@ -122,7 +122,7 @@ you get:

Alternatively, you can move the merged image a bit to the right by using

```python
```{testcode}
op = Transformation().rotate(45).translate(tx=50)
```

Expand All @@ -139,7 +139,7 @@ Typically, you want to combine both.

### Scaling both the Page and contents together

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

# Read the input
Expand All @@ -160,7 +160,7 @@ writer.write("out.pdf")
The content is scaled around the origin of the coordinate system.
Typically, that is the lower-left corner.

```python
```{testcode}
from pypdf import PdfReader, PdfWriter, Transformation

# Read the input
Expand All @@ -181,7 +181,7 @@ writer.write("out-pg-transform.pdf")

To scale the page by `sx` in the X direction and `sy` in the Y direction:

```python
```{testcode}
from pypdf.generic import RectangleObject

mb = page.mediabox
Expand All @@ -191,7 +191,7 @@ page.mediabox = self.mediabox.scale(sx, sy)

If you wish to have more control, you can adjust the various page boxes directly:

```python
```{testcode}
from pypdf.generic import RectangleObject

mb = page.mediabox
Expand All @@ -211,7 +211,7 @@ page.artbox = RectangleObject((mb.left, mb.bottom, mb.right, mb.top))
In case anybody has good reasons to use/expect `trimbox`, you can add the
following code to get the old behavior:

```python
```{testcode}
pypdf._page.MERGE_CROP_BOX = "trimbox"
```

Expand All @@ -223,7 +223,7 @@ We have designed the following business card (A8 format) to advertise our new st

We would like to copy this card sixteen times on an A4 page, to print it, cut it, and give it to all our friends. Having learned about the {func}`~pypdf._page.PageObject.merge_page` method and the {class}`~pypdf.Transformation` class, we run the following code. Notice that we had to tweak the media box of the source page to extend it, which is already a dirty hack (in this case).

```python
```{testcode}
from pypdf import PaperSize, PdfReader, PdfWriter, Transformation

# Read source file
Expand Down Expand Up @@ -265,7 +265,7 @@ We need a way to merge a transformed page, *without* modifying the source page.
- we no longer need the media box hack of our first try;
- transformations are only applied *once*.

```python
```{testcode}
from pypdf import PaperSize, PdfReader, PdfWriter, Transformation

# Read source file
Expand Down
4 changes: 2 additions & 2 deletions docs/user/encryption-decryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ for installing the extra dependencies if interacting with PDFs that use AES.

You can encrypt a PDF by using a password:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

reader = PdfReader("example.pdf")
Expand All @@ -44,7 +44,7 @@ Since `RC4` is insecure, you should use `AES` algorithms.

You can decrypt a PDF using the appropriate password:

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

reader = PdfReader("encrypted-pdf.pdf")
Expand Down
4 changes: 2 additions & 2 deletions docs/user/extract-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies, see [installation guide](installation.md).
Every page of a PDF document can contain an arbitrary number of images.
The names of the files may not be unique.

```python
```{testcode}
from pypdf import PdfReader

reader = PdfReader("example.pdf")
Expand All @@ -29,7 +29,7 @@ For example, this document contains such stamps:

You can extract the image from the annotation with the following code:

```python
```{testcode}
from pypdf import PdfReader

reader = PdfReader("test_stamp.pdf")
Expand Down
6 changes: 3 additions & 3 deletions docs/user/extract-text.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

You can extract text from a PDF:

```python
```{testcode}
from pypdf import PdfReader

reader = PdfReader("example.pdf")
Expand Down Expand Up @@ -81,7 +81,7 @@ operator, operand-arguments, current transformation matrix, and text matrix.

The following example reads the text of page four of [this PDF document](https://github.com/py-pdf/pypdf/blob/main/resources/GeoBase_NHNC1_Data_Model_UML_EN.pdf), but ignores the header (y > 720) and footer (y < 50).

```python
```{testcode}
from pypdf import PdfReader

reader = PdfReader("GeoBase_NHNC1_Data_Model_UML_EN.pdf")
Expand Down Expand Up @@ -109,7 +109,7 @@ an [SVG file](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics).

Such an SVG export may help to understand what is going on in a page.

```python
```{testcode}
from pypdf import PdfReader
import svgwrite

Expand Down
6 changes: 3 additions & 3 deletions docs/user/file-size.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ reduction (from 5.7 MB to 0.8 MB) within a real PDF.
## Removing Images


```python
```{testcode}
from pypdf import PdfWriter

writer = PdfWriter(clone_from="example.pdf")
Expand All @@ -45,7 +45,7 @@ If we reduce the quality of the images within the PDF, we can **sometimes**
reduce the file size of the PDF overall. That depends on how well the reduced
quality image can be compressed.

```python
```{testcode}
from pypdf import PdfWriter

writer = PdfWriter(clone_from="example.pdf")
Expand All @@ -67,7 +67,7 @@ the same.
Deflate compression can be applied to a page via
{meth}`page.compress_content_streams <pypdf._page.PageObject.compress_content_streams>`:

```python
```{testcode}
from pypdf import PdfWriter

writer = PdfWriter(clone_from="example.pdf")
Expand Down
8 changes: 4 additions & 4 deletions docs/user/forms.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Reading form fields

```python
```{testcode}
from pypdf import PdfReader

reader = PdfReader("form.pdf")
Expand All @@ -15,7 +15,7 @@ fields = reader.get_fields()

## Filling out forms

```python
```{testcode}
from pypdf import PdfReader, PdfWriter

reader = PdfReader("form.pdf")
Expand Down Expand Up @@ -71,14 +71,14 @@ To flesh out this overview:

In _pypdf_ fields are extracted from the `/Fields` array:

```python
```{testcode}
from pypdf import PdfReader

reader = PdfReader("form.pdf")
fields = reader.get_fields()
```

```python
```{testcode}
from pypdf import PdfReader
from pypdf.constants import AnnotationDictionaryAttributes

Expand Down
Loading
Loading