From f2038241cc91dbeee206151c1c17a3c42c5e1968 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 27 Oct 2021 05:02:19 +0200 Subject: Adding upstream version 0.1.1. Signed-off-by: Daniel Baumann --- tests/__init__.py | 80 ++++++ tests/test_pydyf.py | 708 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 788 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_pydyf.py (limited to 'tests') diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..ae5cf6a --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,80 @@ +""" +Test suite for pydyf. + +This module adds a PNG export based on GhostScript, that is released under +AGPL. As "the end user has the ability to opt out of installing the AGPL +version of [Ghostscript] during the install process", and with explicit +aggreement from Artifex, it is OK to distribute this code under BSD. + +See https://www.ghostscript.com/license.html. + +""" + +import io +import os +from pathlib import Path +from subprocess import PIPE, run + +from PIL import Image + +PIXELS_BY_CHAR = dict( + _=(255, 255, 255), # white + R=(255, 0, 0), # red + B=(0, 0, 255), # blue + G=(0, 255, 0), # lime green + K=(0, 0, 0), # black + z=None, # any color +) + + +def assert_pixels(document, reference_pixels): + """Test that the rendered document matches the reference pixels.""" + + # Transform the PDF document into a list of RGB tuples + pdf = io.BytesIO() + document.write(pdf) + command = [ + 'gs', '-q', '-dNOPAUSE', '-dSAFER', '-sDEVICE=png16m', + '-r576', '-dDownScaleFactor=8', '-sOutputFile=-', '-'] + png = run(command, input=pdf.getvalue(), stdout=PIPE).stdout + image = Image.open(io.BytesIO(png)) + pixels = image.getdata() + + # Transform reference drawings into a list of RGB tuples + lines = tuple( + line.strip() for line in reference_pixels.splitlines() if line.strip()) + assert len({len(line) for line in lines}) == 1, ( + 'The lines of reference pixels don’t have the same length') + width, height = len(lines[0]), len(lines) + assert (width, height) == image.size, ( + f'Reference size is {width}×{height}, ' + f'output size is {image.width}×{image.height}') + reference_pixels = tuple( + PIXELS_BY_CHAR[char] for line in lines for char in line) + + # Compare pixels + if pixels != reference_pixels: # pragma: no cover + for i, (value, reference) in enumerate(zip(pixels, reference_pixels)): + if reference is None: + continue + if any(value != reference + for value, reference in zip(value, reference)): + name = os.environ.get('PYTEST_CURRENT_TEST') + name = name.split(':')[-1].split(' ')[0] + write_png(f'{name}', pixels, width, height) + reference_pixels = [ + pixel or (255, 255, 255) for pixel in reference_pixels] + write_png(f'{name}-reference', reference_pixels, width, height) + x, y = i % width, i // width + assert 0, ( + f'Pixel ({x}, {y}) in {name}: ' + f'reference rgba{reference}, got rgba{value}') + + +def write_png(name, pixels, width, height): # pragma: no cover + """Take a pixel matrix and write a PNG file.""" + directory = Path(__file__).parent / 'results' + directory.mkdir(exist_ok=True) + image = Image.new('RGB', (width, height)) + image.putdata(pixels) + image.save(directory / f'{name}.png') diff --git a/tests/test_pydyf.py b/tests/test_pydyf.py new file mode 100644 index 0000000..87d9763 --- /dev/null +++ b/tests/test_pydyf.py @@ -0,0 +1,708 @@ +import pydyf + +from . import assert_pixels + + +def test_fill(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.fill() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __________ + __________ + ''') + + +def test_stroke(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.set_line_width(2) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + _KKKKKKK__ + _KKKKKKK__ + _KK___KK__ + _KK___KK__ + _KK___KK__ + _KK___KK__ + _KKKKKKK__ + _KKKKKKK__ + __________ + ''') + + +def test_line_to(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 2) + draw.set_line_width(2) + draw.line_to(2, 5) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + __________ + _KK_______ + _KK_______ + _KK_______ + __________ + __________ + ''') + + +def test_set_color_rgb_stroke(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.set_line_width(2) + draw.set_color_rgb(0, 0, 255, stroke=True) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + _BBBBBBB__ + _BBBBBBB__ + _BB___BB__ + _BB___BB__ + _BB___BB__ + _BB___BB__ + _BBBBBBB__ + _BBBBBBB__ + __________ + ''') + + +def test_set_color_rgb_fill(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.set_color_rgb(255, 0, 0) + draw.fill() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __RRRRR___ + __RRRRR___ + __RRRRR___ + __RRRRR___ + __RRRRR___ + __RRRRR___ + __________ + __________ + ''') + + +def test_set_dash(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 2) + draw.set_line_width(2) + draw.line_to(2, 6) + draw.set_dash([2, 1], 0) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + _KK_______ + __________ + _KK_______ + _KK_______ + __________ + __________ + ''') + + +def test_curve_to(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 5) + draw.set_line_width(2) + draw.curve_to(2, 5, 3, 5, 5, 5) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + __KKK_____ + __KKK_____ + __________ + __________ + __________ + __________ + ''') + + +def test_curve_start_to(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 5) + draw.set_line_width(2) + draw.curve_start_to(3, 5, 5, 5) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + __KKK_____ + __KKK_____ + __________ + __________ + __________ + __________ + ''') + + +def test_curve_end_to(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 5) + draw.set_line_width(2) + draw.curve_end_to(3, 5, 5, 5) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + __KKK_____ + __KKK_____ + __________ + __________ + __________ + __________ + ''') + + +def test_transform(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 2) + draw.set_line_width(2) + draw.line_to(2, 5) + draw.transform(1, 0, 0, 1, 1, 1) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + __KK______ + __KK______ + __KK______ + __________ + __________ + __________ + ''') + + +def test_set_state(): + document = pydyf.PDF() + + graphic_state = pydyf.Dictionary({ + 'Type': '/ExtGState', + 'LW': 2, + }) + document.add_object(graphic_state) + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.set_state('GS') + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + 'Resources': pydyf.Dictionary({ + 'ExtGState': pydyf.Dictionary({'GS': graphic_state.reference}), + }), + })) + + assert_pixels(document, ''' + __________ + _KKKKKKK__ + _KKKKKKK__ + _KK___KK__ + _KK___KK__ + _KK___KK__ + _KK___KK__ + _KKKKKKK__ + _KKKKKKK__ + __________ + ''') + + +def test_fill_and_stroke(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.set_line_width(2) + draw.set_color_rgb(0, 0, 255, stroke=True) + draw.fill_and_stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + _BBBBBBB__ + _BBBBBBB__ + _BBKKKBB__ + _BBKKKBB__ + _BBKKKBB__ + _BBKKKBB__ + _BBBBBBB__ + _BBBBBBB__ + __________ + ''') + + +def test_clip(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(3, 3, 5, 6) + draw.rectangle(4, 3, 2, 6) + draw.clip() + draw.end() + draw.move_to(0, 5) + draw.line_to(10, 5) + draw.set_color_rgb(255, 0, 0, stroke=True) + draw.set_line_width(2) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + ___RRRRR__ + ___RRRRR__ + __________ + __________ + __________ + __________ + ''') + + +def test_clip_even_odd(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(3, 3, 5, 6) + draw.rectangle(4, 3, 2, 6) + draw.clip(even_odd=True) + draw.end() + draw.move_to(0, 5) + draw.line_to(10, 5) + draw.set_color_rgb(255, 0, 0, stroke=True) + draw.set_line_width(2) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __________ + __________ + ___R__RR__ + ___R__RR__ + __________ + __________ + __________ + __________ + ''') + + +def test_close(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 2) + draw.line_to(2, 8) + draw.line_to(7, 8) + draw.line_to(7, 2) + draw.close() + draw.set_color_rgb(0, 0, 255, stroke=True) + draw.set_line_width(2) + draw.stroke() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + _BBBBBBB__ + _BBBBBBB__ + _BB___BB__ + _BB___BB__ + _BB___BB__ + _BB___BB__ + _BBBBBBB__ + _BBBBBBB__ + __________ + ''') + + +def test_stroke_and_close(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 2) + draw.line_to(2, 8) + draw.line_to(7, 8) + draw.line_to(7, 2) + draw.set_color_rgb(0, 0, 255, stroke=True) + draw.set_line_width(2) + draw.stroke_and_close() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + _BBBBBBB__ + _BBBBBBB__ + _BB___BB__ + _BB___BB__ + _BB___BB__ + _BB___BB__ + _BBBBBBB__ + _BBBBBBB__ + __________ + ''') + + +def test_fill_stroke_and_close(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.move_to(2, 2) + draw.line_to(2, 8) + draw.line_to(7, 8) + draw.line_to(7, 2) + draw.set_color_rgb(255, 0, 0) + draw.set_color_rgb(0, 0, 255, stroke=True) + draw.set_line_width(2) + draw.fill_stroke_and_close() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + _BBBBBBB__ + _BBBBBBB__ + _BBRRRBB__ + _BBRRRBB__ + _BBRRRBB__ + _BBRRRBB__ + _BBBBBBB__ + _BBBBBBB__ + __________ + ''') + + +def test_push_pop_state(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.push_state() + draw.rectangle(4, 4, 2, 2) + draw.set_color_rgb(255, 0, 0) + draw.pop_state() + draw.fill() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __________ + __________ + ''') + + +def test_types(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2.0, '5', b'6') + draw.set_line_width(2.3456) + draw.fill() + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __________ + __________ + ''') + + +def test_compress(): + document = pydyf.PDF() + + draw = pydyf.Stream() + draw.rectangle(2, 2, 5, 6) + draw.fill() + assert b'2 2 5 6' in draw.data + + draw = pydyf.Stream(compress=True) + draw.rectangle(2, 2, 5, 6) + draw.fill() + assert b'2 2 5 6' not in draw.data + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + })) + + assert_pixels(document, ''' + __________ + __________ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __KKKKK___ + __________ + __________ + ''') + + +def test_text(): + document = pydyf.PDF() + + font = pydyf.Dictionary({ + 'Type': '/Font', + 'Subtype': '/Type1', + 'Name': '/F1', + 'BaseFont': '/Helvetica', + 'Encoding': '/MacRomanEncoding', + }) + document.add_object(font) + + draw = pydyf.Stream() + draw.begin_text() + draw.set_font_size('F1', 200) + draw.text_matrix(1, 0, 0, 1, -20, 5) + draw.show_text(pydyf.String('l')) + draw.show_text(pydyf.String('É')) + draw.end_text() + + document.add_object(draw) + + document.add_page(pydyf.Dictionary({ + 'Type': '/Page', + 'Parent': document.pages.reference, + 'Contents': draw.reference, + 'MediaBox': pydyf.Array([0, 0, 10, 10]), + 'Resources': pydyf.Dictionary({ + 'ProcSet': pydyf.Array(['/PDF', '/Text']), + 'Font': pydyf.Dictionary({'F1': font.reference}), + }), + })) + + assert_pixels(document, ''' + KKKKKKKKKK + KKKKKKKKKK + KKKKKKKKKK + KKKKKKKKKK + KKKKKKKKKK + __________ + __________ + __________ + __________ + __________ + ''') + + +def test_string_encoding(): + assert pydyf.String('abc').data == b'(abc)' + assert pydyf.String('déf').data == b'' + assert pydyf.String('♡').data == b'' -- cgit v1.2.3