Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

weasyprint: Printing documents using WeasyPrint

The lino.modlib.weasyprint plugin installs two build methods for generating printable documents using weasyprint.

Applications that use this plugin will automatically install the ‘weasyprint’ Python package when the server administrator runs install.

The two build methods defined by this plugin both use the same input template, whose ending must be .weasy.html. Both methods then render the input template through Jinja with the standard context variables (defined by get_printable_context. The base build method WeasyBuildMethod then returns this HTML output “as is”, the other method runs weasyprint over the HTML file to convert it to a .pdf file.

Examples in this document use the lino_book.projects.tera1 demo project.

>>> from lino import startup
>>> startup('lino_book.projects.tera1.settings.doctests')
>>> from lino.api.shell import *
>>> from lino.api.doctest import *

See also printing : Basic printing functionality.

Build methods

This plugin defines no models, it just adds two build methods to your the global list of build methods (lino.modlib.printing.BuildMethods).

class lino.modlib.weasyprint.WeasyBuildMethod

The base class for both build methods.

class lino.modlib.weasyprint.WeasyHtmlBuildMethod

Renders the input template and returns the unmodified output as plain HTML.

class lino.modlib.weasyprint.WeasyPdfBuildMethod

Like WeasyBuildMethod, but the rendered HTML is then passed through weasyprint which converts from HTML to PDF.

Templates

weasyprint/base.weasy.html

The base template. Defines the general HTML and CSS and block definitions to be used by every weasyprint template. See the source code.

Actual templates use the base template by starting adding the following line:

{%- extends "weasyprint/base.weasy.html" -%}

Examples of weasyprint templates

Here is a list of weasyprint templates that use the base template. You can use them as examples for your own work. We also use this list for manual end-user testing.

  • In demo project lino_book.projects.cosi1, go to Sales ‣ Invoices and print one of them. (template trading/VatProductInvoice/default.weasy.html in lino_xl.lib.trading)

  • In demo project lino_book.projects.tera1, go to Accounting ‣ VAT Declarations and print one of them. (template bevats/Declaration/default.weasy.html in lino_xl.lib.bevats)

  • In demo project lino_book.projects.tera1, go to Reports ‣ Accounting ‣ Debtors, click on one of the partners, then click Print. (template contacts/Partner/payment_reminder.weasy.html in lino_xl.lib.accounting)

  • In demo project lino_book.projects.tera1, go to Contacts ‣ Partner lists, double-cick on one of them to open the detail window, and then click “Members” or “Members (HTML)” in the Print field. (template lino_xl.lib.lists in lists/List/list_members.weasy.html)

  • In demo project lino_book.projects.tera1, go to Reports ‣ Accounting ‣ Accounting report, then click the print button (template sheets/Report/default.weasy.html in lino_xl.lib.sheets).

  • In demo project lino_book.projects.voga2, open the detail view of some course and click on one of the Presence sheet links. Try several date ranges and options. (template courses/Course/presence_sheet.weasy.html in lino_xl.lib.courses)

  • lino_xl.lib.workingworking/ServiceReport/default.weasy.html

  • In demo project lino_book.projects.avanti1, go to some client and click the print button. (template avanti/Client/final_report.weasy.html in lino_avanti.lib.clients)

Here is a list of the weasy templates included with the Lino Extensions Library:

>>> import lino_xl
>>> from os.path import dirname
>>> from atelier.sheller import Sheller
>>> shell = Sheller(dirname(lino_xl.__file__))
>>> shell("find -name '*.weasy.html' | sort")
... 
./lib/accounting/config/contacts/Partner/payment_reminder.weasy.html
./lib/agenda/config/cal/Event/base.weasy.html
./lib/agenda/config/cal/Event/default.weasy.html
./lib/bevats/config/bevats/Declaration/default.weasy.html
./lib/courses/config/courses/Course/description.weasy.html
./lib/courses/config/courses/Course/presence_sheet.weasy.html
./lib/excerpts/config/excerpts/base.weasy.html
./lib/lists/config/lists/List/list_members.weasy.html
./lib/orders/config/orders/Order/base.weasy.html
./lib/orders/config/orders/Order/default.weasy.html
./lib/products/config/products/Product/product_barcode.weasy.html
./lib/sheets/config/sheets/Report/default.weasy.html
./lib/storage/config/storage/DeliveryNote/base.weasy.html
./lib/storage/config/storage/DeliveryNote/default.weasy.html
./lib/subscriptions/config/subscriptions/Subscription/base.weasy.html
./lib/subscriptions/config/subscriptions/Subscription/default.weasy.html
./lib/trading/config/trading/VatProductInvoice/base.weasy.html
./lib/trading/config/trading/VatProductInvoice/default.weasy.html
./lib/working/config/working/ServiceReport/default.weasy.html

Note that excerpts, orders and trading have their own FOO/base.weasy.html templates, which inherit from the main base weasyprint/base.weasy.html.

For other usage examples see the specs of Lino Welfare.

Warnings about Cairo and Pango

This plugin installs a warnings filter for the cffi.model module in order to get rid of a disturbing warning There are known rendering problems with Cairo <= 1.14.0 and @font-face support needs Pango >= 1.38 issued by weasyprint.

How it all works

What happens when I print an invoice?

When Lino starts up, it finds the excerpt type for trading invoices (more precisely the trading.VatProductInvoice model) and therefore installs the print action on that model. This is why you a have a print button per invoice.

The excerpt type tells Lino which build method you want to use for building your printable document. The default build method is weasypdf.

When we know the build method, we can compute the name of the template to use. This name is a combination of trading/VatProductInvoice (the plugin and model name) and default.weasy.html (the default template filename for weasypdf when lino_xl.lib.excerpts.ExcerptType.template is empty).

Lino now searches for a file named trading/VatProductInvoice/default.weasy.html. This file can exist under any config directory. If you have a local config directory, this is searched first. Otherwise Lino uses a default file from the source code directory. More about config directories in Introduction to config directories.

Now look at the default trading/VatProductInvoice/default.weasy.html template file. The first line is:

{%- extends "weasyprint/base.weasy.html" -%}

Which means that Lino first loads yet another template, called weasyprint/base.weasy.html.

How weasyprint templates work

The weasyprint template uses the CSS @-rules @page and @bottom-right, which define styles to apply to individual pages when printing the document. That is, they are used to apply styles for paged media only, not for continuous media like a browser window.

List of all page-margin properties: https://www.quackit.com/css/at-rules/css_page-margin_properties_list.cfm

Setting the height attribute in HTML is called a “presentational hint” and it’s now recommended not to use them and use CSS instead. Presentational hints are ignored by WeasyPrint by default, but you can handle them using the –presentational-hints CLI parameter. https://github.com/Kozea/WeasyPrint/issues/872

Lino currently doesn’t use arbitrary “complex” HTML in headers and footers (as documented here). The standard system with at-rules works well for us.

It contains pseudo-elements for styling the first page as well as the left and right margins of the page.

It can contain something like this:

<style type="text/css">
@page {
    @top-right {
      height: 20mm;
      padding: 0px;
      text-align: right;
      content: url(file://{{logo_file}});
    }
</style>

More readings:

Related work: