Add pdf-invoice module: one invoice in OpenPDF 3.0.5, iText Core 9.8.0 and Flying Saucer 10.7.0, Devanagari shaping checked by rendering, streamed vs buffered download, subset sizes and timings

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b6Kg9RWSTJzyz6KP9X
This commit is contained in:
2026-10-30 16:46:25 +00:00
co-authored by Claude Sonnet 5.5
parent 9b1da079c6
commit 3a30194459
54 changed files with 1906 additions and 0 deletions
@@ -0,0 +1,20 @@
# licences as declared in the POMs Maven downloaded (~/.m2) and in the pdfCalligraph POM on iText's own repository, fetched 2026-10-30
openpdf 3.0.5 (declared in openpdf-parent): GNU Lesser General Public License (LGPL), Version 2.1;Mozilla Public License Version 2.0;
itext kernel/layout 9.8.0 (root pom): GNU Affero General Public License v3;
flying-saucer-pdf 10.7.0 (flying-saucer-parent): GNU Lesser General Public License (LGPL), version 2.1 or later;
typography (pdfCalligraph) 5.1.1: Proprietary License;
# sentences from https://itextpdf.com/how-buy/agpl-license (verbatim, page text with tags stripped)
- With the onset of e-invoicing legislation coming into effect across Europe, there is renewed interest in the ZUGFeRD/Factur-X hybrid invoice standard. This article examines how the standard has evolved, and how you can use the dual-licensed iText Core library to easily create ZUGFeRD-compatible files to comply with EN 16931 requirements.
- iText 5 and all subsequent versions of iText are dual-licensed, and available under open source (AGPLv3) or commercial license agreements. Since iText is
- If you cannot comply with the AGPLv3 terms, we offer commercial licensing options. Purchasing a commercial license will release you from the AGPLv3 copyleft restrictions, allowing you to keep your applications closed-source to your users.
- You may not deploy it on a network without disclosing the full source code of your own applications under the AGPL license. You must distribute all source code, including your own product and web-based applications.
- It’s a legal violation to use iText Core/Community and our open source add-ons in a non-AGPL environment.
- When using iText Core/Community under AGPL, you must prominently mention iText and include the iText copyright and AGPL license in output file metadata, and also retain the producer line in every PDF that is created or manipulated using iText.
# versions: latest GA per maven-metadata.xml
com/github/librepdf/openpdf latest 3.0.5
com/itextpdf/kernel latest 9.8.0
org/xhtmlrenderer/flying-saucer-pdf latest 10.7.0
org/xhtmlrenderer/flying-saucer-pdf-openpdf latest 9.4.0
org/apache/pdfbox/pdfbox latest 3.0.8
+125
View File
@@ -0,0 +1,125 @@
# PDFBox 3.0.8 text extraction of each engine's invoice, written by PdfContentTest
=== flyingsaucer (20012 bytes) ===
Tax Invoice / कर चलन
Invoice INV-2026-0042 | 2026-10-30
From / विक्रेता Bill to / ग्राहक
Ankur Book House, Mumbai श्रीमती क्षमा कुलकर्णी
12 Dadar Market Road, Mumbai 400014 पुणे, महाराष्ट्र 411004
GSTIN 27AAAAA0000A1Z5 GSTIN 27BBBBB1111B1Z6
Item / तपशील Qty Rate Amount / रक्कम
ज्ञानेश्वरी – संपूर्ण ग्रंथ (मराठी) 2 ₹ 450.00 ₹ 900.00
प्रोग्रामिंग की कला (हिन्दी) 1 ₹ 899.00 ₹ 899.00
विद्वान कर्मचारी मार्गदर्शिका 3 ₹ 125.50 ₹ 376.50
Spring Boot 4 Handbook 1 ₹ 1,299.00 ₹ 1,299.00
Subtotal / उपबेरीज ₹ 3,474.50
CGST 9% + SGST 9% / कर ₹ 625.41
Total / एकूण ₹ 4,099.91
कृपया धन्यवाद — द्वारा: अंकुर बुक हाऊस. Payment due in 15 days.
=== flyingsaucer-shaped (21537 bytes) ===
Tax Invoice / कर चलन
Invoice INV-2026-0042 | 2026-10-30
From / ववकेता Bill to / गाहक
Ankur Book House, Mumbai शीमती कमा कुलकणर
12 Dadar Market Road, Mumbai 400014 पणु,े महाराष् 411004
GSTIN 27AAAAA0000A1Z5 GSTIN 27BBBBB1111B1Z6
Item / तपशील Qty Rate Amount / रककम
जानशेरी – सपंणू र गथं (मराठी) 2 ₹ 450.00 ₹ 900.00
पोगाममरग की कला (हहनदी) 1 ₹ 899.00 ₹ 899.00
ववदान कमचर ारी मागदर रशरका 3 ₹ 125.50 ₹ 376.50
Spring Boot 4 Handbook 1 ₹ 1,299.00 ₹ 1,299.00
Subtotal / उपबरेीज ₹ 3,474.50
CGST 9% + SGST 9% / कर ₹ 625.41
Total / एकूण ₹ 4,099.91
कृपया धनयवाद — दारा: अकुंर बकु हाऊस. Payment due in 15 days.
=== itext (23344 bytes) ===
Tax Invoice / कर चलन
Invoice INV-2026-0042 | 2026-10-30
From / विक्रेता Bill to / ग्राहक
Ankur Book House, Mumbai श्रीमती क्षमा कुलकर्णी
12 Dadar Market Road, Mumbai 400014 पुणे, महाराष्ट्र 411004
GSTIN 27AAAAA0000A1Z5 GSTIN 27BBBBB1111B1Z6
Item / तपशील Qty Rate Amount / रक्कम
ज्ञानेश्वरी – संपूर्ण ग्रंथ (मराठी) 2 ₹ 450.00 ₹ 900.00
प्रोग्रामिंग की कला (हिन्दी) 1 ₹ 899.00 ₹ 899.00
विद्वान कर्मचारी मार्गदर्शिका 3 ₹ 125.50 ₹ 376.50
Spring Boot 4 Handbook 1 ₹ 1,299.00 ₹ 1,299.00
Subtotal / उपबेरीज ₹ 3,474.50
CGST 9% + SGST 9% / कर ₹ 625.41
Total / एकूण ₹ 4,099.91
कृपया धन्यवाद — द्वारा: अंकुर बुक हाऊस. Payment due in 15 days.
=== openpdf (20039 bytes) ===
Tax Invoice / कर चलन
Invoice INV-2026-0042 | 2026-10-30
From / विक्रेता Bill to / ग्राहक
Ankur Book House, Mumbai श्रीमती क्षमा कुलकर्णी
12 Dadar Market Road, Mumbai 400014 पुणे, महाराष्ट्र 411004
GSTIN 27AAAAA0000A1Z5 GSTIN 27BBBBB1111B1Z6
Item / तपशील Qty Rate Amount / रक्कम
ज्ञानेश्वरी – संपूर्ण ग्रंथ (मराठी) 2 ₹ 450.00 ₹ 900.00
प्रोग्रामिंग की कला (हिन्दी) 1 ₹ 899.00 ₹ 899.00
विद्वान कर्मचारी मार्गदर्शिका 3 ₹ 125.50 ₹ 376.50
Spring Boot 4 Handbook 1 ₹ 1,299.00 ₹ 1,299.00
Subtotal / उपबेरीज ₹ 3,474.50
CGST 9% + SGST 9% / कर ₹ 625.41
Total / एकूण ₹ 4,099.91
कृपया धन्यवाद — द्वारा: अंकुर बुक हाऊस. Payment due in 15 days.
=== openpdf-jdk (21553 bytes) ===
Tax Invoice / कर चलन
Invoice INV-2026-0042 | 2026-10-30
From / ववकेता Bill to / गाहक
Ankur Book House, Mumbai शीमती कमा कुलकणर
12 Dadar Market Road, Mumbai 400014 पुणे, महाराष् 411004
GSTIN 27AAAAA0000A1Z5 GSTIN 27BBBBB1111B1Z6
Item / तपशील Qty Rate Amount / रककम
जानशेरी – संपूणर गंथ (मराठी) 2 ₹ 450.00 ₹ 900.00
पोगाममरग की कला (हहनदी) 1 ₹ 899.00 ₹ 899.00
ववदान कमरचारी मागदर रशरका 3 ₹ 125.50 ₹ 376.50
Spring Boot 4 Handbook 1 ₹ 1,299.00 ₹ 1,299.00
Subtotal / उपबरेीज ₹ 3,474.50
CGST 9% + SGST 9% / कर ₹ 625.41
Total / एकूण ₹ 4,099.91
कृपया धनयवाद — दारा: अंकुर बकु हाऊस. Payment due in 15 days.
=== openpdf-jdk-actualtext (22285 bytes) ===
Tax Invoice / कर चलन
Invoice INV-2026-0042 | 2026-10-30
From / विक्रेता Bill to / ग्राहक
Ankur Book House, Mumbai श्रीमती क्षमा कुलकर्णी
12 Dadar Market Road, Mumbai 400014 पुणे, महाराष्ट्र 411004
GSTIN 27AAAAA0000A1Z5 GSTIN 27BBBBB1111B1Z6
Item / तपशील Qty Rate Amount / रक्कम
ज्ञानेश्वरी – संपूर्ण ग्रंथ (मराठी) 2 ₹ 450.00 ₹ 900.00
प्रोग्रामिंग की कला (हिन्दी) 1 ₹ 899.00 ₹ 899.00
विद्वान कर्मचारी मार्गदर्शिका 3 ₹ 125.50 ₹ 376.50
Spring Boot 4 Handbook 1 ₹ 1,299.00 ₹ 1,299.00
Subtotal / उपबेरीज ₹ 3,474.50
CGST 9% + SGST 9% / कर ₹ 625.41
Total / एकूण ₹ 4,099.91
कृपया धन्यवाद — द्वारा: अंकुर बुक हाऊस. Payment due in 15 days.
fonts flyingsaucer [LXEQIC+NotoSansDevanagari-Bold embedded=true, OUOZGZ+NotoSansDevanagari-Regular embedded=true]
fonts flyingsaucer-shaped [GJBPWA+NotoSansDevanagari-Bold embedded=true, FYEFNQ+NotoSansDevanagari-Regular embedded=true]
fonts itext [YCXTVQ+NotoSansDevanagari-Bold embedded=true, PXNQYY+NotoSansDevanagari-Regular embedded=true]
fonts openpdf [FFJNLO+NotoSansDevanagari-Bold embedded=true, CRMHHV+NotoSansDevanagari-Regular embedded=true]
fonts openpdf-jdk-actualtext [GEKVVR+NotoSansDevanagari-Bold embedded=true, PPYLBT+NotoSansDevanagari-Regular embedded=true]
+47
View File
@@ -0,0 +1,47 @@
# HTTP: status and headers per engine (curl -D -), then streaming vs buffered, then a failure after rendering
# openjdk version "25.0.5" 2026-10-20 LTS | 2 vCPU | 8027 MB RAM
--- GET /invoices/openpdf.pdf
HTTP/1.1 200
Content-Disposition: attachment; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
--- GET /invoices/itext.pdf
HTTP/1.1 200
Content-Disposition: attachment; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
--- GET /invoices/flyingsaucer.pdf
HTTP/1.1 200
Content-Disposition: attachment; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
--- GET /invoices/openpdf-jdk.pdf
HTTP/1.1 200
Content-Disposition: attachment; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
--- GET /invoices/openpdf-jdk-actualtext.pdf
HTTP/1.1 200
Content-Disposition: attachment; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
--- GET /invoices/flyingsaucer-shaped.pdf
HTTP/1.1 200
Content-Disposition: attachment; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
--- GET /invoices/openpdf.pdf?mode=buffered
HTTP/1.1 200
Content-Length: 20039
--- GET /invoices/openpdf.pdf?disposition=inline
HTTP/1.1 200
Content-Disposition: inline; filename="__-INV-2026-0042.pdf"; filename*=UTF-8''%E0%A4%AC%E0%A4%BF%E0%A4%B2-INV-2026-0042.pdf
--- GET /invoices/nope.pdf
HTTP/1.1 404
--- GET /invoices/openpdf.pdf?failAtEnd=true (streamed: headers are already gone when the failure happens)
HTTP/1.1 200
Transfer-Encoding: chunked
curl exit=18 bytes_received=20039
--- GET /invoices/openpdf.pdf?failAtEnd=true&mode=buffered (rendered into memory first)
HTTP/1.1 500
curl exit=0 bytes_received=116
@@ -0,0 +1,43 @@
# pdffonts per engine (poppler): name, type, embedded, subset
--- openpdf (20039 bytes)
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
QVECCU+NotoSansDevanagari-Bold CID TrueType Identity-H yes yes yes 1 0
LWDRDP+NotoSansDevanagari-Regular CID TrueType Identity-H yes yes yes 2 0
--- itext (23344 bytes)
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
BYDHEU+NotoSansDevanagari-Bold CID TrueType Identity-H yes yes yes 6 0
EUBWBF+NotoSansDevanagari-Regular CID TrueType Identity-H yes yes yes 7 0
--- flyingsaucer (20011 bytes)
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
AKIOED+NotoSansDevanagari-Bold CID TrueType Identity-H yes yes yes 2 0
WSTLFR+NotoSansDevanagari-Regular CID TrueType Identity-H yes yes yes 3 0
--- openpdf-jdk (21553 bytes)
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
SJCTQQ+NotoSansDevanagari-Bold CID TrueType Identity-H yes yes yes 1 0
SDAHWP+NotoSansDevanagari-Regular CID TrueType Identity-H yes yes yes 2 0
--- openpdf-jdk-actualtext (22285 bytes)
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
VNADNX+NotoSansDevanagari-Bold CID TrueType Identity-H yes yes yes 1 0
EAQUGS+NotoSansDevanagari-Regular CID TrueType Identity-H yes yes yes 2 0
--- flyingsaucer-shaped (21536 bytes)
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
RTHNRU+NotoSansDevanagari-Bold CID TrueType Identity-H yes yes yes 2 0
STZJGA+NotoSansDevanagari-Regular CID TrueType Identity-H yes yes yes 3 0
# Producer/Creator lines the libraries write into the PDF (pdfinfo)
--- openpdf
Title: Invoice INV-2026-0042
Producer: OpenPDF 3.0.5
--- itext
Title: Invoice INV-2026-0042
Producer: iText® Core 9.8.0 (AGPL version) ©2000-2026 Apryse Group NV
--- flyingsaucer
Title: Invoice INV-2026-0042
Creator: Flying Saucer 10.7.0 with OpenPDF 3.0.5
Producer: Flying Saucer 10.7.0 with OpenPDF 3.0.5
+9
View File
@@ -0,0 +1,9 @@
# glyph codes written into page 1 content streams for the probe invoice (Identity-H, 2 bytes each)
# code points drawn, spaces excluded: 364
# the engines also draw the spaces as glyphs, so read the numbers against each other, not against this one
openpdf glyph codes: 427
itext glyph codes: 423
flyingsaucer glyph codes: 421
openpdf-jdk glyph codes: 387
openpdf-jdk-actualtext glyph codes: 387
flyingsaucer-shaped glyph codes: 381
@@ -0,0 +1,4 @@
# LayoutProcessor.enable() in the same JVM, then two other OpenPDF writers, then disable()
openpdf: RuntimeException: Font creation failed for NotoSansDevanagari-Regular.ttf. | root cause IOException: NotoSansDevanagari-Regular.ttf not found as file or resource.
openpdf-jdk: IllegalStateException: LayoutProcessor is enabled. Don't use the deprecated LayoutProcessor! | root cause IllegalStateException: LayoutProcessor is enabled. Don't use the deprecated LayoutProcessor!
after LayoutProcessor.disable(): openpdf-jdk rendered normally
+16
View File
@@ -0,0 +1,16 @@
# in-process timing, openjdk version "25.0.5" 2026-10-20 LTS | 2 vCPU | 8027 MB RAM
# indicative: shared sandbox, one JVM, one run per line
# n=500 invoices per engine after 100 warm-up, one JVM, engines run one after another
# OpenJDK 64-Bit Server VM 25.0.5 | 2 vCPU
engine first_ms mean_ms p95_ms total_s pdf_bytes
openpdf 377.0 7.93 18.89 3.96 20034
itext 330.9 15.88 36.38 7.94 23338
flyingsaucer 1163.4 28.05 57.66 14.02 20008
openpdf-jdk-actualtext 423.5 42.89 107.47 21.45 22281
flyingsaucer-shaped 107.5 49.99 93.45 24.99 21537
# subset=false (whole font embedded), same invoice, one render each
openpdf subset=20025 bytes, whole font=186300 bytes
itext subset=23330 bytes, whole font=200149 bytes
flyingsaucer subset=19996 bytes, whole font=186308 bytes
openpdf-jdk-actualtext subset=22268 bytes, whole font=187399 bytes
flyingsaucer-shaped subset=21525 bytes, whole font=186691 bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB