1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-08-17 00:20:42 +03:00

Clean more and more tests

This commit is contained in:
Guillaume Ayoub 2018-03-18 22:23:39 +01:00
parent cffa1f35d4
commit 6a35d779a1
11 changed files with 3996 additions and 3684 deletions

2
.gitignore vendored
View File

@ -19,4 +19,4 @@
# Tests
/weasyprint/tests/.cache
/weasyprint/tests/results
/weasyprint/tests/test_draw/results

View File

@ -9,41 +9,27 @@
"""
import contextlib
import gzip
import io
import math
import os
import sys
import threading
import unicodedata
import zlib
from urllib.parse import urljoin, uses_relative
import cairocffi as cairo
import py
import pytest
from pdfrw import PdfReader
from .. import CSS, HTML, __main__, default_url_fetcher
from ..urls import path2url
from .test_draw import B, _, assert_pixels_equal, image_to_pixels, r
from .test_draw import assert_pixels_equal, image_to_pixels
from .test_draw.test_draw import B, _, r
from .testing_utils import (
FakeHTML, assert_no_logs, capture_logs, http_server, resource_filename)
CHDIR_LOCK = threading.Lock()
@contextlib.contextmanager
def chdir(path):
"""Change the current directory in a context manager."""
with CHDIR_LOCK:
old_dir = os.getcwd()
try:
os.chdir(path)
yield
finally:
os.chdir(old_dir)
def _test_resource(class_, basename, check, **kwargs):
"""Common code for testing the HTML and CSS classes."""
@ -63,13 +49,13 @@ def _test_resource(class_, basename, check, **kwargs):
check(class_(file_obj=fd, **kwargs))
with open(absolute_filename, 'rb') as fd:
content = fd.read()
with chdir(os.path.dirname(__file__)):
relative_filename = os.path.join('resources', basename)
check(class_(relative_filename, **kwargs))
check(class_(string=content, base_url=relative_filename, **kwargs))
encoding = kwargs.get('encoding') or 'utf8'
check(class_(string=content.decode(encoding), # unicode
base_url=relative_filename, **kwargs))
py.path.local(os.path.dirname(__file__)).chdir()
relative_filename = os.path.join('resources', basename)
check(class_(relative_filename, **kwargs))
check(class_(string=content, base_url=relative_filename, **kwargs))
encoding = kwargs.get('encoding') or 'utf8'
check(class_(string=content.decode(encoding), # unicode
base_url=relative_filename, **kwargs))
with pytest.raises(TypeError):
class_(filename='foo', url='bar')
@ -90,38 +76,89 @@ def _assert_equivalent_pdf(pdf_bytes1, pdf_bytes2):
assert page1.BleedBox == page2.BleedBox
def _check_doc1(html, has_base_url=True):
"""Check that a parsed HTML document looks like resources/doc1.html"""
root = html.etree_element
assert root.tag == 'html'
assert [child.tag for child in root] == ['head', 'body']
_head, body = root
assert [child.tag for child in body] == ['h1', 'p', 'ul', 'div']
h1, p, ul, div = body
assert h1.text == 'WeasyPrint test document (with Ünicōde)'
if has_base_url:
url = urljoin(html.base_url, 'pattern.png')
assert url.startswith('file:')
assert url.endswith('weasyprint/tests/resources/pattern.png')
else:
assert html.base_url is None
def _run(args, stdin=b''):
stdin = io.BytesIO(stdin)
stdout = io.BytesIO()
try:
__main__.HTML = FakeHTML
__main__.main(args.split(), stdin=stdin, stdout=stdout)
finally:
__main__.HTML = HTML
return stdout.getvalue()
class _fake_file(object):
def __init__(self):
self.chunks = []
def write(self, data):
self.chunks.append(bytes(data[:]))
def getvalue(self):
return b''.join(self.chunks)
def _png_size(result):
png_bytes, width, height = result
surface = cairo.ImageSurface.create_from_png(io.BytesIO(png_bytes))
assert (surface.get_width(), surface.get_height()) == (width, height)
return width, height
def _round_meta(pages):
"""Eliminate errors of floating point arithmetic for metadata.
(eg. 49.99999999999994 instead of 50)
"""
for page in pages:
anchors = page.anchors
for anchor_name, (pos_x, pos_y) in anchors.items():
anchors[anchor_name] = round(pos_x, 6), round(pos_y, 6)
links = page.links
for i, link in enumerate(links):
link_type, target, (pos_x, pos_y, width, height) = link
link = (
link_type, target, (round(pos_x, 6), round(pos_y, 6),
round(width, 6), round(height, 6)))
links[i] = link
bookmarks = page.bookmarks
for i, (level, label, (pos_x, pos_y)) in enumerate(bookmarks):
bookmarks[i] = level, label, (round(pos_x, 6), round(pos_y, 6))
@assert_no_logs
def test_html_parsing():
"""Test the constructor for the HTML class."""
def check_doc1(html, has_base_url=True):
"""Check that a parsed HTML document looks like resources/doc1.html"""
root = html.etree_element
assert root.tag == 'html'
assert [child.tag for child in root] == ['head', 'body']
_head, body = root
assert [child.tag for child in body] == ['h1', 'p', 'ul', 'div']
h1, p, ul, div = body
assert h1.text == 'WeasyPrint test document (with Ünicōde)'
if has_base_url:
url = urljoin(html.base_url, 'pattern.png')
assert url.startswith('file:')
assert url.endswith('weasyprint/tests/resources/pattern.png')
else:
assert html.base_url is None
_test_resource(FakeHTML, 'doc1.html', check_doc1)
_test_resource(FakeHTML, 'doc1_UTF-16BE.html', check_doc1,
_test_resource(FakeHTML, 'doc1.html', _check_doc1)
_test_resource(FakeHTML, 'doc1_UTF-16BE.html', _check_doc1,
encoding='UTF-16BE')
with chdir(os.path.dirname(__file__)):
filename = os.path.join('resources', 'doc1.html')
with open(filename) as fd:
string = fd.read()
check_doc1(FakeHTML(string=string, base_url=filename))
check_doc1(FakeHTML(string=string), has_base_url=False)
string_with_meta = string.replace(
'<meta', '<base href="resources/"><meta')
check_doc1(FakeHTML(string=string_with_meta, base_url='.'))
py.path.local(os.path.dirname(__file__)).chdir()
filename = os.path.join('resources', 'doc1.html')
with open(filename) as fd:
string = fd.read()
_check_doc1(FakeHTML(string=string, base_url=filename))
_check_doc1(FakeHTML(string=string), has_base_url=False)
string_with_meta = string.replace(
'<meta', '<base href="resources/"><meta')
_check_doc1(FakeHTML(string=string_with_meta, base_url='.'))
@assert_no_logs
@ -226,23 +263,13 @@ def test_python_render(tmpdir):
pdf_bytes = html.write_pdf(stylesheets=[css])
assert png_bytes.startswith(b'\211PNG\r\n\032\n')
assert pdf_bytes.startswith(b'%PDF')
check_png_pattern(png_bytes)
# TODO: check PDF content? How?
class fake_file(object):
def __init__(self):
self.chunks = []
def write(self, data):
self.chunks.append(bytes(data[:]))
def getvalue(self):
return b''.join(self.chunks)
png_file = fake_file()
png_file = _fake_file()
html.write_png(png_file, stylesheets=[css])
assert png_file.getvalue() == png_bytes
pdf_file = fake_file()
pdf_file = _fake_file()
html.write_pdf(pdf_file, stylesheets=[css])
_assert_equivalent_pdf(pdf_file.getvalue(), pdf_bytes)
@ -282,7 +309,6 @@ def test_python_render(tmpdir):
@assert_no_logs
def test_command_line_render(tmpdir):
"""Test rendering with the command-line API."""
css = b'''
@page { margin: 2px; size: 8px; background: #fff }
@media screen { img { transform: rotate(-90deg) } }
@ -292,30 +318,20 @@ def test_command_line_render(tmpdir):
combined = b'<style>' + css + b'</style>' + html
linked = b'<link rel=stylesheet href=style.css>' + html
with chdir(resource_filename('')):
# Reference
html_obj = FakeHTML(string=combined, base_url='dummy.html')
pdf_bytes = html_obj.write_pdf()
png_bytes = html_obj.write_png()
x2_png_bytes = html_obj.write_png(resolution=192)
rotated_png_bytes = FakeHTML(string=combined, base_url='dummy.html',
media_type='screen').write_png()
empty_png_bytes = FakeHTML(
string=b'<style>' + css + b'</style>').write_png()
py.path.local(resource_filename('')).chdir()
# Reference
html_obj = FakeHTML(string=combined, base_url='dummy.html')
pdf_bytes = html_obj.write_pdf()
png_bytes = html_obj.write_png()
x2_png_bytes = html_obj.write_png(resolution=192)
rotated_png_bytes = FakeHTML(string=combined, base_url='dummy.html',
media_type='screen').write_png()
empty_png_bytes = FakeHTML(
string=b'<style>' + css + b'</style>').write_png()
check_png_pattern(png_bytes)
check_png_pattern(rotated_png_bytes, rotated=True)
check_png_pattern(empty_png_bytes, blank=True)
def run(args, stdin=b''):
stdin = io.BytesIO(stdin)
stdout = io.BytesIO()
try:
__main__.HTML = FakeHTML
__main__.main(args.split(), stdin=stdin, stdout=stdout)
finally:
__main__.HTML = HTML
return stdout.getvalue()
tmpdir.chdir()
with open(resource_filename('pattern.png'), 'rb') as pattern_fd:
pattern_bytes = pattern_fd.read()
@ -327,72 +343,72 @@ def test_command_line_render(tmpdir):
tmpdir.join('linked.html').write_binary(linked)
tmpdir.join('style.css').write_binary(css)
run('combined.html out1.png')
run('combined.html out2.pdf')
_run('combined.html out1.png')
_run('combined.html out2.pdf')
assert tmpdir.join('out1.png').read_binary() == png_bytes
_assert_equivalent_pdf(tmpdir.join('out2.pdf').read_binary(), pdf_bytes)
run('combined-UTF-16BE.html out3.png --encoding UTF-16BE')
_run('combined-UTF-16BE.html out3.png --encoding UTF-16BE')
assert tmpdir.join('out3.png').read_binary() == png_bytes
run(tmpdir.join('combined.html').strpath + ' out4.png')
_run(tmpdir.join('combined.html').strpath + ' out4.png')
assert tmpdir.join('out4.png').read_binary() == png_bytes
run(path2url(tmpdir.join('combined.html')) + ' out5.png')
_run(path2url(tmpdir.join('combined.html')) + ' out5.png')
assert tmpdir.join('out5.png').read_binary() == png_bytes
run('linked.html out6.png') # test relative URLs
_run('linked.html out6.png') # test relative URLs
assert tmpdir.join('out6.png').read_binary() == png_bytes
run('combined.html out7 -f png')
run('combined.html out8 --format pdf')
_run('combined.html out7 -f png')
_run('combined.html out8 --format pdf')
assert tmpdir.join('out7').read_binary() == png_bytes
_assert_equivalent_pdf(tmpdir.join('out8').read_binary(), pdf_bytes)
run('no_css.html out9.png')
run('no_css.html out10.png -s style.css')
_run('no_css.html out9.png')
_run('no_css.html out10.png -s style.css')
assert tmpdir.join('out9.png').read_binary() != png_bytes
assert tmpdir.join('out10.png').read_binary() == png_bytes
stdout = run('--format png combined.html -')
stdout = _run('--format png combined.html -')
assert stdout == png_bytes
run('- out11.png', stdin=combined)
_run('- out11.png', stdin=combined)
check_png_pattern(tmpdir.join('out11.png').read_binary())
assert tmpdir.join('out11.png').read_binary() == png_bytes
stdout = run('--format png - -', stdin=combined)
stdout = _run('--format png - -', stdin=combined)
assert stdout == png_bytes
run('combined.html out13.png --media-type screen')
run('combined.html out12.png -m screen')
run('linked.html out14.png -m screen')
_run('combined.html out13.png --media-type screen')
_run('combined.html out12.png -m screen')
_run('linked.html out14.png -m screen')
assert tmpdir.join('out12.png').read_binary() == rotated_png_bytes
assert tmpdir.join('out13.png').read_binary() == rotated_png_bytes
assert tmpdir.join('out14.png').read_binary() == rotated_png_bytes
stdout = run('-f pdf combined.html -')
stdout = _run('-f pdf combined.html -')
assert stdout.count(b'attachment') == 0
stdout = run('-f pdf -a pattern.png combined.html -')
stdout = _run('-f pdf -a pattern.png combined.html -')
assert stdout.count(b'attachment') == 1
stdout = run('-f pdf -a style.css -a pattern.png combined.html -')
stdout = _run('-f pdf -a style.css -a pattern.png combined.html -')
assert stdout.count(b'attachment') == 2
stdout = run('-f png -r 192 linked.html -')
stdout = _run('-f png -r 192 linked.html -')
assert stdout == x2_png_bytes
stdout = run('-f png --resolution 192 linked.html -')
assert run('linked.html - -f png --resolution 192') == x2_png_bytes
stdout = _run('-f png --resolution 192 linked.html -')
assert _run('linked.html - -f png --resolution 192') == x2_png_bytes
assert stdout == x2_png_bytes
os.mkdir('subdirectory')
os.chdir('subdirectory')
py.path.local('subdirectory').chdir()
with capture_logs() as logs:
stdout = run('--format png - -', stdin=combined)
stdout = _run('--format png - -', stdin=combined)
assert len(logs) == 1
assert logs[0].startswith('ERROR: Failed to load image')
assert stdout == empty_png_bytes
stdout = run('--format png --base-url .. - -', stdin=combined)
stdout = _run('--format png --base-url .. - -', stdin=combined)
assert stdout == png_bytes
@ -473,17 +489,11 @@ def test_low_level_api():
assert (width, height) == (16, 16)
check_png_pattern(png_bytes, x2=True)
def png_size(result):
png_bytes, width, height = result
surface = cairo.ImageSurface.create_from_png(io.BytesIO(png_bytes))
assert (surface.get_width(), surface.get_height()) == (width, height)
return width, height
document = html.render([css], enable_hinting=True)
page, = document.pages
assert (page.width, page.height) == (8, 8)
# A resolution that is not multiple of 96:
assert png_size(document.write_png(resolution=145.2)) == (13, 13)
assert _png_size(document.write_png(resolution=145.2)) == (13, 13)
document = FakeHTML(string='''
<style>
@ -499,31 +509,10 @@ def test_low_level_api():
result = document.write_png()
# (Max of both widths, Sum of both heights)
assert png_size(result) == (6, 14)
assert _png_size(result) == (6, 14)
assert document.copy([page_1, page_2]).write_png() == result
assert png_size(document.copy([page_1]).write_png()) == (5, 10)
assert png_size(document.copy([page_2]).write_png()) == (6, 4)
def round_meta(pages):
"""Eliminate errors of floating point arithmetic for metadata.
(eg. 49.99999999999994 instead of 50)
"""
for page in pages:
anchors = page.anchors
for anchor_name, (pos_x, pos_y) in anchors.items():
anchors[anchor_name] = round(pos_x, 6), round(pos_y, 6)
links = page.links
for i, link in enumerate(links):
link_type, target, (pos_x, pos_y, width, height) = link
link = (
link_type, target, (round(pos_x, 6), round(pos_y, 6),
round(width, 6), round(height, 6)))
links[i] = link
bookmarks = page.bookmarks
for i, (level, label, (pos_x, pos_y)) in enumerate(bookmarks):
bookmarks[i] = level, label, (round(pos_x, 6), round(pos_y, 6))
assert _png_size(document.copy([page_1]).write_png()) == (5, 10)
assert _png_size(document.copy([page_2]).write_png()) == (6, 4)
@assert_no_logs
@ -531,7 +520,7 @@ def test_bookmarks():
def assert_bookmarks(html, expected_by_page, expected_tree, round=False):
document = FakeHTML(string=html).render()
if round:
round_meta(document.pages)
_round_meta(document.pages)
assert [p.bookmarks for p in document.pages] == expected_by_page
assert document.make_bookmark_tree() == expected_tree
assert_bookmarks('''
@ -670,7 +659,7 @@ def test_links():
with capture_logs() as logs:
document = FakeHTML(string=html, base_url=base_url).render()
if round:
round_meta(document.pages)
_round_meta(document.pages)
resolved_links = list(document.resolve_links())
assert len(logs) == len(warnings)
for message, expected in zip(logs, warnings):

File diff suppressed because it is too large Load Diff

View File

@ -16,8 +16,7 @@ from .testing_utils import assert_no_logs, capture_logs
@assert_no_logs
def test_font_face():
"""Test the ``font-face`` rule."""
def test_font_face_1():
stylesheet = tinycss2.parse_stylesheet(
'@font-face {'
' font-family: Gentium Hard;'
@ -32,6 +31,9 @@ def test_font_face():
assert src == (
'src', (('external', 'http://example.com/fonts/Gentium.woff'),))
@assert_no_logs
def test_font_face_2():
stylesheet = tinycss2.parse_stylesheet(
'@font-face {'
' font-family: "Fonty Smiley";'
@ -53,6 +55,9 @@ def test_font_face():
assert font_weight == ('font_weight', 200)
assert font_stretch == ('font_stretch', 'condensed')
@assert_no_logs
def test_font_face_3():
stylesheet = tinycss2.parse_stylesheet(
'@font-face {'
' font-family: Gentium Hard;'
@ -66,6 +71,9 @@ def test_font_face():
assert font_family == ('font_family', 'Gentium Hard')
assert src == ('src', (('local', None),))
@assert_no_logs
def test_font_face_4():
# See bug #487
stylesheet = tinycss2.parse_stylesheet(
'@font-face {'
@ -81,8 +89,7 @@ def test_font_face():
assert src == ('src', (('local', 'Gentium Hard'),))
def test_bad_font_face():
"""Test bad ``font-face`` rules."""
def test_font_face_bad_1():
stylesheet = tinycss2.parse_stylesheet(
'@font-face {'
' font-family: "Bad Font";'
@ -108,6 +115,8 @@ def test_bad_font_face():
'WARNING: Ignored `font-weight: bolder` at 1:111, invalid value.',
'WARNING: Ignored `font-stretch: wrong` at 1:133, invalid value.']
def test_font_face_bad_2():
stylesheet = tinycss2.parse_stylesheet('@font-face{}')
with capture_logs() as logs:
descriptors = []
@ -118,6 +127,8 @@ def test_bad_font_face():
assert logs == [
"WARNING: Missing src descriptor in '@font-face' rule at 1:1"]
def test_font_face_bad_3():
stylesheet = tinycss2.parse_stylesheet('@font-face{src: url(test.woff)}')
with capture_logs() as logs:
descriptors = []
@ -128,6 +139,8 @@ def test_bad_font_face():
assert logs == [
"WARNING: Missing font-family descriptor in '@font-face' rule at 1:1"]
def test_font_face_bad_4():
stylesheet = tinycss2.parse_stylesheet('@font-face{font-family: test}')
with capture_logs() as logs:
descriptors = []
@ -138,6 +151,8 @@ def test_bad_font_face():
assert logs == [
"WARNING: Missing src descriptor in '@font-face' rule at 1:1"]
def test_font_face_bad_5():
stylesheet = tinycss2.parse_stylesheet(
'@font-face { font-family: test; src: wrong }')
with capture_logs() as logs:
@ -150,6 +165,8 @@ def test_bad_font_face():
'WARNING: Ignored `src: wrong ` at 1:33, invalid value.',
"WARNING: Missing src descriptor in '@font-face' rule at 1:1"]
def test_font_face_bad_6():
stylesheet = tinycss2.parse_stylesheet(
'@font-face { font-family: good, bad; src: url(test.woff) }')
with capture_logs() as logs:
@ -162,6 +179,8 @@ def test_bad_font_face():
'WARNING: Ignored `font-family: good, bad` at 1:14, invalid value.',
"WARNING: Missing font-family descriptor in '@font-face' rule at 1:1"]
def test_font_face_bad_7():
stylesheet = tinycss2.parse_stylesheet(
'@font-face { font-family: good, bad; src: really bad }')
with capture_logs() as logs:

View File

@ -53,182 +53,224 @@ def test_not_print():
def test_function():
assert expand_to_dict('clip: rect(1px, 3em, auto, auto)') == {
'clip': ((1, 'px'), (3, 'em'), 'auto', 'auto')}
assert_invalid('clip: square(1px, 3em, auto, auto)')
assert_invalid('clip: rect(1px, 3em, auto auto)', 'invalid')
assert_invalid('clip: rect(1px, 3em, auto)')
assert_invalid('clip: rect(1px, 3em / auto)')
@assert_no_logs
def test_counters():
assert expand_to_dict('counter-reset: foo bar 2 baz') == {
'counter_reset': (('foo', 0), ('bar', 2), ('baz', 0))}
assert expand_to_dict('counter-increment: foo bar 2 baz') == {
'counter_increment': (('foo', 1), ('bar', 2), ('baz', 1))}
assert expand_to_dict('counter-reset: foo') == {
'counter_reset': (('foo', 0),)}
assert expand_to_dict('counter-reset: FoO') == {
'counter_reset': (('FoO', 0),)}
assert expand_to_dict('counter-increment: foo bAr 2 Bar') == {
'counter_increment': (('foo', 1), ('bAr', 2), ('Bar', 1))}
assert expand_to_dict('counter-reset: none') == {
'counter_reset': ()}
assert expand_to_dict(
'counter-reset: foo none', 'Invalid counter name') == {}
assert expand_to_dict(
'counter-reset: foo initial', 'Invalid counter name') == {}
assert_invalid('counter-reset: foo 3px')
assert_invalid('counter-reset: 3')
@pytest.mark.parametrize('rule', (
'clip: square(1px, 3em, auto, auto)',
'clip: rect(1px, 3em, auto auto)',
'clip: rect(1px, 3em, auto)',
'clip: rect(1px, 3em / auto)',
))
def test_function_invalid(rule):
assert_invalid(rule)
@assert_no_logs
def test_spacing():
assert expand_to_dict('letter-spacing: normal') == {
'letter_spacing': 'normal'}
assert expand_to_dict('letter-spacing: 3px') == {
'letter_spacing': (3, 'px')}
assert_invalid('letter-spacing: 3')
assert expand_to_dict(
'letter_spacing: normal', 'did you mean letter-spacing') == {}
@pytest.mark.parametrize('rule, result', (
('counter-reset: foo bar 2 baz', {
'counter_reset': (('foo', 0), ('bar', 2), ('baz', 0))}),
('counter-increment: foo bar 2 baz', {
'counter_increment': (('foo', 1), ('bar', 2), ('baz', 1))}),
('counter-reset: foo', {'counter_reset': (('foo', 0),)}),
('counter-reset: FoO', {'counter_reset': (('FoO', 0),)}),
('counter-increment: foo bAr 2 Bar', {
'counter_increment': (('foo', 1), ('bAr', 2), ('Bar', 1))}),
('counter-reset: none', {'counter_reset': ()}),
))
def test_counters(rule, result):
assert expand_to_dict(rule) == result
assert expand_to_dict('word-spacing: normal') == {
'word_spacing': 'normal'}
assert expand_to_dict('word-spacing: 3px') == {
'word_spacing': (3, 'px')}
assert_invalid('word-spacing: 3')
@pytest.mark.parametrize('rule, warning, result', (
('counter-reset: foo initial', 'Invalid counter name: initial.', {}),
('counter-reset: foo none', 'Invalid counter name: none.', {}),
))
def test_counters_warning(rule, warning, result):
assert expand_to_dict(rule, warning) == result
@assert_no_logs
def test_decoration():
assert expand_to_dict('text-decoration: none') == {
'text_decoration': 'none'}
assert expand_to_dict('text-decoration: overline') == {
'text_decoration': frozenset(['overline'])}
# blink is accepted but ignored
assert expand_to_dict('text-decoration: overline blink line-through') == {
'text_decoration': frozenset(['line-through', 'overline'])}
@pytest.mark.parametrize('rule', (
'counter-reset: foo 3px',
'counter-reset: 3',
))
def test_counters_invalid(rule):
assert_invalid(rule)
@assert_no_logs
def test_size():
assert expand_to_dict('size: 200px') == {
'size': ((200, 'px'), (200, 'px'))}
assert expand_to_dict('size: 200px 300pt') == {
'size': ((200, 'px'), (300, 'pt'))}
assert expand_to_dict('size: auto') == {
'size': ((210, 'mm'), (297, 'mm'))}
assert expand_to_dict('size: portrait') == {
'size': ((210, 'mm'), (297, 'mm'))}
assert expand_to_dict('size: landscape') == {
'size': ((297, 'mm'), (210, 'mm'))}
assert expand_to_dict('size: A3 portrait') == {
'size': ((297, 'mm'), (420, 'mm'))}
assert expand_to_dict('size: A3 landscape') == {
'size': ((420, 'mm'), (297, 'mm'))}
assert expand_to_dict('size: portrait A3') == {
'size': ((297, 'mm'), (420, 'mm'))}
assert expand_to_dict('size: landscape A3') == {
'size': ((420, 'mm'), (297, 'mm'))}
assert_invalid('size: A3 landscape A3')
assert_invalid('size: A9')
assert_invalid('size: foo')
assert_invalid('size: foo bar')
assert_invalid('size: 20%')
@pytest.mark.parametrize('rule, result', (
('letter-spacing: normal', {'letter_spacing': 'normal'}),
('letter-spacing: 3px', {'letter_spacing': (3, 'px')}),
('word-spacing: normal', {'word_spacing': 'normal'}),
('word-spacing: 3px', {'word_spacing': (3, 'px')}),
))
def test_spacing(rule, result):
assert expand_to_dict(rule) == result
@assert_no_logs
def test_transforms():
assert expand_to_dict('transform: none') == {
'transform': ()}
def test_spacing_warning():
assert expand_to_dict(
'transform: translate(6px) rotate(90deg)'
) == {'transform': (('translate', ((6, 'px'), (0, 'px'))),
('rotate', math.pi / 2))}
assert expand_to_dict(
'transform: translate(-4px, 0)'
) == {'transform': (('translate', ((-4, 'px'), (0, None))),)}
assert expand_to_dict(
'transform: translate(6px, 20%)'
) == {'transform': (('translate', ((6, 'px'), (20, '%'))),)}
assert expand_to_dict(
'transform: scale(2)'
) == {'transform': (('scale', (2, 2)),)}
assert_invalid('transform: translate(6px 20%)') # missing comma
assert_invalid('transform: lipsumize(6px)')
assert_invalid('transform: foo')
assert_invalid('transform: scale(2) foo')
assert_invalid('transform: 6px')
'letter_spacing: normal', 'did you mean letter-spacing?') == {}
@assert_no_logs
def test_expand_four_sides():
"""Test the 4-value properties."""
assert expand_to_dict('margin: inherit') == {
@pytest.mark.parametrize('rule', (
'letter-spacing: 3',
'word-spacing: 3',
))
def test_spacing_invalid(rule):
assert_invalid(rule)
@assert_no_logs
@pytest.mark.parametrize('rule, result', (
('text-decoration: none', {'text_decoration': 'none'}),
('text-decoration: overline', {
'text_decoration': frozenset(['overline'])}),
('text-decoration: overline blink line-through', {
'text_decoration': frozenset(['line-through', 'overline'])}),
))
def test_decoration(rule, result):
assert expand_to_dict(rule) == result
@assert_no_logs
@pytest.mark.parametrize('rule, result', (
('size: 200px', {'size': ((200, 'px'), (200, 'px'))}),
('size: 200px 300pt', {'size': ((200, 'px'), (300, 'pt'))}),
('size: auto', {'size': ((210, 'mm'), (297, 'mm'))}),
('size: portrait', {'size': ((210, 'mm'), (297, 'mm'))}),
('size: landscape', {'size': ((297, 'mm'), (210, 'mm'))}),
('size: A3 portrait', {'size': ((297, 'mm'), (420, 'mm'))}),
('size: A3 landscape', {'size': ((420, 'mm'), (297, 'mm'))}),
('size: portrait A3', {'size': ((297, 'mm'), (420, 'mm'))}),
('size: landscape A3', {'size': ((420, 'mm'), (297, 'mm'))}),
))
def test_size(rule, result):
assert expand_to_dict(rule) == result
@pytest.mark.parametrize('rule', (
'size: A3 landscape A3',
'size: A9',
'size: foo',
'size: foo bar',
'size: 20%',
))
def test_size_invalid(rule):
assert_invalid(rule)
@assert_no_logs
@pytest.mark.parametrize('rule, result', (
('transform: none', {'transform': ()}),
('transform: translate(6px) rotate(90deg)', {
'transform': (
('translate', ((6, 'px'), (0, 'px'))),
('rotate', math.pi / 2))}),
('transform: translate(-4px, 0)', {
'transform': (('translate', ((-4, 'px'), (0, None))),)}),
('transform: translate(6px, 20%)', {
'transform': (('translate', ((6, 'px'), (20, '%'))),)}),
('transform: scale(2)', {'transform': (('scale', (2, 2)),)}),
))
def test_transforms(rule, result):
assert expand_to_dict(rule) == result
@assert_no_logs
@pytest.mark.parametrize('rule', (
'transform: translate(6px 20%)', # missing comma
'transform: lipsumize(6px)',
'transform: foo',
'transform: scale(2) foo',
'transform: 6px',
))
def test_transforms_invalid(rule):
assert_invalid(rule)
@assert_no_logs
@pytest.mark.parametrize('rule, result', (
('margin: inherit', {
'margin_top': 'inherit',
'margin_right': 'inherit',
'margin_bottom': 'inherit',
'margin_left': 'inherit',
}
assert expand_to_dict('margin: 1em') == {
}),
('margin: 1em', {
'margin_top': (1, 'em'),
'margin_right': (1, 'em'),
'margin_bottom': (1, 'em'),
'margin_left': (1, 'em'),
}
assert expand_to_dict('margin: -1em auto 20%') == {
}),
('margin: -1em auto 20%', {
'margin_top': (-1, 'em'),
'margin_right': 'auto',
'margin_bottom': (20, '%'),
'margin_left': 'auto',
}
assert expand_to_dict('padding: 1em 0') == {
}),
('padding: 1em 0', {
'padding_top': (1, 'em'),
'padding_right': (0, None),
'padding_bottom': (1, 'em'),
'padding_left': (0, None),
}
assert expand_to_dict('padding: 1em 0 2%') == {
}),
('padding: 1em 0 2%', {
'padding_top': (1, 'em'),
'padding_right': (0, None),
'padding_bottom': (2, '%'),
'padding_left': (0, None),
}
assert expand_to_dict('padding: 1em 0 2em 5px') == {
}),
('padding: 1em 0 2em 5px', {
'padding_top': (1, 'em'),
'padding_right': (0, None),
'padding_bottom': (2, 'em'),
'padding_left': (5, 'px'),
}
assert expand_to_dict(
'padding: 1 2 3 4 5',
'Expected 1 to 4 token components got 5') == {}
assert_invalid('margin: rgb(0, 0, 0)')
assert_invalid('padding: auto')
assert_invalid('padding: -12px')
assert_invalid('border-width: -3em')
assert_invalid('border-width: 12%')
}),
))
def test_expand_four_sides(rule, result):
assert expand_to_dict(rule) == result
@assert_no_logs
def test_expand_borders():
"""Test the ``border`` property."""
assert expand_to_dict('border-top: 3px dotted red') == {
def test_expand_four_sides_warning():
assert expand_to_dict(
'padding: 1 2 3 4 5', 'Expected 1 to 4 token components got 5') == {}
@assert_no_logs
@pytest.mark.parametrize('rule', (
'margin: rgb(0, 0, 0)',
'padding: auto',
'padding: -12px',
'border-width: -3em',
'border-width: 12%',
))
def test_expand_four_sides_invalid(rule):
assert_invalid(rule)
@assert_no_logs
@pytest.mark.parametrize('rule, result', (
('border-top: 3px dotted red', {
'border_top_width': (3, 'px'),
'border_top_style': 'dotted',
'border_top_color': (1, 0, 0, 1), # red
}
assert expand_to_dict('border-top: 3px dotted') == {
}),
('border-top: 3px dotted', {
'border_top_width': (3, 'px'),
'border_top_style': 'dotted',
}
assert expand_to_dict('border-top: 3px red') == {
}),
('border-top: 3px red', {
'border_top_width': (3, 'px'),
'border_top_color': (1, 0, 0, 1), # red
}
assert expand_to_dict('border-top: solid') == {
'border_top_style': 'solid',
}
assert expand_to_dict('border: 6px dashed lime') == {
}),
('border-top: solid', {'border_top_style': 'solid'}),
('border: 6px dashed lime', {
'border_top_width': (6, 'px'),
'border_top_style': 'dashed',
'border_top_color': (0, 1, 0, 1), # lime
@ -244,42 +286,63 @@ def test_expand_borders():
'border_right_width': (6, 'px'),
'border_right_style': 'dashed',
'border_right_color': (0, 1, 0, 1), # lime
}
}),
))
def test_expand_borders(rule, result):
assert expand_to_dict(rule) == result
@assert_no_logs
def test_expand_borders_invalid():
assert_invalid('border: 6px dashed left')
@assert_no_logs
def test_expand_list_style():
"""Test the ``list_style`` property."""
assert expand_to_dict('list-style: inherit') == {
@pytest.mark.parametrize('rule, result', (
('list-style: inherit', {
'list_style_position': 'inherit',
'list_style_image': 'inherit',
'list_style_type': 'inherit',
}
assert expand_to_dict('list-style: url(../bar/lipsum.png)') == {
}),
('list-style: url(../bar/lipsum.png)', {
'list_style_image': ('url', 'http://weasyprint.org/bar/lipsum.png'),
}
assert expand_to_dict('list-style: square') == {
}),
('list-style: square', {
'list_style_type': 'square',
}
assert expand_to_dict('list-style: circle inside') == {
}),
('list-style: circle inside', {
'list_style_position': 'inside',
'list_style_type': 'circle',
}
assert expand_to_dict('list-style: none circle inside') == {
}),
('list-style: none circle inside', {
'list_style_position': 'inside',
'list_style_image': ('none', None),
'list_style_type': 'circle',
}
assert expand_to_dict('list-style: none inside none') == {
}),
('list-style: none inside none', {
'list_style_position': 'inside',
'list_style_image': ('none', None),
'list_style_type': 'none',
}
assert_invalid('list-style: none inside none none')
assert_invalid('list-style: red')
assert_invalid('list-style: circle disc',
'got multiple type values in a list-style shorthand')
}),
))
def test_expand_list_style(rule, result):
assert expand_to_dict(rule) == result
@assert_no_logs
def test_expand_list_style_warning():
assert_invalid(
'list-style: circle disc',
'got multiple type values in a list-style shorthand')
@assert_no_logs
@pytest.mark.parametrize('rule', (
'list-style: none inside none none',
'list-style: red',
))
def test_expand_list_style_invalid(rule):
assert_invalid(rule)
def assert_background(css, **expected):
@ -296,7 +359,6 @@ def assert_background(css, **expected):
@assert_no_logs
def test_expand_background():
"""Test the ``background`` property."""
assert_background('red', background_color=(1, 0, 0, 1))
assert_background(
'url(lipsum.png)',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
"""
weasyprint.tests.test_draw
--------------------------
Test the final, drawn results and compare PNG images pixel per pixel.
:copyright: Copyright 2011-2018 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])
_ = 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.
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.
def assert_pixels(name, expected_width, expected_height, expected_pixels,
html):
"""Helper testing the size of the image and the pixels values."""
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))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,530 @@
"""
weasyprint.tests.test_draw.test_tables
--------------------------------------
Test the drawn tables.
:copyright: Copyright 2011-2014 Simon Sapin and contributors, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from . import B, R, S, _, as_pixel, assert_pixels, p
from ...html import HTML_HANDLERS
from ..testing_utils import assert_no_logs, requires
# rgba(255, 0, 0, 0.5) above #fff
r = as_pixel(b'\xff\x7f\x7f\xff')
# rgba(0, 255, 0, 0.5) above #fff
g = as_pixel(b'\x7f\xff\x7f\xff')
# r above B above #fff.
b = as_pixel(b'\x80\x00\x7f\xff')
# TODO: refactor colspan/rowspan into CSS:
# td, th { column-span: attr(colspan integer) }
HTML_HANDLERS['x-td'] = HTML_HANDLERS['td']
HTML_HANDLERS['x-th'] = HTML_HANDLERS['th']
tables_source = '''
<style>
@page { size: 28px; background: #fff }
x-table { margin: 1px; padding: 1px; border-spacing: 1px;
border: 1px solid transparent }
x-td { width: 2px; height: 2px; padding: 1px;
border: 1px solid transparent }
%(extra_css)s
</style>
<x-table>
<x-colgroup>
<x-col></x-col>
<x-col></x-col>
</x-colgroup>
<x-col></x-col>
<x-tbody>
<x-tr>
<x-td></x-td>
<x-td rowspan=2></x-td>
<x-td></x-td>
</x-tr>
<x-tr>
<x-td colspan=2></x-td>
<x-td></x-td>
</x-tr>
</x-tbody>
<x-tr>
<x-td></x-td>
<x-td></x-td>
</x-tr>
</x-table>
'''
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_1():
assert_pixels('table_borders', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+_+_+_+_+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+r+r+r+r+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+r+_+_+_+_+S+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+r+_+_+_+_+S+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+r+_+_+_+_+S+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+r+_+_+_+_+S+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border-color: #00f; table-layout: fixed }
x-td { border-color: rgba(255, 0, 0, 0.5) }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_2():
assert_pixels('table_collapsed_borders', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+r+r+r+r+r+_+_+_+_+r+r+r+r+r+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+r+r+r+r+r+r+r+r+r+r+r+r+r+r+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border: 2px solid #00f; table-layout: fixed;
border-collapse: collapse }
x-td { border-color: #ff7f7f }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_3():
assert_pixels('table_collapsed_borders_paged', 28, 52, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+r+r+r+r+r+_+_+_+_+r+r+r+r+r+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+r+r+r+r+r+r+r+r+r+r+r+r+r+r+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+_, # noqa
_+g+_+B+B+r+r+r+r+r+r+r+r+r+r+r+r+r+r+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+g+_, # noqa
_+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border: solid #00f; border-width: 8px 2px;
table-layout: fixed; border-collapse: collapse }
x-td { border-color: #ff7f7f }
@page { size: 28px 26px; margin: 1px;
border: 1px solid rgba(0, 255, 0, 0.5); }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_4():
assert_pixels('table_td_backgrounds', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+S+S+S+S+S+S+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border-color: #00f; table-layout: fixed }
x-td { background: rgba(255, 0, 0, 0.5) }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_5():
assert_pixels('table_row_backgrounds', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border-color: #00f; table-layout: fixed }
x-tbody { background: rgba(0, 0, 255, 1) }
x-tr { background: rgba(255, 0, 0, 0.5) }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_6():
assert_pixels('table_column_backgrounds', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border-color: #00f; table-layout: fixed }
x-colgroup { background: rgba(0, 0, 255, 1) }
x-col { background: rgba(255, 0, 0, 0.5) }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_7():
assert_pixels('table_borders_and_row_backgrounds', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+b+B+B+B+B+b+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+b+B+B+B+B+b+_+b+b+b+b+b+b+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+b+B+B+B+B+b+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+p+b+b+b+b+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+_+_+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+r+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border-color: #00f; table-layout: fixed }
x-tr:first-child { background: blue }
x-td { border-color: rgba(255, 0, 0, 0.5) }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_8():
assert_pixels('table_borders_and_column_backgrounds', 28, 28, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+r+_+_+_+_+r+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+b+b+b+b+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+B+B+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+B+B+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+B+B+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+B+B+b+B+B+B+B+p+_+r+_+_+_+_+r+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+b+p+p+p+p+p+p+_+r+r+r+r+r+r+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+B+B+B+B+b+_+r+_+_+_+_+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+b+b+b+b+b+b+_+r+r+r+r+r+r+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], tables_source % {'extra_css': '''
x-table { border-color: #00f; table-layout: fixed }
x-col:first-child { background: blue }
x-td { border-color: rgba(255, 0, 0, 0.5) }
'''})
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_9():
r = as_pixel(b'\xff\x00\x00\xff')
assert_pixels('collapsed_border_thead', 22, 36, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+r+_+_+_+_+r+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+_+_+_+_+_+r+_+_+_+_+r+_+_+_+_+_+r+_+_, # noqa
_+_+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+r+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], '''
<style>
@page { size: 22px 18px; margin: 1px; background: #fff }
td { border: 1px red solid; width: 4px; height: 3px; }
</style>
<table style="table-layout: fixed; border-collapse: collapse">
<thead style="border: blue solid; border-width: 2px 3px;
"><td></td><td></td><td></td></thead>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>''')
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_10():
assert_pixels('collapsed_border_tfoot', 22, 36, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+_+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+R+_+_+_+_+R+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+R+_+_+_+_+R+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+R+_+_+_+_+R+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+_+R+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+_+R+_+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+R+_+_+_+_+R+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+R+_+_+_+_+R+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+_+_+_+_+R+_+_+_+_+R+_+_+_+_+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], '''
<style>
@page { size: 22px 18px; margin: 1px; background: #fff }
td { border: 1px red solid; width: 4px; height: 3px; }
</style>
<table style="table-layout: fixed; margin-left: 1px;
border-collapse: collapse">
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tfoot style="border: blue solid; border-width: 2px 3px;
"><td></td><td></td><td></td></tfoot>''')
@assert_no_logs
@requires('cairo', (1, 12, 0))
def test_tables_11():
# Segression test for inline table with collapsed border and alignment
# rendering borders incorrectly
# https://github.com/Kozea/WeasyPrint/issues/82
assert_pixels('inline_text_align', 20, 10, [
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+R+R+R+R+R+R+R+R+R+R+R+_, # noqa
_+_+_+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+R+_, # noqa
_+_+_+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+R+_, # noqa
_+_+_+_+_+_+_+_+R+_+_+_+_+R+_+_+_+_+R+_, # noqa
_+_+_+_+_+_+_+_+R+R+R+R+R+R+R+R+R+R+R+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, # noqa
], '''
<style>
@page { size: 20px 10px; margin: 1px; background: #fff }
body { text-align: right; font-size: 0 }
table { display: inline-table; width: 11px }
td { border: 1px red solid; width: 4px; height: 3px }
</style>
<table style="table-layout: fixed; border-collapse: collapse">
<tr><td></td><td></td></tr>''')

View File

@ -10,7 +10,7 @@
"""
from .test_boxes import render_pages
from .test_draw import requires
from .test_draw.test_draw import requires
from .testing_utils import assert_no_logs

View File

@ -12,7 +12,8 @@
import pytest
from ..test_boxes import render_pages
from ..test_draw import B, _, assert_pixels, r
from ..test_draw import assert_pixels
from ..test_draw.test_draw import B, _, r
from ..testing_utils import assert_no_logs, capture_logs, requires