Skip to content

Commit

Permalink
Fetch background images early
Browse files Browse the repository at this point in the history
This removes the need to have a reference to the image cache
in draw.py
  • Loading branch information
SimonSapin committed Sep 12, 2012
1 parent 43bccc7 commit 463a33c
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 64 deletions.
3 changes: 2 additions & 1 deletion weasyprint/css/properties.py
Expand Up @@ -24,6 +24,7 @@
'background_attachment': 'scroll',
'background_color': COLOR_KEYWORDS['transparent'],
'background_image': 'none',
'_fetched_background_image': None, # internal, image cache
'background_position': (Dimension(0, '%'), Dimension(0, '%')),
'background_repeat': 'repeat',
'background_clip': 'border-box', # CSS3
Expand Down Expand Up @@ -262,4 +263,4 @@

BACKGROUND_INITIAL = dict(
(name, value) for name, value in INITIAL_VALUES.items()
if name.startswith('background'))
if 'background' in name)
3 changes: 1 addition & 2 deletions weasyprint/document.py
Expand Up @@ -50,8 +50,7 @@ def render_pages(self):

def draw_page(self, page, context):
"""Draw page on context at scale cairo device units per CSS pixel."""
return draw.draw_page(page, context, self.enable_hinting,
self.get_image_from_uri)
return draw.draw_page(page, context, self.enable_hinting)

def get_png_surfaces(self, resolution=None):
"""Yield (width, height, image_surface) tuples, one for each page."""
Expand Down
89 changes: 31 additions & 58 deletions weasyprint/draw.py
Expand Up @@ -51,39 +51,31 @@ def lighten(color, offset):
color.alpha)


def draw_page(page, context, enable_hinting, get_image_from_uri):
def draw_page(page, context, enable_hinting):
"""Draw the given PageBox."""
stacking_context = StackingContext.from_page(page)
draw_box_background(
context, stacking_context.page, stacking_context.box, enable_hinting,
get_image_from_uri)
draw_canvas_background(context, page, enable_hinting, get_image_from_uri)
context, stacking_context.page, stacking_context.box, enable_hinting)
draw_canvas_background(context, page, enable_hinting)
draw_border(context, page, enable_hinting)
draw_stacking_context(context, stacking_context, enable_hinting,
get_image_from_uri)
draw_stacking_context(context, stacking_context, enable_hinting)


def draw_box_background_and_border(context, page, box, enable_hinting,
get_image_from_uri):
draw_box_background(context, page, box, enable_hinting, get_image_from_uri)
def draw_box_background_and_border(context, page, box, enable_hinting):
draw_box_background(context, page, box, enable_hinting)
if not isinstance(box, boxes.TableBox):
draw_border(context, box, enable_hinting)
else:
for column_group in box.column_groups:
draw_box_background(context, page, column_group, enable_hinting,
get_image_from_uri)
draw_box_background(context, page, column_group, enable_hinting)
for column in column_group.children:
draw_box_background(context, page, column, enable_hinting,
get_image_from_uri)
draw_box_background(context, page, column, enable_hinting)
for row_group in box.children:
draw_box_background(context, page, row_group, enable_hinting,
get_image_from_uri)
draw_box_background(context, page, row_group, enable_hinting)
for row in row_group.children:
draw_box_background(context, page, row, enable_hinting,
get_image_from_uri)
draw_box_background(context, page, row, enable_hinting)
for cell in row.children:
draw_box_background(context, page, cell, enable_hinting,
get_image_from_uri)
draw_box_background(context, page, cell, enable_hinting)
if box.style.border_collapse == 'separate':
draw_border(context, box, enable_hinting)
for row_group in box.children:
Expand All @@ -94,8 +86,7 @@ def draw_box_background_and_border(context, page, box, enable_hinting,
draw_collapsed_borders(context, box, enable_hinting)


def draw_stacking_context(context, stacking_context, enable_hinting,
get_image_from_uri):
def draw_stacking_context(context, stacking_context, enable_hinting):
"""Draw a ``stacking_context`` on ``context``."""
# See http://www.w3.org/TR/CSS2/zindex.html
with stacked(context):
Expand Down Expand Up @@ -133,38 +124,32 @@ def draw_stacking_context(context, stacking_context, enable_hinting,
boxes.InlineBlockBox)):
# The canvas background was removed by set_canvas_background
draw_box_background_and_border(
context, stacking_context.page, box, enable_hinting,
get_image_from_uri)
context, stacking_context.page, box, enable_hinting)

# Point 3
for child_context in stacking_context.negative_z_contexts:
draw_stacking_context(context, child_context, enable_hinting,
get_image_from_uri)
draw_stacking_context(context, child_context, enable_hinting)

# Point 4
for block in stacking_context.block_level_boxes:
draw_box_background_and_border(
context, stacking_context.page, block, enable_hinting,
get_image_from_uri)
context, stacking_context.page, block, enable_hinting)

# Point 5
for child_context in stacking_context.float_contexts:
draw_stacking_context(context, child_context, enable_hinting,
get_image_from_uri)
draw_stacking_context(context, child_context, enable_hinting)

# Point 6
if isinstance(box, boxes.InlineBox):
draw_inline_level(
context, stacking_context.page, box,
enable_hinting, get_image_from_uri)
context, stacking_context.page, box, enable_hinting)

# Point 7
for block in [box] + stacking_context.blocks_and_cells:
marker_box = getattr(block, 'outside_list_marker', None)
if marker_box:
draw_inline_level(
context, stacking_context.page, marker_box,
enable_hinting, get_image_from_uri)
context, stacking_context.page, marker_box, enable_hinting)

if isinstance(block, boxes.ReplacedBox):
draw_replacedbox(context, block)
Expand All @@ -174,17 +159,15 @@ def draw_stacking_context(context, stacking_context, enable_hinting,
# TODO: draw inline tables
draw_inline_level(
context, stacking_context.page, child,
enable_hinting, get_image_from_uri)
enable_hinting)

# Point 8
for child_context in stacking_context.zero_z_contexts:
draw_stacking_context(context, child_context, enable_hinting,
get_image_from_uri)
draw_stacking_context(context, child_context, enable_hinting)

# Point 9
for child_context in stacking_context.positive_z_contexts:
draw_stacking_context(context, child_context, enable_hinting,
get_image_from_uri)
draw_stacking_context(context, child_context, enable_hinting)

# Point 10
draw_outlines(context, box, enable_hinting)
Expand Down Expand Up @@ -227,7 +210,7 @@ def background_positioning_area(page, box, style):
return box_rectangle(box, box.style.background_origin)


def draw_canvas_background(context, page, enable_hinting, get_image_from_uri):
def draw_canvas_background(context, page, enable_hinting):
if not page.children or isinstance(page.children[0], boxes.MarginBox):
# Skip the canvas background on content-empty pages
# TODO: remove this when content empty pages still get boxes
Expand All @@ -238,12 +221,10 @@ def draw_canvas_background(context, page, enable_hinting, get_image_from_uri):
draw_background(context, style,
painting_area=box_rectangle(page, 'padding-box'),
positioning_area=background_positioning_area(page, root_box, style),
enable_hinting=enable_hinting,
get_image_from_uri=get_image_from_uri)
enable_hinting=enable_hinting)


def draw_box_background(context, page, box, enable_hinting,
get_image_from_uri):
def draw_box_background(context, page, box, enable_hinting):
"""Draw the box background color and image to a ``cairo.Context``."""
if box.style.visibility == 'hidden':
return
Expand All @@ -254,8 +235,7 @@ def draw_box_background(context, page, box, enable_hinting,
draw_background(
context, box.style, painting_area,
positioning_area=background_positioning_area(page, box, box.style),
enable_hinting=enable_hinting,
get_image_from_uri=get_image_from_uri)
enable_hinting=enable_hinting)


def percentage(value, refer_to):
Expand All @@ -268,14 +248,10 @@ def percentage(value, refer_to):


def draw_background(context, style, painting_area, positioning_area,
enable_hinting, get_image_from_uri):
enable_hinting):
"""Draw the background color and image to a ``cairo.Context``."""
bg_color = style.background_color
bg_image = style.background_image
if bg_image == 'none':
image = None
else:
image = get_image_from_uri(bg_image)
image = style._fetched_background_image
if bg_color.alpha == 0 and image is None:
# No background.
return
Expand Down Expand Up @@ -713,23 +689,20 @@ def draw_replacedbox(context, box):
box.replacement = 'Removed to work around cairo’s behavior'


def draw_inline_level(context, page, box, enable_hinting, get_image_from_uri):
def draw_inline_level(context, page, box, enable_hinting):
if isinstance(box, StackingContext):
stacking_context = box
assert isinstance(stacking_context.box, boxes.InlineBlockBox)
draw_stacking_context(context, stacking_context, enable_hinting,
get_image_from_uri)
draw_stacking_context(context, stacking_context, enable_hinting)
else:
draw_box_background(
context, page, box, enable_hinting, get_image_from_uri)
draw_box_background(context, page, box, enable_hinting)
draw_border(context, box, enable_hinting)
if isinstance(box, (boxes.InlineBox, boxes.LineBox)):
for child in box.children:
if isinstance(child, boxes.TextBox):
draw_text(context, child, enable_hinting)
else:
draw_inline_level(context, page, child, enable_hinting,
get_image_from_uri)
draw_inline_level(context, page, child, enable_hinting)
elif isinstance(box, boxes.InlineReplacedBox):
draw_replacedbox(context, box)
else:
Expand Down
12 changes: 9 additions & 3 deletions weasyprint/formatting_structure/build.py
Expand Up @@ -61,7 +61,7 @@ def build_formatting_structure(element_tree, style_for, get_image_from_uri):
return box


def make_box(element_tag, sourceline, style, content):
def make_box(element_tag, sourceline, style, content, get_image_from_uri):
if (style.display in ('table', 'inline-table')
and style.border_collapse == 'collapse'):
# Padding do not apply
Expand All @@ -72,6 +72,10 @@ def make_box(element_tag, sourceline, style, content):
for side in ['top', 'bottom', 'left', 'right']:
style['margin_' + side] = ZERO_PIXELS

# Do this early so that draw.py does not need a reference to the cache.
style._fetched_background_image = (
get_image_from_uri(style.background_image)
if style.background_image != 'none' else None)
return BOX_TYPE_FROM_DISPLAY[style.display](element_tag, sourceline,
style, content)

Expand Down Expand Up @@ -112,7 +116,8 @@ def element_to_box(element, style_for, get_image_from_uri, state=None):
if display == 'none':
return []

box = make_box(element.tag, element.sourceline, style, [])
box = make_box(element.tag, element.sourceline, style, [],
get_image_from_uri)

if state is None:
# use a list to have a shared mutable object
Expand Down Expand Up @@ -177,7 +182,8 @@ def pseudo_to_box(element, pseudo_type, state, style_for, get_image_from_uri):
return

box = make_box(
'%s:%s' % (element.tag, pseudo_type), element.sourceline, style, [])
'%s:%s' % (element.tag, pseudo_type), element.sourceline, style, [],
get_image_from_uri)

quote_depth, counter_values, _counter_scopes = state
update_counters(state, style)
Expand Down
8 changes: 8 additions & 0 deletions weasyprint/layout/pages.py
Expand Up @@ -287,6 +287,10 @@ def make_box(at_keyword, containing_block):
style = context.style_for(page.page_type, at_keyword)
if style is None:
style = page.style.inherit_from()
# Do this early so that draw.py does not need a reference to the cache.
style._fetched_background_image = (
get_image_from_uri(style.background_image)
if style.background_image != 'none' else None)
box = boxes.MarginBox(at_keyword, style)
# Empty boxes should not be generated, but they may be needed for
# the layout of their neighbors.
Expand Down Expand Up @@ -470,6 +474,10 @@ def make_page(context, root_box, page_type, resume_at, content_empty):
style = context.style_for(page_type)
# Propagated from the root or <body>.
style.overflow = root_box.viewport_overflow
# Do this early so that draw.py does not need a reference to the cache.
style._fetched_background_image = (
get_image_from_uri(style.background_image)
if style.background_image != 'none' else None)
page = boxes.PageBox(page_type, style)

device_size = page.style.size
Expand Down

0 comments on commit 463a33c

Please sign in to comment.