.. $ doctest docs/specs/weasyprint.rst
.. _specs.weasyprint:
===================================================
``weasyprint``: Printing documents using WeasyPrint
===================================================
The :mod:`lino.modlib.weasyprint` plugin installs two build methods
for generating printable documents using `weasyprint
`__.
Applications that use this plugin will have `'weasyprint'` in their
:ref:`install_requires`.
The two build methods defined by this plugin both use the same input template,
whose ending must be :xfile:`.weasy.html`. Both methods then render the input
template through Jinja with the standard context variables (defined by
:meth:`get_printable_context `.
The base build method :class:`WeasyBuildMethod
` then returns this HTML
output "as is", the other method runs weasyprint over the HTML file to convert
it to a :file:`.pdf` file.
Examples in this document use the :mod:`lino_book.projects.lydia` demo
project.
>>> from lino import startup
>>> startup('lino_book.projects.lydia.settings.doctests')
>>> from lino.api.shell import *
>>> from lino.api.doctest import *
See also :doc:`printing`.
Build methods
=============
This plugin defines no models, it just adds two build methods to your the global
list of build methods (:class:`lino.modlib.printing.BuildMethods`).
.. currentmodule:: lino.modlib.weasyprint
.. class:: WeasyBuildMethod
The base class for both build methods.
.. class:: WeasyHtmlBuildMethod
Renders the input template and returns the unmodified output as
plain HTML.
.. class:: WeasyPdfBuildMethod
Like :class:`WeasyBuildMethod`, but the rendered HTML is then
passed through weasyprint which converts from HTML to PDF.
Templates
=========
.. xfile:: 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" -%}
.. _specs.weasyprint.examples:
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 :mod:`lino_book.projects.apc`, go to :menuselection:`Sales --> Invoices` and print one
of them. (template :xfile:`sales/VatProductInvoice/default.weasy.html` in
:mod:`lino_xl.lib.sales`)
- In demo project :mod:`lino_book.projects.lydia`, go to :menuselection:`Accounting --> VAT Declarations`
and print one of them.
(template :xfile:`bevats/Declaration/default.weasy.html` in :mod:`lino_xl.lib.bevats`)
- In demo project :mod:`lino_book.projects.lydia`, go to :menuselection:`Reports --> Accounting -->
Debtors`, click on one of the partners, then click :guilabel:`Print`.
(template :xfile:`contacts/Partner/payment_reminder.weasy.html` in
:mod:`lino_xl.lib.ledger`)
- In demo project :mod:`lino_book.projects.lydia`, go to :menuselection:`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 :mod:`lino_xl.lib.lists` in
:xfile:`lists/List/list_members.weasy.html`)
- In demo project :mod:`lino_book.projects.lydia`, go to :menuselection:`Reports --> Accounting -->
Accounting report`, then click the print button (template
:xfile:`sheets/Report/default.weasy.html` in :mod:`lino_xl.lib.sheets`).
- In demo project :mod:`lino_book.projects.roger`, open the detail view of some course and click on one of
the `Presence sheet` links. Try several date ranges and options.
(template :xfile:`courses/Course/presence_sheet.weasy.html` in :mod:`lino_xl.lib.courses`)
- :mod:`lino_xl.lib.working` --
:xfile:`working/ServiceReport/default.weasy.html`
- In demo project :mod:`lino_book.projects.avanti1`, go to some client and click
the print button. (template :xfile:`avanti/Client/final_report.weasy.html` in
:mod:`lino_avanti.lib.clients`)
Here is a list of the weasy templates included with the :ref:`xl`:
>>> 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/bevats/config/bevats/Declaration/default.weasy.html
./lib/courses/config/courses/Course/presence_sheet.weasy.html
./lib/excerpts/config/excerpts/base.weasy.html
./lib/ledger/config/contacts/Partner/payment_reminder.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/sales/config/sales/VatProductInvoice/base.weasy.html
./lib/sales/config/sales/VatProductInvoice/default.weasy.html
./lib/sheets/config/sheets/Report/default.weasy.html
./lib/working/config/working/ServiceReport/default.weasy.html
Note that `excerpts`, `orders` and `sales` have their own
:file:`FOO/base.weasy.html` templates, which inherit from the main base
:xfile:`weasyprint/base.weasy.html`.
For other usage examples see the specs of :ref:`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 :message:`There are known
rendering problems with Cairo <= 1.14.0` and :message:`@font-face
support needs Pango >= 1.38` issued by weasyprint.
.. _specs.weasyprint.logo:
How to customize your logo in the header or footer
==================================================
You can add a logo to all your weasyprint documents by adding a local
:xfile:`config` directory with a subdirectory :file:`weasyprint`, and then
*either* a file named :file:`top-right.jpg`, *or* a file named
:file:`header.jpg`.
.. xfile:: weasyprint/header.jpg
.. xfile:: weasyprint/top-right.jpg
When a config file of that name exists, the logo will get printed in the top
right area of every page (unless you override the template).
Additionally, this file causes the page margins of all documents to change:
margin: 15mm; margin-top: 35mm;
The base template defines the following blocks, which you can override in your
child template.
- pagesize : either "portrait" or "landscape"
- header : printed on every page. The default implementation checks whether a
file :xfile:`weasyprint/header.jpg` exists.
- footer : the default implementation prints the address of the
:attr:`SiteConfig.site_company`, the page number and print time.
- intro
- main
- bottomright
- bottomleft
How it all works
================
What happens when I print an invoice?
When Lino starts up, it finds the :term:`excerpt type` for sales invoices (more
precisely the :class:`sales.VatProductInvoice
` model) and therefore installs the print
action on that model. This is why you a have a print button per invoice.
The :term:`excerpt type` tells Lino which :term:`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 ``sales/VatProductInvoice`` (the plugin and model name) and
``default.weasy.html`` (the default template filename for weasypdf when
:class:`lino_xl.lib.excerpts.ExcerptType.template` is empty).
Lino now searches for a file named :xfile:`sales/VatProductInvoice/default.weasy.html`.
This file can exist under any :xfile:`config` directory.
If you have a local :xfile:`config` directory, this is searched first.
Otherwise Lino uses a default file from the source code directory.
More about config directories in :doc:`/admin/config_dirs`.
Now look at the default :xfile:`sales/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
:xfile:`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::
More readings:
- https://www.qhmit.com/css/at-rules/
- https://www.quackit.com/css/properties/css_content.cfm
- https://stackoverflow.com/questions/39941967/generate-pdf-with-weasyprint-having-common-header-footer-and-pagination
- https://github.com/Kozea/WeasyPrint/blob/gh-pages/samples/invoice/invoice.css
- https://gist.github.com/pikhovkin/5642563 complex headers