Skip to content

Commit

Permalink
Remove backends in favor of methods on Document
Browse files Browse the repository at this point in the history
This much genericity was not that useful.
  • Loading branch information
SimonSapin committed Jun 21, 2012
1 parent e06da5c commit 040eb76
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 157 deletions.
33 changes: 13 additions & 20 deletions weasyprint/__init__.py
Expand Up @@ -99,26 +99,16 @@ def _ua_stylesheet(self):
from .html import HTML5_UA_STYLESHEET
return [HTML5_UA_STYLESHEET]

def _get_document(self, backend, stylesheets=(), ua_stylesheets=None):
def _get_document(self, stylesheets, enable_hinting, ua_stylesheets=None):
if ua_stylesheets is None:
ua_stylesheets = self._ua_stylesheet()
from .document import Document
return Document(
backend,
self.root_element,
enable_hinting,
user_stylesheets=list(_parse_stylesheets(stylesheets)),
user_agent_stylesheets=ua_stylesheets)

def _write(self, backend, target, stylesheets):
write_to = self._get_document(backend, stylesheets).write_to
if target is None:
import io
target = io.BytesIO()
write_to(target)
return target.getvalue()
else:
write_to(target)

def write_pdf(self, target=None, stylesheets=None):
"""Render the document to PDF.
Expand All @@ -130,8 +120,8 @@ def write_pdf(self, target=None, stylesheets=None):
:returns:
If :obj:`target` is :obj:`None`, a PDF byte string.
"""
from .backends import MetadataPDFBackend
return self._write(MetadataPDFBackend, target, stylesheets)
document = self._get_document(stylesheets, enable_hinting=False)
return document.write_pdf(target)

def write_png(self, target=None, stylesheets=None):
"""Render the document to a single PNG image.
Expand All @@ -144,10 +134,10 @@ def write_png(self, target=None, stylesheets=None):
:returns:
If :obj:`target` is :obj:`None`, a PNG byte string.
"""
from .backends import PNGBackend
return self._write(PNGBackend, target, stylesheets)
document = self._get_document(stylesheets, enable_hinting=True)
return document.write_png(target)

def get_png_pages(self, stylesheets=None):
def get_png_pages(self, stylesheets=None, _with_document=False):
"""Render the document to multiple PNG images, one per page.
:param stylesheets:
Expand All @@ -158,9 +148,12 @@ def get_png_pages(self, stylesheets=None):
each page, in order.
"""
from .backends import PNGBackend
document = self._get_document(PNGBackend, stylesheets)
return document.get_png_pages()
document = self._get_document(stylesheets, enable_hinting=True)
pages = document.get_png_pages()
if _with_document:
return document, pages
else:
return pages


class CSS(Resource):
Expand Down
109 changes: 0 additions & 109 deletions weasyprint/backends.py

This file was deleted.

86 changes: 70 additions & 16 deletions weasyprint/document.py
Expand Up @@ -13,20 +13,24 @@
from __future__ import division, unicode_literals

import io
import math
import shutil

import cairo

from .css import get_all_computed_styles
from .formatting_structure.build import build_formatting_structure
from . import layout
from . import draw
from . import images
from . import backends
from . import pdf


class Document(object):
"""Abstract output document."""
def __init__(self, backend, dom, user_stylesheets, user_agent_stylesheets):
self.backend = backend
self.enable_hinting = backend.enable_hinting
def __init__(self, dom, enable_hinting, user_stylesheets,
user_agent_stylesheets):
self.enable_hinting = enable_hinting
self.dom = dom #: lxml HtmlElement object
self.user_stylesheets = user_stylesheets
self.user_agent_stylesheets = user_agent_stylesheets
Expand Down Expand Up @@ -84,22 +88,72 @@ def pages(self):
def get_image_from_uri(self, uri, type_=None):
return images.get_image_from_uri(self._image_cache, uri, type_)

def write_to(self, target):
"""Write to the filename or file-like `target`."""
backend = self.backend(target)
def get_png_surfaces(self):
"""Yield (width, height, image_surface) tuples, one for each page."""
for page in self.pages:
context = backend.start_page(page.outer_width, page.outer_height)
draw.draw_page(self, page, context)

backend.finish(self)
width = int(math.ceil(page.outer_width))
height = int(math.ceil(page.outer_height))
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
draw.draw_page(self, page, draw.CairoContext(surface))
yield width, height, surface

def get_png_pages(self):
"""Yield (width, height, png_bytes) tuples, one for each page."""
backend = backends.PNGBackend(None)
for page in self.pages:
context = backend.start_page(page.outer_width, page.outer_height)
draw.draw_page(self, page, context)
width, height, surface = backend.pages.pop()
for width, height, surface in self.get_png_surfaces():
file_obj = io.BytesIO()
surface.write_to_png(file_obj)
yield width, height, file_obj.getvalue()

def write_png(self, target=None):
"""Write a single PNG image."""
surfaces = list(self.get_png_surfaces())
if len(surfaces) == 1:
_, _, surface = surfaces[0]
else:
total_height = sum(height for _, height, _ in surfaces)
max_width = max(width for width, _, _ in surfaces)
surface = cairo.ImageSurface(
cairo.FORMAT_ARGB32, max_width, total_height)
context = cairo.Context(surface)
pos_y = 0
for width, height, page_surface in surfaces:
pos_x = (max_width - width) // 2
context.set_source_surface(page_surface, pos_x, pos_y)
context.paint()
pos_y += height

if target is None:
target = io.BytesIO()
surface.write_to_png(target)
return target.getvalue()
else:
surface.write_to_png(target)

def write_pdf(self, target=None):
"""Write a single PNG image."""
# Use an in-memory buffer. We will need to seek for metadata
# TODO: avoid this if target can seek? Benchmark first.
file_obj = io.BytesIO()
# We’ll change the surface size for each page
surface = cairo.PDFSurface(file_obj, 1, 1)
px_to_pt = pdf.PX_TO_PT
for page in self.pages:
surface.set_size(page.outer_width * px_to_pt,
page.outer_height * px_to_pt)
context = draw.CairoContext(surface)
context.scale(px_to_pt, px_to_pt)
draw.draw_page(self, page, context)
surface.show_page()
surface.finish()

pdf.write_pdf_metadata(self, file_obj)

if target is None:
return file_obj.getvalue()
else:
file_obj.seek(0)
if hasattr(target, 'write'):
shutil.copyfileobj(file_obj, target)
else:
with open(target, 'wb') as fd:
shutil.copyfileobj(file_obj, fd)
9 changes: 4 additions & 5 deletions weasyprint/navigator.py
Expand Up @@ -17,10 +17,10 @@

import cairo

from weasyprint import HTML, CSS, draw
from weasyprint.backends import PNGBackend
from weasyprint import HTML, CSS
from weasyprint.formatting_structure import boxes
from weasyprint.urls import url_is_absolute
from weasyprint.compat import izip


FAVICON = os.path.join(os.path.dirname(__file__),
Expand Down Expand Up @@ -50,9 +50,8 @@ def find_links(box, links, anchors):


def get_pages(html, *stylesheets):
document = html._get_document(PNGBackend, stylesheets)
for page, (width, height, png_bytes) in zip(
document.pages, document.get_png_pages()):
document, png_pages = html.get_png_pages(stylesheets, _with_document=True)
for page, (width, height, png_bytes) in izip(document.pages, png_pages):
links = []
anchors = []
find_links(page, links, anchors)
Expand Down
5 changes: 2 additions & 3 deletions weasyprint/tests/test_draw.py
Expand Up @@ -132,11 +132,10 @@ def document_to_pixels(document, name, expected_width, expected_height,
"""
Render an HTML document to PNG, checks its size and return pixel data.
"""
file_like = BytesIO()
document.write_to(file_like)
png_bytes = document.write_png()
assert len(document.pages) == nb_pages

with contextlib.closing(pystacia.read_blob(file_like.getvalue())) as image:
with contextlib.closing(pystacia.read_blob(png_bytes)) as image:
assert image.size == (expected_width, expected_height)
raw = image.get_raw('rgba')['raw']
assert len(raw) == expected_width * expected_height * BYTES_PER_PIXELS
Expand Down
7 changes: 3 additions & 4 deletions weasyprint/tests/testing_utils.py
Expand Up @@ -20,7 +20,6 @@

from .. import HTML, CSS
from ..document import Document
from ..backends import PNGBackend, MetadataPDFBackend
from ..logger import LOGGER


Expand All @@ -38,13 +37,13 @@ class TestPNGDocument(Document):
This stylesheet is shorter, which makes tests faster.
"""
backend_class = PNGBackend
enable_hinting = True

def __init__(self, html_source, base_url=None, user_stylesheets=(),
user_agent_stylesheets=(TEST_UA_STYLESHEET,)):
super(TestPNGDocument, self).__init__(
self.backend_class,
HTML(string=html_source, base_url=base_url).root_element,
enable_hinting=self.enable_hinting,
user_stylesheets=user_stylesheets,
user_agent_stylesheets=user_agent_stylesheets)

Expand All @@ -55,7 +54,7 @@ class TestPDFDocument(TestPNGDocument):
This stylesheet is shorter, which makes tests faster.
"""
backend_class = MetadataPDFBackend
enable_hinting = False


def resource_filename(basename):
Expand Down

0 comments on commit 040eb76

Please sign in to comment.