Skip to content

Commit d349c59

Browse files
authored
Minor edits to PDF/A tutorial (#1390)
1 parent b228272 commit d349c59

File tree

10 files changed

+38
-56
lines changed

10 files changed

+38
-56
lines changed

CHANGELOG.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
1818

1919
## [2.8.3] - Not released yet
2020
### Added
21-
* added tutorial "tuto7" (in [English](https://py-pdf.github.io/fpdf2/Tutorial.html#tuto-7-creating-pdfa-documents) and [German](https://py-pdf.github.io/fpdf2/Tutorial-de.html#tuto-7-ein-pdfa-dokument-erstellen)) with documentation to create PDF/A files with fpdf2
22-
* support for [Output Intents](https://py-pdf.github.io/fpdf2/Images.html#output-intents) on document level
21+
* added tutorial "tuto7" (in [English](https://py-pdf.github.io/fpdf2/Tutorial.html#tuto-7-creating-pdfa-documents) and [German](https://py-pdf.github.io/fpdf2/Tutorial-de.html#tuto-7-ein-pdfa-dokument-erstellen)) with documentation to create PDF/A files with fpdf2 - thanks to @lka
22+
* support for [Output Intents](https://py-pdf.github.io/fpdf2/Images.html#output-intents) on document level - thanks to @lka
2323
* support for [shading patterns (gradients)](https://py-pdf.github.io/fpdf2/Patterns.html) - thanks to @andersonhc - [PR #1334](https://github.com/py-pdf/fpdf2/pull/1334)
2424
* support for [setting a minimal row height in tables](https://py-pdf.github.io/fpdf2/Tables.html#setting-row-height)
2525
* support for [`v_align` at the row level in tables](https://py-pdf.github.io/fpdf2/Tables.html#setting-vertical-alignment-of-text-in-cells)
2626
* new optional `reset_page_indices` parameter for [`insert_toc_placeholder()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.insert_toc_placeholder)
2727
* support for <s>strikethrough text</s>
2828
* new [ViewerPreferences.print_scaling](https://py-pdf.github.io/fpdf2/fpdf/prefs.html#fpdf.prefs.ViewerPreferences.print_scaling)
2929
* documentation on [verifying provenance of `fpdf2` releases](https://py-pdf.github.io/fpdf2/#verifying-provenance)
30-
* documentation on [`fpdf2` internals](https://py-pdf.github.io/fpdf2/Internals.html)### Added
30+
* documentation on [`fpdf2` internals](https://py-pdf.github.io/fpdf2/Internals.html)
3131
* added Slovenian translation of the tutorial: [Vodič](https://py-pdf.github.io/fpdf2/Tutorial-sl.html) - thanks to @DeepBlackHole
3232
* support for adding TrueType fonts that are missing the `.notdef` glyph - [issue #1161](https://github.com/py-pdf/fpdf2/issues/1161) - thanks to @spacegaori
3333
* improved SVG image speed by 50% to 70% - thanks to @petri-lipponen-movesense - [PR #1350](https://github.com/py-pdf/fpdf2/pull/1350)

docs/Metadata.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ The PDF 2.0 specification removes the `DocumentInformation` dictionary.
77
Currently, the following methods on `fpdf.FPDF` allow to set metadata information
88
in the `DocumentInformation` dictionary:
99

10-
- `set_title`
11-
- `set_lang`
12-
- `set_subject`
13-
- `set_author`
14-
- `set_keywords`
15-
- `set_producer`
16-
- `set_creator`
17-
- `set_creation_date`
18-
- `set_xmp_metadata`, that requires you to craft the necessary XML string
10+
- [`set_title()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_title)
11+
- [`set_lang()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_lang)
12+
- [`set_subject()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_subject)
13+
- [`set_author()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_author)
14+
- [`set_keywords()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_keywords)
15+
- [`set_producer()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_producer)
16+
- [`set_creator()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_creator)
17+
- [`set_creation_date()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_creation_date)
18+
- [`set_xmp_metadata()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_xmp_metadata), that requires you to craft the necessary XML string
1919

2020
For a more user-friendly API to set metadata,
2121
we recommend using [`pikepdf`](https://github.com/pikepdf/pikepdf/) that will set both XMP & `DocumentInformation` metadata:

docs/Tutorial-de.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,6 @@ pdf.create_pdf_with_metadata(
236236

237237
Dabei benutzen wir pikepdf um die nötigen Metadata zu erzeugen und den Typen auf PDF/A-3B zu setzen.
238238

239-
In der Funktion `create_pdf_with_metadata` setzen wir 'language' und 'subject' ausserhalb der Metadata bevor wir pikepdf aufrufen um, die Konformität zu erreichen.
239+
In der Funktion `create_pdf_with_metadata` setzen wir `language` und `subject` ausserhalb der Metadata bevor wir pikepdf aufrufen um, die Konformität zu erreichen.
240240

241241
Bitte benutzen Sie ein Programm, wie z.B. [VeraPDF](https://verapdf.org/), um die Konformität des erstellten PDF zu sicherzustellen.

docs/Tutorial.md

+5-15
Original file line numberDiff line numberDiff line change
@@ -266,22 +266,12 @@ Then, we add the ICC profile object to the output intents array using the
266266
[add_output_intent()](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.add_output_intent)
267267
method.
268268

269-
After adding first page, using the embedded font, writing some text, we'll create the pdf:
270-
```python
271-
pdf.create_pdf_with_metadata(
272-
filename="tuto7.pdf",
273-
language="en-US",
274-
title="Tutorial7",
275-
subject="Example for PDFA",
276-
creator=["John Dow", "Jane Dow"],
277-
description="this is my description of this file",
278-
keywords="Example Tutorial7"
279-
)
280-
```
281-
282-
Here we use [pikepdf](https://pypi.org/project/pikepdf/) to create the necessary metadata and set the type to PDF/A-3B.
269+
After adding some pages, using the embedded fonts, and writing some text,
270+
we create the pdf by calling `create_pdf_with_metadata()`,
271+
that uses [pikepdf](https://pypi.org/project/pikepdf/)
272+
to create the necessary metadata and set the type to PDF/A-3B.
283273

284-
In the function `create_pdf_with_metadata` we need to set 'language' and 'subject' outside the metadata before we use `pikepdf` to achieve conformance.
274+
For information on PDF metadata, check the dedicated documentation page: [Metadata](Metadata.md).
285275

286276
Note that instead of using a function, you could also subclass `FPDF.output()` to ensure that all your documents are PDF-A compliant, as done in [test/pdf-a/test_pdf_a.py](https://github.com/py-pdf/fpdf2/blob/master/test/pdf-a/test_pdf_a.py).
287277

fpdf/fpdf.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class Image:
127127
PDFPageLabel,
128128
ResourceCatalog,
129129
stream_content_for_raster_image,
130-
PDFICCProfileObject,
130+
PDFICCProfile,
131131
OutputIntentDictionary,
132132
)
133133
from .recorder import FPDFRecorder
@@ -487,7 +487,7 @@ def add_output_intent(
487487
output_condition_identifier: str = None,
488488
output_condition: str = None,
489489
registry_name: str = None,
490-
dest_output_profile: PDFICCProfileObject = None,
490+
dest_output_profile: PDFICCProfile = None,
491491
info: str = None,
492492
):
493493
"""
@@ -500,8 +500,8 @@ def add_output_intent(
500500
output_condition (str, optional): see the Definition in
501501
https://www.color.org/registry.xalter
502502
registry_name (str, optional): "https://www.color.org"
503-
dest_output_profile (PDFICCProfileObject, required/optional):
504-
PDFICCProfileObject | None # (required if
503+
dest_output_profile (PDFICCProfile, required/optional):
504+
PDFICCProfile | None # (required if
505505
output_condition_identifier does not specify a standard
506506
production condition; optional otherwise)
507507
info (str, required/optional see dest_output_profile): human

fpdf/output.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def __init__(
216216
self.s_mask = None
217217

218218

219-
class PDFICCProfileObject(PDFContentStream):
219+
class PDFICCProfile(PDFContentStream):
220220
"""holds values for ICC Profile Stream
221221
Args:
222222
contents (str): stream content
@@ -463,8 +463,8 @@ class OutputIntentDictionary:
463463
output_condition (str, optional): see the Definition in
464464
https://www.color.org/registry.xalter
465465
registry_name (str, optional): "https://www.color.org"
466-
dest_output_profile (PDFICCProfileObject, required/optional):
467-
PDFICCProfileObject | None # (required if
466+
dest_output_profile (PDFICCProfile, required/optional):
467+
PDFICCProfile | None # (required if
468468
output_condition_identifier does not specify a standard
469469
production condition; optional otherwise)
470470
info (str, required/optional see dest_output_profile): human
@@ -487,7 +487,7 @@ def __init__(
487487
output_condition_identifier: str,
488488
output_condition: str = None,
489489
registry_name: str = None,
490-
dest_output_profile: PDFICCProfileObject = None,
490+
dest_output_profile: PDFICCProfile = None,
491491
info: str = None,
492492
):
493493
self.type = Name("OutputIntent")
@@ -503,8 +503,7 @@ def __init__(
503503
self.registry_name = PDFString(registry_name) if registry_name else None
504504
self.dest_output_profile = (
505505
dest_output_profile
506-
if dest_output_profile
507-
and isinstance(dest_output_profile, PDFICCProfileObject)
506+
if dest_output_profile and isinstance(dest_output_profile, PDFICCProfile)
508507
else None
509508
)
510509
self.info = PDFString(info) if info else None
@@ -967,7 +966,7 @@ def _ensure_iccp(self, img_info):
967966
break
968967
assert iccp_content is not None
969968
# Note: n should be 4 if the profile ColorSpace is CMYK
970-
iccp_obj = PDFICCProfileObject(
969+
iccp_obj = PDFICCProfile(
971970
contents=iccp_content, n=img_info["dpn"], alternate=img_info["cs"]
972971
)
973972
iccp_pdf_i = self._add_pdf_obj(iccp_obj, "iccp")

test/output_intents/test_output_intents.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from fpdf import FPDF
44
from fpdf.enums import OutputIntentSubType
5-
from fpdf.output import PDFICCProfileObject, OutputIntentDictionary
5+
from fpdf.output import PDFICCProfile, OutputIntentDictionary
66

77
import pytest
88
from test.conftest import assert_pdf_equal
@@ -50,7 +50,7 @@ def test_output_intents(tmp_path):
5050
"""
5151
doc = FPDF()
5252
with open(HERE / "sRGB2014.icc", "rb") as iccp_file:
53-
icc_profile = PDFICCProfileObject(
53+
icc_profile = PDFICCProfile(
5454
contents=iccp_file.read(), n=3, alternate="DeviceRGB"
5555
)
5656

@@ -110,7 +110,7 @@ def test_two_output_intents(tmp_path):
110110
"""
111111
doc = FPDF()
112112
with open(HERE / "sRGB2014.icc", "rb") as iccp_file:
113-
icc_profile = PDFICCProfileObject(
113+
icc_profile = PDFICCProfile(
114114
contents=iccp_file.read(), n=3, alternate="DeviceRGB"
115115
)
116116
doc.add_output_intent(
@@ -148,7 +148,7 @@ def test_two_equal_output_intents_raises():
148148
"""
149149
doc = FPDF()
150150
with open(HERE / "sRGB2014.icc", "rb") as iccp_file:
151-
icc_profile = PDFICCProfileObject(
151+
icc_profile = PDFICCProfile(
152152
contents=iccp_file.read(), n=3, alternate="DeviceRGB"
153153
)
154154
doc.add_output_intent(

test/pdf-a/README.md

-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,3 @@ by calling those scripts from the repository root directory:
55

66
scripts/install-verapdf.sh
77
scripts/check-PDF-A-with-verapdf.sh
8-
9-
`pikepdf` also checks if the PDF **claims** to be conformant:
10-
https://pikepdf.readthedocs.io/en/latest/topics/metadata.html#checking-pdf-a-conformance
11-
It is used in unit tests by checking PDF metadata `.pdfa_status`.

test/pdf-a/test_pdf_a.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from fpdf import FPDF
55
from fpdf.enums import OutputIntentSubType
6-
from fpdf.output import PDFICCProfileObject
6+
from fpdf.output import PDFICCProfile
77
from fpdf import FPDF_VERSION
88
import pikepdf
99

@@ -35,7 +35,7 @@ def output(self, name: str, *args, **kwargs):
3535
if self.subject:
3636
self.set_subject(self.subject)
3737
super().output(name, *args, **kwargs)
38-
if hasattr(name, "name"): # => io.BufferedWriter
38+
if hasattr(name, "name"): # => io.BufferedWriter from assert_pdf_equal()
3939
name.close() # closing buffer before opening file with pikepdf (required on Windows)
4040
name = name.name
4141
with pikepdf.open(name, allow_overwriting_input=True) as pdf:
@@ -53,7 +53,6 @@ def output(self, name: str, *args, **kwargs):
5353
# meta["xmp:CreateDate"] = already done by assert_pdf_equal()
5454
meta["pdfaid:part"] = "3"
5555
meta["pdfaid:conformance"] = "B"
56-
assert meta.pdfa_status == "3B"
5756
pdf.save(deterministic_id=True)
5857

5958

@@ -83,7 +82,7 @@ def test_basic_pdfa(tmp_path):
8382
pdf.set_font(style="I")
8483
pdf.write(text="Example text in italics")
8584
with open(TUTORIAL / "sRGB2014.icc", "rb") as iccp_file:
86-
icc_profile = PDFICCProfileObject(
85+
icc_profile = PDFICCProfile(
8786
contents=iccp_file.read(), n=3, alternate="DeviceRGB"
8887
)
8988
pdf.add_output_intent(

tutorial/tuto7.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from fpdf import FPDF
55
from fpdf.enums import OutputIntentSubType
6-
from fpdf.output import PDFICCProfileObject
6+
from fpdf.output import PDFICCProfile
77
from fpdf import FPDF_VERSION
88
import pikepdf
99

@@ -43,7 +43,7 @@ def create_pdf_with_metadata(
4343
meta["xmp:CreateDate"] = datetime.now(timezone.utc).isoformat()
4444
meta["pdfaid:part"] = "3"
4545
meta["pdfaid:conformance"] = "B"
46-
pike_pdf.save()
46+
pike_pdf.save(deterministic_id=True)
4747

4848

4949
pdf = FPDF()
@@ -62,9 +62,7 @@ def create_pdf_with_metadata(
6262

6363
# set Output Intents
6464
with open(DIR / "sRGB2014.icc", "rb") as iccp_file:
65-
icc_profile = PDFICCProfileObject(
66-
contents=iccp_file.read(), n=3, alternate="DeviceRGB"
67-
)
65+
icc_profile = PDFICCProfile(contents=iccp_file.read(), n=3, alternate="DeviceRGB")
6866
pdf.add_output_intent(
6967
OutputIntentSubType.PDFA,
7068
"sRGB",

0 commit comments

Comments
 (0)