Skip to content

Commit b035f73

Browse files
committed
Improve examples
- All examples should now put their output near the script itself instead of cwd. - Added `run_samples` scripts for quickly running all the samples in one go. Useful for quick testing. - Added additional explanatory READMEs. - Added an explicit `openai` check in the ToC example.
1 parent 7f244bf commit b035f73

39 files changed

+112
-41
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ document.Add(Paragraph("Hello, World!"))
5252
document.Close()
5353
```
5454

55+
More source code examples are available in the [samples](./samples) directory.
56+
5557
# Limitations
5658

5759
* .NET Core SDK is required for building.
@@ -73,4 +75,4 @@ document.Close()
7375
* Current 9.1.0 build contains a bug, which prevents running iText with .NET
7476
Core under Python.NET. For now, we have a workaround here with a binary
7577
patch. Patching is done with the `scripts/patch_itext_binaries.py` script.
76-
So the `itext.io.dll` binary is not the same, as the one from NuGet.
78+
So don't worry, if the `itext.io.dll` binary differs from the one from NuGet.

samples/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
This directory contains examples on how to use the iText library within a
2+
Python.NET environment. The Python scripts themselves are located in the
3+
`sandbox` directory.
4+
5+
Files, which start with an `_` are common "library" files and should not be
6+
run directly. Every other Python file is a separate example, which can be
7+
run on its own. Examples will put their output next to the Python scripts
8+
themselves with the same base name.
9+
10+
For example, to run the `paragraph_text_with_style` sample, make sure the
11+
`python` environment you are using has the `itextpy` package installed and
12+
run this command in the terminal:
13+
14+
```shell
15+
python layout/paragraph_text_with_style.py
16+
```
17+
18+
This will create a PDF file at `layout/paragraph_text_with_style.pdf`, where
19+
you can check the results.
20+
21+
To run all the available samples, use the `run_samples.ps1` script on Windows
22+
and the `run_samples.sh` script on *nix.
23+
24+
Most of the samples are based on the .NET iText samples, which can be found at
25+
[itext-publications-samples-dotnet](https://github.com/itext/itext-publications-samples-dotnet).

samples/run_samples.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Get-ChildItem -Path $PSScriptRoot\sandbox\ -File -Filter "*.py" -Exclude "_*" -Recurse | ForEach-Object {
2+
Write-Output "Running $($_.Directory.Name)/$($_.Name)..."
3+
python $_.FullName
4+
}

samples/run_samples.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
3+
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
4+
shopt -s globstar
5+
for f in "$SCRIPT_DIR"/**/[^_]*.py; do
6+
echo "Processing $(basename "$(dirname "$f")")/$(basename "$f")..."
7+
python "$f"
8+
done

samples/sandbox/acroforms/create_form_field_through_layout.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33

44
from itextpy.util import disposing
55

6+
from pathlib import Path
7+
68
from iText.Forms.Form.Element import Button, InputField, Radio, TextArea
79
from iText.Kernel.Colors import ColorConstants
810
from iText.Kernel.Pdf import PdfWriter, PdfDocument
911
from iText.Layout import Document
1012
from iText.Layout.Borders import SolidBorder
1113
from iText.Layout.Element import Cell, Paragraph, Table
1214

15+
SCRIPT_DIR = Path(__file__).parent.absolute()
16+
1317

1418
def manipulate_pdf(dest):
1519
with disposing(Document(PdfDocument(PdfWriter(dest)))) as document:
@@ -62,4 +66,4 @@ def manipulate_pdf(dest):
6266

6367

6468
if __name__ == "__main__":
65-
manipulate_pdf("create_form_field_through_layout.pdf")
69+
manipulate_pdf(str(SCRIPT_DIR / "create_form_field_through_layout.pdf"))

samples/sandbox/ai/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The `table_of_contents.py` example requires additional Python packages, which
2+
are listed in `requirements.txt`. Additionally, you need to have a local
3+
Ollama instance running with a `Qwen2.5` model pre-pulled.
4+
5+
For further information on how to setup the environment or customize the LLM
6+
environment used, check the comments in the `table_of_contents.py` script.

samples/sandbox/ai/table_of_contents.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
# The default Ollama context window is small, so make sure to change it using
1111
# the instructions below.
1212
#
13+
import sys
14+
1315
import itextpy
1416
itextpy.load()
1517

@@ -18,8 +20,6 @@
1820
from pathlib import Path
1921
from sys import stderr
2022

21-
from openai import OpenAI
22-
2323
from iText.Kernel.Geom import PageSize
2424
from iText.Kernel.Pdf.Canvas.Parser import PdfTextExtractor
2525
from iText.Kernel.Pdf.Canvas.Parser.Listener import LocationTextExtractionStrategy
@@ -138,6 +138,13 @@ def add_page_data(toc_data: TableEntry, pages: list[str]) -> None:
138138
# This function ask the LLM to generate the table of contents for the provided
139139
# pages of text. The result gets parsed into a tree-like structure.
140140
def generate_toc_data(pages: list[str]) -> TableEntry:
141+
try:
142+
from openai import OpenAI
143+
except ImportError:
144+
print('ai/table_of_contents.py sample requires openai package, '
145+
'skipping...', file=sys.stderr)
146+
sys.exit()
147+
141148
openai_client = OpenAI(
142149
base_url=OPENAI_BASE_URL,
143150
api_key=OPENAI_API_KEY,
@@ -238,4 +245,4 @@ def main(in_path: str, out_path: str) -> None:
238245

239246

240247
# Call the function to create a PDF
241-
main(INPUT_PDF_PATH, "table_of_contents.pdf")
248+
main(INPUT_PDF_PATH, str(SCRIPT_DIR / "table_of_contents.pdf"))

samples/sandbox/annotations/add_link_annotation_5.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ def manipulate_pdf(dest):
3636

3737

3838
if __name__ == "__main__":
39-
manipulate_pdf("add_link_annotation_5.pdf")
39+
manipulate_pdf(str(SCRIPT_DIR / "add_link_annotation_5.pdf"))

samples/sandbox/annotations/move_popup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ def manipulate_pdf(dest):
3939

4040

4141
if __name__ == "__main__":
42-
manipulate_pdf("move_popup.pdf")
42+
manipulate_pdf(str(SCRIPT_DIR / "move_popup.pdf"))

samples/sandbox/events/page_rotation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ def manipulate_pdf(dest):
5757

5858

5959
if __name__ == "__main__":
60-
manipulate_pdf("page_rotation.pdf")
60+
manipulate_pdf(str(SCRIPT_DIR / "page_rotation.pdf"))

samples/sandbox/events/text_footer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,4 @@ def manipulate_pdf(dest):
7373

7474

7575
if __name__ == "__main__":
76-
manipulate_pdf("text_footer.pdf")
76+
manipulate_pdf(str(SCRIPT_DIR / "text_footer.pdf"))

samples/sandbox/events/watermarking.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,4 @@ def manipulate_pdf(dest):
8383

8484

8585
if __name__ == "__main__":
86-
manipulate_pdf("watermarking.pdf")
86+
manipulate_pdf(str(SCRIPT_DIR / "watermarking.pdf"))

samples/sandbox/images/multiple_images.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ def manipulate_pdf(dest):
3333

3434

3535
if __name__ == "__main__":
36-
manipulate_pdf("multiple_images.pdf")
36+
manipulate_pdf(str(SCRIPT_DIR / "multiple_images.pdf"))

samples/sandbox/layout/page_size_and_margins.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
from itextpy.util import disposing
55

6+
from pathlib import Path
7+
68
from iText.Kernel.Geom import PageSize
79
from iText.Kernel.Pdf import PdfWriter, PdfDocument
810
from iText.Layout import Document
911
from iText.Layout.Element import AreaBreak, Paragraph
1012

13+
SCRIPT_DIR = Path(__file__).parent.absolute()
14+
1115

1216
def manipulate_pdf(dest):
1317
page_size = PageSize(200, 200)
@@ -40,4 +44,4 @@ def manipulate_pdf(dest):
4044

4145

4246
if __name__ == "__main__":
43-
manipulate_pdf("page_size_and_margins.pdf")
47+
manipulate_pdf(str(SCRIPT_DIR / "page_size_and_margins.pdf"))

samples/sandbox/layout/paragraph_text_with_style.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33

44
from itextpy.util import disposing
55

6+
from pathlib import Path
7+
68
from iText.IO.Font.Constants import StandardFonts
79
from iText.Kernel.Colors import ColorConstants
810
from iText.Kernel.Font import PdfFontFactory
911
from iText.Kernel.Pdf import PdfWriter, PdfDocument
1012
from iText.Layout import Style, Document
1113
from iText.Layout.Element import Paragraph, Text
1214

15+
SCRIPT_DIR = Path(__file__).parent.absolute()
16+
1317

1418
def manipulate_pdf(dest):
1519
code = PdfFontFactory.CreateFont(StandardFonts.COURIER)
@@ -32,4 +36,4 @@ def manipulate_pdf(dest):
3236

3337

3438
if __name__ == "__main__":
35-
manipulate_pdf("paragraph_text_with_style.pdf")
39+
manipulate_pdf(str(SCRIPT_DIR / "paragraph_text_with_style.pdf"))

samples/sandbox/merge/add_cover_1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ def manipulate_pdf(dest):
2424

2525

2626
if __name__ == "__main__":
27-
manipulate_pdf("add_cover_1.pdf")
27+
manipulate_pdf(str(SCRIPT_DIR / "add_cover_1.pdf"))

samples/sandbox/objects/nested_lists.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33

44
from itextpy.util import disposing
55

6+
from pathlib import Path
7+
68
from iText.Kernel.Pdf import PdfWriter, PdfDocument
79
from iText.Layout import Document
810
from iText.Layout.Element import List, ListItem, Paragraph
911

12+
SCRIPT_DIR = Path(__file__).parent.absolute()
13+
1014

1115
def manipulate_pdf(dest):
1216
with disposing(Document(PdfDocument(PdfWriter(dest)))) as document:
@@ -31,4 +35,4 @@ def manipulate_pdf(dest):
3135

3236

3337
if __name__ == "__main__":
34-
manipulate_pdf("nested_lists.pdf")
38+
manipulate_pdf(str(SCRIPT_DIR / "nested_lists.pdf"))

samples/sandbox/pdfa/pdf_a3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ def manipulate_pdf(dest):
7070

7171

7272
if __name__ == "__main__":
73-
manipulate_pdf("pdf_a3.pdf")
73+
manipulate_pdf(str(SCRIPT_DIR / "pdf_a3.pdf"))

samples/sandbox/pdfa/pdf_a4.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ def manipulate_pdf(dest):
3838

3939

4040
if __name__ == "__main__":
41-
manipulate_pdf("pdf_a4.pdf")
41+
manipulate_pdf(str(SCRIPT_DIR / "pdf_a4.pdf"))

samples/sandbox/pdfhtml/parse_html_as_print.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ def manipulate_pdf(html_source, pdf_dest, resource_loc):
2929
if __name__ == "__main__":
3030
manipulate_pdf(
3131
html_source=str(SRC_DIR / "rainbow.html"),
32-
pdf_dest="parse_html_as_print.pdf",
32+
pdf_dest=str(SCRIPT_DIR / "parse_html_as_print.pdf"),
3333
resource_loc=str(SRC_DIR),
3434
)

samples/sandbox/pdfhtml/parse_html_color_blind.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from System.IO import FileAccess, FileMode, FileShare, FileStream
99
from iText.Html2pdf import ConverterProperties, HtmlConverter
1010

11-
from colorblindness import ColorBlindnessCssApplierFactory, ColorBlindnessTransforms
11+
from _colorblindness import ColorBlindnessCssApplierFactory, ColorBlindnessTransforms
1212

1313
SCRIPT_DIR = Path(__file__).parent.absolute()
1414
RESOURCES_DIR = SCRIPT_DIR / ".." / ".." / "resources"
@@ -34,6 +34,6 @@ def manipulate_pdf(html_source, pdf_dest, resource_loc):
3434
if __name__ == "__main__":
3535
manipulate_pdf(
3636
html_source=str(SRC_DIR / "rainbow.html"),
37-
pdf_dest="parse_html_color_blind.pdf",
37+
pdf_dest=str(SCRIPT_DIR / "parse_html_color_blind.pdf"),
3838
resource_loc=str(SRC_DIR),
3939
)

samples/sandbox/pdfhtml/parse_html_qr_code.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from System.IO import FileAccess, FileMode, FileStream
99
from iText.Html2pdf import ConverterProperties, HtmlConverter
1010

11-
from qr_code_tag import QRCodeTagCssApplierFactory, QRCodeTagWorkerFactory
11+
from _qr_code_tag import QRCodeTagCssApplierFactory, QRCodeTagWorkerFactory
1212

1313
SCRIPT_DIR = Path(__file__).parent.absolute()
1414
RESOURCES_DIR = SCRIPT_DIR / ".." / ".." / "resources"
@@ -39,6 +39,6 @@ def manipulate_pdf(html_source, pdf_dest, resource_loc):
3939
if __name__ == "__main__":
4040
manipulate_pdf(
4141
html_source=str(SRC_DIR / "qrcode.html"),
42-
pdf_dest="parse_html_qr_code.pdf",
42+
pdf_dest=str(SCRIPT_DIR / "parse_html_qr_code.pdf"),
4343
resource_loc=str(SRC_DIR),
4444
)

samples/sandbox/pdfhtml/parse_html_simple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ def manipulate_pdf(html_source, pdf_dest, resource_loc):
2323
if __name__ == "__main__":
2424
manipulate_pdf(
2525
html_source=str(SRC_DIR / "rainbow.html"),
26-
pdf_dest="parse_html_simple.pdf",
26+
pdf_dest=str(SCRIPT_DIR / "parse_html_simple.pdf"),
2727
resource_loc=str(SRC_DIR),
2828
)

samples/sandbox/pdfhtml/using_custom_fonts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,5 @@ def manipulate_pdf(html_source, pdf_dest):
4747
if __name__ == "__main__":
4848
manipulate_pdf(
4949
html_source=str(SRC_DIR / "FontExample.html"),
50-
pdf_dest="using_custom_fonts.pdf",
50+
pdf_dest=str(SCRIPT_DIR / "using_custom_fonts.pdf"),
5151
)

samples/sandbox/pdfua/pdf_ua.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,4 @@ def manipulate_pdf(dest):
163163

164164

165165
if __name__ == "__main__":
166-
manipulate_pdf("pdf_ua.pdf")
166+
manipulate_pdf(str(SCRIPT_DIR / "pdf_ua.pdf"))

samples/sandbox/pdfua/wtpdf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self, element: IElementNode, context: ProcessorContext):
4949

5050
def GetElementResult(self) -> IPropertyContainer:
5151
# FIXME: For some reason super().GetElementResult() doesn't work.
52-
# It just call this method again, causing infinite recursion.
52+
# It just calls this method again, causing infinite recursion.
5353
element_result = HTagWorker.GetElementResult(self)
5454
# Duck typing does not really work for .NET types...
5555
result = clr_try_cast(element_result, Div)
@@ -96,4 +96,4 @@ def manipulate_pdf(dest):
9696

9797

9898
if __name__ == "__main__":
99-
manipulate_pdf("wtpdf.pdf")
99+
manipulate_pdf(str(SCRIPT_DIR / "wtpdf.pdf"))

samples/sandbox/signatures/clients.py renamed to samples/sandbox/signatures/_clients.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from typing import Self
55

6-
from utils import TestOcspResponseBuilder
6+
from _utils import TestOcspResponseBuilder
77

88
from System.Collections.Generic import Dictionary
99
from iText.Bouncycastleconnector import BouncyCastleFactoryCreator

samples/sandbox/signatures/fips.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,14 @@
2323
from pathlib import Path
2424
import sys
2525

26-
from utils import PemFileHelper
26+
from _utils import PemFileHelper
2727

2828
from System.IO import FileMode, FileStream
2929
from iText.Kernel.Crypto import DigestAlgorithms
3030
from iText.Kernel.Pdf import PdfReader, StampingProperties
3131
from iText.Signatures import PdfSigner, PrivateKeySignature
3232
from Org.BouncyCastle.Crypto import CryptoServicesRegistrar
3333

34-
3534
SCRIPT_DIR = Path(__file__).parent.absolute()
3635
RESOURCES_DIR = SCRIPT_DIR / ".." / ".." / "resources"
3736
SRC_PATH = str(RESOURCES_DIR / "pdfs" / "signExample.pdf")
@@ -57,4 +56,4 @@ def manipulate_pdf(dest):
5756

5857

5958
if __name__ == "__main__":
60-
manipulate_pdf("fips.pdf")
59+
manipulate_pdf(str(SCRIPT_DIR / "fips.pdf"))

samples/sandbox/signatures/signature_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,4 @@ def manipulate_pdf(dest):
8484

8585

8686
if __name__ == "__main__":
87-
manipulate_pdf("signature_example.pdf")
87+
manipulate_pdf(str(SCRIPT_DIR / "signature_example.pdf"))

samples/sandbox/signatures/validate_chain_before_signing_example.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from pathlib import Path
77

8-
from utils import PemFileHelper, TestOcspResponseBuilder
9-
from clients import TestOcspClient
8+
from _utils import PemFileHelper, TestOcspResponseBuilder
9+
from _clients import TestOcspClient
1010

1111
from System import Func
1212
from System.Collections.Generic import List
@@ -99,5 +99,5 @@ def manipulate_pdf(src, dest):
9999
if __name__ == "__main__":
100100
manipulate_pdf(
101101
str(RESOURCES_DIR / "pdfs" / "hello.pdf"),
102-
"validate_chain_before_signing_example.txt",
102+
str(SCRIPT_DIR / "validate_chain_before_signing_example.txt"),
103103
)

samples/sandbox/svg/convert_svg_string_to_pdf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ def manipulate_pdf(pdf_dest):
3737

3838

3939
if __name__ == "__main__":
40-
manipulate_pdf("convert_svg_string_to_pdf.pdf")
40+
manipulate_pdf(str(SCRIPT_DIR / "convert_svg_string_to_pdf.pdf"))

samples/sandbox/svg/convert_svg_to_layout_image.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ def manipulate_pdf(svg_source, pdf_dest):
4040
if __name__ == "__main__":
4141
manipulate_pdf(
4242
svg_source=str(SVG_RESOURCES_DIR / "cauldron.svg"),
43-
pdf_dest="convert_svg_to_layout_image.pdf",
43+
pdf_dest=str(SCRIPT_DIR / "convert_svg_to_layout_image.pdf"),
4444
)

samples/sandbox/svg/convert_svg_to_pdf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ def manipulate_pdf(svg_source, pdf_dest):
1818
if __name__ == "__main__":
1919
manipulate_pdf(
2020
svg_source=str(SVG_RESOURCES_DIR / "cauldron.svg"),
21-
pdf_dest="convert_svg_to_pdf.pdf",
21+
pdf_dest=str(SCRIPT_DIR / "convert_svg_to_pdf.pdf"),
2222
)

0 commit comments

Comments
 (0)