From 5c8b3a2433993b900e8e77fad24864a58548d6e5 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Tue, 9 Jul 2024 11:54:38 +0200 Subject: [PATCH] Add option to include default sRGB profile for RGB colors --- tests/test_api.py | 10 ++++++++++ weasyprint/__init__.py | 4 ++++ weasyprint/__main__.py | 3 +++ weasyprint/pdf/__init__.py | 21 +++++++++++++++++++++ weasyprint/pdf/pdfa.py | 38 +++++++------------------------------- 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 009b3b71..ad25c3a9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -521,6 +521,16 @@ def test_partial_pdf_custom_metadata(): assert b'value' in stdout +def test_pdf_srgb(): + stdout = _run('--srgb --uncompressed-pdf - -', b'test') + assert b'sRGB' in stdout + + +def test_pdf_no_srgb(): + stdout = _run('--uncompressed-pdf - -', b'test') + assert b'sRGB' not in stdout + + @pytest.mark.parametrize('html, fields', ( ('', ['/Tx', '/V ()']), ('', ['/Tx', '/V ()']), diff --git a/weasyprint/__init__.py b/weasyprint/__init__.py index 2908524e..03aff07d 100644 --- a/weasyprint/__init__.py +++ b/weasyprint/__init__.py @@ -45,6 +45,9 @@ VERSION = __version__ = '62.3' #: Whether custom HTML metadata should be stored in the generated PDF. #: :param bool presentational_hints: #: Whether HTML presentational hints are followed. +#: :param bool srgb: +#: Whether sRGB color profile should be included and set as default for +#: device-dependant RGB colors. #: :param bool optimize_images: #: Whether size of embedded images should be optimized, with no quality #: loss. @@ -71,6 +74,7 @@ DEFAULT_OPTIONS = { 'uncompressed_pdf': False, 'custom_metadata': False, 'presentational_hints': False, + 'srgb': False, 'optimize_images': False, 'jpeg_quality': None, 'dpi': None, diff --git a/weasyprint/__main__.py b/weasyprint/__main__.py index fcb3ff05..afb48ab9 100644 --- a/weasyprint/__main__.py +++ b/weasyprint/__main__.py @@ -98,6 +98,9 @@ PARSER.add_argument( PARSER.add_argument( '-p', '--presentational-hints', action='store_true', help='follow HTML presentational hints') +PARSER.add_argument( + '--srgb', action='store_true', + help='include sRGB color profile') PARSER.add_argument( '--optimize-images', action='store_true', help='optimize size of embedded images with no quality loss') diff --git a/weasyprint/pdf/__init__.py b/weasyprint/pdf/__init__.py index b6f2b2b2..15d12e81 100644 --- a/weasyprint/pdf/__init__.py +++ b/weasyprint/pdf/__init__.py @@ -1,5 +1,7 @@ """PDF generation management.""" +from importlib.resources import files + import pydyf from .. import VERSION, Attachment @@ -116,11 +118,14 @@ def generate_pdf(document, target, zoom, **options): # Set properties according to PDF variants mark = False + srgb = options['srgb'] variant = options['pdf_variant'] if variant: variant_function, properties = VARIANTS[variant] if 'mark' in properties: mark = properties['mark'] + if 'srgb' in properties: + srgb = properties['srgb'] pdf = pydyf.PDF() states = pydyf.Dictionary() @@ -294,6 +299,22 @@ def generate_pdf(document, target, zoom, **options): pdf.catalog['Names'] = pydyf.Dictionary() pdf.catalog['Names']['Dests'] = dests + if srgb: + # Add ICC profile. + profile = pydyf.Stream( + [(files(__package__) / 'sRGB2014.icc').read_bytes()], + pydyf.Dictionary({'N': 3, 'Alternate': '/DeviceRGB'}), + compress=compress) + pdf.add_object(profile) + pdf.catalog['OutputIntents'] = pydyf.Array([ + pydyf.Dictionary({ + 'Type': '/OutputIntent', + 'S': '/GTS_PDFA1', + 'OutputConditionIdentifier': pydyf.String('sRGB IEC61966-2.1'), + 'DestOutputProfile': profile.reference, + }), + ]) + # Apply PDF variants functions if variant: variant_function( diff --git a/weasyprint/pdf/pdfa.py b/weasyprint/pdf/pdfa.py index 974c8acb..4f069e2b 100644 --- a/weasyprint/pdf/pdfa.py +++ b/weasyprint/pdf/pdfa.py @@ -1,15 +1,5 @@ """PDF/A generation.""" -try: - # Available in Python 3.9+ - from importlib.resources import files -except ImportError: - # Deprecated in Python 3.11+ - from importlib.resources import read_binary -else: - def read_binary(package, resource): - return (files(package) / resource).read_bytes() - from functools import partial import pydyf @@ -20,20 +10,6 @@ from .metadata import add_metadata def pdfa(pdf, metadata, document, page_streams, attachments, compress, version, variant): """Set metadata for PDF/A documents.""" - # Add ICC profile. - profile = pydyf.Stream( - [read_binary(__package__, 'sRGB2014.icc')], - pydyf.Dictionary({'N': 3, 'Alternate': '/DeviceRGB'}), - compress=compress) - pdf.add_object(profile) - pdf.catalog['OutputIntents'] = pydyf.Array([ - pydyf.Dictionary({ - 'Type': '/OutputIntent', - 'S': '/GTS_PDFA1', - 'OutputConditionIdentifier': pydyf.String('sRGB IEC61966-2.1'), - 'DestOutputProfile': profile.reference, - }), - ]) # Handle attachments. if version == 1: @@ -95,23 +71,23 @@ def pdfa(pdf, metadata, document, page_streams, attachments, compress, VARIANTS = { 'pdf/a-1b': ( partial(pdfa, version=1, variant='B'), - {'version': '1.4', 'identifier': True}), + {'version': '1.4', 'identifier': True, 'srgb': True}), 'pdf/a-2b': ( partial(pdfa, version=2, variant='B'), - {'version': '1.7', 'identifier': True}), + {'version': '1.7', 'identifier': True, 'srgb': True}), 'pdf/a-3b': ( partial(pdfa, version=3, variant='B'), - {'version': '1.7', 'identifier': True}), + {'version': '1.7', 'identifier': True, 'srgb': True}), 'pdf/a-4b': ( partial(pdfa, version=4, variant='B'), - {'version': '2.0', 'identifier': True}), + {'version': '2.0', 'identifier': True, 'srgb': True}), 'pdf/a-2u': ( partial(pdfa, version=2, variant='U'), - {'version': '1.7', 'identifier': True}), + {'version': '1.7', 'identifier': True, 'srgb': True}), 'pdf/a-3u': ( partial(pdfa, version=3, variant='U'), - {'version': '1.7', 'identifier': True}), + {'version': '1.7', 'identifier': True, 'srgb': True}), 'pdf/a-4u': ( partial(pdfa, version=4, variant='U'), - {'version': '2.0', 'identifier': True}), + {'version': '2.0', 'identifier': True, 'srgb': True}), }