Weasyprint: Is it possible to add image to header or footer?

Created on 7 Apr 2014  路  7Comments  路  Source: Kozea/WeasyPrint

I could not see tutorial how to add image (any HTML tags) to header or footer. I found only 'content' attribute which cannot add HTML tags.

Most helpful comment

With the content property you can use a url(...) value which will insert an image. However there is no way to style the components of content individually, so only way to size the image is to use e.g. image-resolution: 300dpi.

You can also use background-image and background-size on the page box or page-margin boxes.

Finally, any element with position: fixed is repeated on every page. You can use something like bottom: 100%; margin-bottom: 1em to position it in the top page margin. However the content must be the same on every page, you can not use a page counter there.

Do any of these work for you?

All 7 comments

With the content property you can use a url(...) value which will insert an image. However there is no way to style the components of content individually, so only way to size the image is to use e.g. image-resolution: 300dpi.

You can also use background-image and background-size on the page box or page-margin boxes.

Finally, any element with position: fixed is repeated on every page. You can use something like bottom: 100%; margin-bottom: 1em to position it in the top page margin. However the content must be the same on every page, you can not use a page counter there.

Do any of these work for you?

Nice solution! Thanks.

Please close the bug if you find a solution that works for you.

I'm trying to display an image in the header using a @top-center. Using the content property, the image is correctly displayed but indeed, I cannot size it.
So then, I tried using the background-image property but I cannot manage to have the image displayed in the header: nothing is draw. However, I can manage to display it using this technique if I do this on a normal div....

Here is my code:

    @top-center {
        background: url('../icons/image.png') no-repeat 0 0;
        background-size: 10%;
    }

The page-margin box might not be rendered, or have a zero size if it doesn鈥檛 have content. Try something like:

@top-center {
    content: "";
    width: 100%;
    height: 100%;

    background: ...
}

Percentages in width and height refer to the size of the containing block, which is described here for page-margin boxes. See also the diagrams in the table here. On each of the four sides, three non-corner page-margin boxes share the same containing block.

Alternatively, you can also set a background (or multiple backgrounds, even) on @page itself, and size/position it with background-size and background-position.

Thanks it was actually the reason why nothing was drawn.

My problem was somewhat related to this thread so this is my solution if anyone finds it. I needed a footer image per page.

def content_to_boxes(style, parent_box, quote_depth, counter_values, get_image_from_uri, context=None):
    """
    Dirty hack to allow page counts within a image url (used for the QRCODE).
    Pulled from weasyprint 0.31
    """

    from weasyprint.formatting_structure.build import boxes, counters

    texts = []
    for type_, value in style.content:
        if type_ == 'STRING':
            texts.append(value)
        elif type_ == 'URI':

            # hacked bit
            # interpolate counter
            def repl(m):
                counter_name = m.group(1)
                counter_value = counter_values.get(counter_name, [0])[-1]
                return counters.format(counter_value, 'decimal')

            value = re.sub('\$([^$]+)\$', repl, value)
            # end hacked bit

            image = get_image_from_uri(value)
            if image is not None:
                text = ''.join(texts)
                if text:
                    yield boxes.TextBox.anonymous_from(parent_box, text)
                texts = []
                yield boxes.InlineReplacedBox.anonymous_from(parent_box, image)
        elif type_ == 'counter':
            counter_name, counter_style = value
            counter_value = counter_values.get(counter_name, [0])[-1]
            texts.append(counters.format(counter_value, counter_style))
        elif type_ == 'counters':
            counter_name, separator, counter_style = value
            texts.append(separator.join(
                counters.format(counter_value, counter_style)
                for counter_value in counter_values.get(counter_name, [0])
            ))
        elif type_ == 'string' and context is not None:
            text = context.get_string_set_for(*value)
            texts.append(text)
        else:
            assert type_ == 'QUOTE'
            is_open, insert = value
            if not is_open:
                quote_depth[0] = max(0, quote_depth[0] - 1)
            if insert:
                open_quotes, close_quotes = style.quotes
                quotes = open_quotes if is_open else close_quotes
                texts.append(quotes[min(quote_depth[0], len(quotes) - 1)])
            if is_open:
                quote_depth[0] += 1
    text = ''.join(texts)
    if text:
        yield boxes.TextBox.anonymous_from(parent_box, text)

weasyprint.formatting_structure.build.content_to_boxes = content_to_boxes

so now I can place a image per page

@bottom-left {
                font-style: italic;
                font-size: 0.7em;
                vertical-align: bottom;
                content: url("myprotocol://{{ datafromview }}$page$:$pages$")
}

dont know if this is the best solution or if it is worth to make a hook on content_to_boxes but here it is.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mjbeyeler picture mjbeyeler  路  4Comments

whitelynx picture whitelynx  路  5Comments

Daniyal-Javani picture Daniyal-Javani  路  3Comments

SimonSapin picture SimonSapin  路  4Comments

knyttl picture knyttl  路  4Comments