debian-weasyprint/weasyprint/tests/test_draw/__init__.py

166 lines
6.1 KiB
Python

"""
weasyprint.tests.test_draw
--------------------------
Test the final, drawn results and compare PNG images pixel per pixel.
:copyright: Copyright 2011-2019 Simon Sapin and contributors, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import sys
import cairocffi as cairo
from ..testing_utils import FakeHTML, resource_filename
# RGBA to native-endian ARGB
as_pixel = (
lambda x: x[:-1][::-1] + x[-1:]
if sys.byteorder == 'little' else
lambda x: x[-1:] + x[:-1])
PIXELS_BY_CHAR = dict(
_=as_pixel(b'\xff\xff\xff\xff'), # white
R=as_pixel(b'\xff\x00\x00\xff'), # red
B=as_pixel(b'\x00\x00\xff\xff'), # blue
G=as_pixel(b'\x00\xff\x00\xff'), # lime green
V=as_pixel(b'\xBF\x00\x40\xff'), # average of 1*B and 3*R.
S=as_pixel(b'\xff\x3f\x3f\xff'), # R above R above #fff
r=as_pixel(b'\xff\x00\x00\xff'), # red
g=as_pixel(b'\x00\x80\x00\xff'), # half green
b=as_pixel(b'\x00\x00\x80\xff'), # half blue
v=as_pixel(b'\x80\x00\x80\xff'), # average of B and R.
h=as_pixel(b'\x40\x00\x40\xff'), # half average of B and R.
a=as_pixel(b'\x00\x00\xfe\xff'), # JPG is lossy...
p=as_pixel(b'\xc0\x00\x3f\xff'), # R above R above B above #fff.
)
# 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".
def parse_pixels(pixels, pixels_overrides=None):
chars = dict(PIXELS_BY_CHAR, **(pixels_overrides or {}))
lines = [line.split('#')[0].strip() for line in pixels.splitlines()]
return [b''.join(chars[char] for char in line) for line in lines if line]
def assert_pixels(name, expected_width, expected_height, expected_pixels,
html):
"""Helper testing the size of the image and the pixels values."""
if isinstance(expected_pixels, str):
expected_pixels = parse_pixels(expected_pixels)
assert len(expected_pixels) == expected_height
assert len(expected_pixels[0]) == expected_width * 4
expected_raw = b''.join(expected_pixels)
_doc, pixels = html_to_pixels(name, expected_width, expected_height, html)
assert_pixels_equal(
name, expected_width, expected_height, pixels, expected_raw)
def assert_same_rendering(expected_width, expected_height, documents,
tolerance=0):
"""Render HTML documents to PNG and check that they render the same.
Each document is passed as a (name, html_source) tuple.
"""
pixels_list = []
for name, html in documents:
_doc, pixels = html_to_pixels(
name, expected_width, expected_height, html)
pixels_list.append((name, pixels))
_name, reference = pixels_list[0]
for name, pixels in pixels_list[1:]:
assert_pixels_equal(name, expected_width, expected_height,
reference, pixels, tolerance)
def assert_different_renderings(expected_width, expected_height, documents):
"""Render HTML documents to PNG and check that they don't render the same.
Each document is passed as a (name, html_source) tuple.
"""
pixels_list = []
for name, html in documents:
_doc, pixels = html_to_pixels(
name, expected_width, expected_height, html)
pixels_list.append((name, pixels))
for i, (name_1, pixels_1) in enumerate(pixels_list):
for name_2, pixels_2 in pixels_list[i + 1:]:
if pixels_1 == pixels_2: # pragma: no cover
write_png(name_1, pixels_1, expected_width, expected_height)
# Same as "assert pixels_1 != pixels_2" but the output of
# the assert hook would be gigantic and useless.
assert False, '%s and %s are the same' % (name_1, name_2)
def write_png(basename, pixels, width, height): # pragma: no cover
"""Take a pixel matrix and write a PNG file."""
directory = os.path.join(os.path.dirname(__file__), 'results')
if not os.path.isdir(directory):
os.mkdir(directory)
filename = os.path.join(directory, basename + '.png')
cairo.ImageSurface(
cairo.FORMAT_ARGB32, width, height,
data=bytearray(pixels), stride=width * 4
).write_to_png(filename)
def html_to_pixels(name, expected_width, expected_height, 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,
# Dummy filename, but in the right directory.
base_url=resource_filename('<test>'))
pixels = document_to_pixels(
document, name, expected_width, expected_height)
return document, pixels
def document_to_pixels(document, name, expected_width, expected_height):
"""Render an HTML document to PNG, check its size and return pixel data."""
surface = document.write_image_surface()
return image_to_pixels(surface, expected_width, expected_height)
def image_to_pixels(surface, width, height):
assert (surface.get_width(), surface.get_height()) == (width, height)
# RGB24 is actually the same as ARGB32, with A unused.
assert surface.get_format() in (cairo.FORMAT_ARGB32, cairo.FORMAT_RGB24)
pixels = surface.get_data()[:]
assert len(pixels) == width * height * 4
return pixels
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
for i, (value, expected) in enumerate(zip(raw, expected_raw)):
if abs(value - expected) > tolerance:
write_png(name, raw, width, height)
write_png(name + '.expected', expected_raw,
width, height)
pixel_n = i // 4
x = pixel_n // width
y = pixel_n % width
i % 4
pixel = tuple(list(raw[i:i + 4]))
expected_pixel = tuple(list(
expected_raw[i:i + 4]))
assert 0, (
'Pixel (%i, %i) in %s: expected rgba%s, got rgba%s'
% (x, y, name, expected_pixel, pixel))