File: __init__.py

package info (click to toggle)
weasyprint 62.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,964 kB
  • sloc: python: 54,652; makefile: 12
file content (134 lines) | stat: -rw-r--r-- 5,056 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""Test the final, drawn results and compare PNG images pixel per pixel."""

import io
from itertools import zip_longest
from pathlib import Path

from PIL import Image

from ..testing_utils import FakeHTML, resource_path

# NOTE: "r" is not half red on purpose. In the pixel strings it has
# better contrast with "B" than does "R". eg. "rBBBrrBrB" vs "RBBBRRBRB".
PIXELS_BY_CHAR = {
    '_': (255, 255, 255),  # white
    'R': (255, 0, 0),  # red
    'B': (0, 0, 255),  # blue
    'G': (0, 255, 0),  # lime green
    'V': (191, 0, 64),  # average of 1*B and 3*R
    'S': (255, 63, 63),  # R above R above _
    'C': (0, 255, 255),  # cyan
    'M': (255, 0, 255),  # magenta
    'Y': (255, 255, 0),  # yellow
    'K': (0, 0, 0),  # black
    'r': (255, 0, 0),  # red
    'g': (0, 128, 0),  # half green
    'b': (0, 0, 128),  # half blue
    'v': (128, 0, 128),  # average of B and R
    's': (255, 127, 127),  # R above _
    't': (127, 255, 127),  # G above _
    'u': (128, 0, 127),  # r above B above _
    'h': (64, 0, 64),  # half average of B and R
    'a': (0, 0, 254),  # R in lossy JPG
    'p': (192, 0, 63),  # R above R above B above _
    'z': None,
}


def parse_pixels(pixels):
    lines = (line.split('#')[0].strip() for line in pixels.splitlines())
    lines = tuple(line for line in lines if line)
    widths = {len(line) for line in lines}
    assert len(widths) == 1, 'All lines of pixels must have the same width'
    width = widths.pop()
    height = len(lines)
    pixels = tuple(PIXELS_BY_CHAR[char] for line in lines for char in line)
    return width, height, pixels


def assert_pixels(name, expected_pixels, html):
    """Helper testing the size of the image and the pixels values."""
    expected_width, expected_height, expected_pixels = parse_pixels(
        expected_pixels)
    width, height, pixels = html_to_pixels(html)
    assert (expected_width, expected_height) == (width, height), (
        'Images do not have the same sizes:\n'
        f'- expected: {expected_width} × {expected_height}\n'
        f'- result: {width} × {height}')
    assert_pixels_equal(name, width, height, pixels, expected_pixels)


def assert_same_renderings(name, *documents, tolerance=0):
    """Render HTML documents to PNG and check that they're the same."""
    pixels_list = []

    for html in documents:
        width, height, pixels = html_to_pixels(html)
        pixels_list.append(pixels)

    reference = pixels_list[0]
    for i, pixels in enumerate(pixels_list[1:], start=1):
        assert_pixels_equal(
            f'{name}_{i}', width, height, pixels, reference, tolerance)


def assert_different_renderings(name, *documents):
    """Render HTML documents to PNG and check that they’re different."""
    pixels_list = []

    for html in documents:
        width, height, pixels = html_to_pixels(html)
        pixels_list.append(pixels)

    for i, pixels_1 in enumerate(pixels_list, start=1):
        for j, pixels_2 in enumerate(pixels_list[i:], start=i+1):
            if pixels_1 == pixels_2:  # pragma: no cover
                name_1, name_2 = f'{name}_{i}', f'{name}_{j}'
                write_png(name_1, pixels_1, width, height)
                assert False, f'{name_1} and {name_2} are the same'


def assert_pixels_equal(name, width, height, raw, expected_raw, tolerance=0):
    """Take 2 matrices of pixels and assert that they are the same."""
    if raw != expected_raw:  # pragma: no cover
        pixels = zip_longest(raw, expected_raw, fillvalue=(-1, -1, -1))
        for i, (value, expected) in enumerate(pixels):
            if expected is None:
                continue
            if any(abs(value - expected) > tolerance
                   for value, expected in zip(value, expected)):
                actual_height = len(raw) // width
                write_png(name, raw, width, actual_height)
                expected_raw = [
                    pixel or (255, 255, 255) for pixel in expected_raw]
                write_png(f'{name}.expected', expected_raw, width, height)
                x = i % width
                y = i // width
                assert 0, (
                    f'Pixel ({x}, {y}) in {name}: '
                    f'expected rgba{expected}, 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')


def html_to_pixels(html):
    """Render an HTML document to PNG, checks its size and return pixel data.

    Also return the document to aid debugging.

    """
    document = FakeHTML(string=html, base_url=resource_path('<dummy>'))
    return document_to_pixels(document)


def document_to_pixels(document):
    """Render an HTML document to PNG, check its size and return pixel data."""
    image = Image.open(io.BytesIO(document.write_png()))
    return image.width, image.height, image.getdata()