Replace GPLv3-derived PDF/A template with PostScript generator

This commit is contained in:
James R. Barlow
2020-08-05 01:30:45 -07:00
parent aa0ec40102
commit 8c90f7c972
+50 -29
View File
@@ -1,4 +1,4 @@
# © 2015 James R. Barlow: github.com/jbarlow83 # © 2020 James R. Barlow: github.com/jbarlow83
# #
# This Source Code Form is subject to the terms of the Mozilla Public # This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,8 +11,7 @@ Utilities for PDF/A production and confirmation with Ghostspcript.
import base64 import base64
from pathlib import Path from pathlib import Path
from string import Template from typing import Dict, Iterator, Union
from typing import Dict, Union
import pikepdf import pikepdf
import pkg_resources import pkg_resources
@@ -22,29 +21,56 @@ ICC_PROFILE_RELPATH = 'data/sRGB.icc'
SRGB_ICC_PROFILE = pkg_resources.resource_filename('ocrmypdf', ICC_PROFILE_RELPATH) SRGB_ICC_PROFILE = pkg_resources.resource_filename('ocrmypdf', ICC_PROFILE_RELPATH)
# This is a template written in PostScript which is needed to create PDF/A def _postscript_objdef(
# files, from the Ghostscript documentation. Lines beginning with % are alias: str,
# comments. Python substitution variables have a '$' prefix. dictionary: Dict[str, str],
pdfa_def_template = u"""%! *,
% Define an ICC profile : stream_name: str = None,
/ICCProfile $icc_profile stream_data: bytes = None,
def ) -> Iterator[str]:
assert (stream_name is None) == (stream_data is None)
[/_objdef {icc_PDFA} /type /stream /OBJ pdfmark objtype = '/stream' if stream_name else '/dict'
[{icc_PDFA} << /N 3 >> /PUT pdfmark
[{icc_PDFA} ICCProfile /PUT pdfmark
% Define the output intent dictionary : if stream_name:
a85_data = base64.a85encode(stream_data, adobe=True).decode('ascii')
yield f'{stream_name} ' + a85_data
yield 'def'
[/_objdef {OutputIntent_PDFA} /type /dict /OBJ pdfmark if alias != '{Catalog}': # Catalog needs no definition
[{OutputIntent_PDFA} << yield f'[/_objdef {alias} /type {objtype} /OBJ pdfmark'
/Type /OutputIntent % Must be so (the standard requires).
/S /GTS_PDFA1 % Must be so (the standard requires). yield f'[{alias} <<'
/DestOutputProfile {icc_PDFA} % Must be so (see above). for key, val in dictionary.items():
/OutputConditionIdentifier ($icc_identifier) yield f' {key} {val}'
>> /PUT pdfmark yield '>> /PUT pdfmark'
[{Catalog} <</OutputIntents [ {OutputIntent_PDFA} ]>> /PUT pdfmark
""" if stream_name:
yield f'[{alias} {stream_name[1:]} /PUT pdfmark'
def _make_postscript(icc_name: str, icc_data: bytes, colors: int) -> Iterator[str]:
yield '%!'
yield from _postscript_objdef(
'{icc_PDFA}', # Not an f-string
{'/N': str(colors)},
stream_name='/ICCProfile',
stream_data=icc_data,
)
yield ''
yield from _postscript_objdef(
'{OutputIntent_PDFA}',
{
'/Type': '/OutputIntent',
'/S': '/GTS_PDFA1',
'/DestOutputProfile': '{icc_PDFA}',
'/OutputConditionIdentifier': f'({icc_name})', # Only f-string
},
)
yield ''
yield from _postscript_objdef(
'{Catalog}', {'/OutputIntents': '[ {OutputIntent_PDFA} ]'}
)
def generate_pdfa_ps(target_filename: Path, icc: str = 'sRGB'): def generate_pdfa_ps(target_filename: Path, icc: str = 'sRGB'):
@@ -75,13 +101,8 @@ def generate_pdfa_ps(target_filename: Path, icc: str = 'sRGB'):
else: else:
raise NotImplementedError("Only supporting sRGB") raise NotImplementedError("Only supporting sRGB")
# Read the ICC profile, encode as ASCII85 and convert to a string which we
# will insert in the .ps file
bytes_icc_profile = Path(icc_profile).read_bytes() bytes_icc_profile = Path(icc_profile).read_bytes()
icc_profile = base64.a85encode(bytes_icc_profile, adobe=True).decode('ascii') ps = '\n'.join(_make_postscript(icc, bytes_icc_profile, 3))
t = Template(pdfa_def_template)
ps = t.substitute(icc_profile=icc_profile, icc_identifier=icc)
# We should have encoded everything to pure ASCII by this point, and # We should have encoded everything to pure ASCII by this point, and
# to be safe, only allow ASCII in PostScript # to be safe, only allow ASCII in PostScript