Search code examples
xmlodooodoo-10qweb

How to print divs with an accurate size using Wkhtmltopdf in Odoo?


I am trying to create a new Qweb report of product labels in Odoo 10.

The paperformat of the report must be an A4 with no margin. Each label must have a width of 70mm and a height of 42.4mm. This means there will be three labels per row and seven rows per page (A4 = 210mm x 297mm. So 70mm x 3 = 210mm, 42.4mm x 7 = 297mm). I've created the paperformat this way:

<record id="paperformat_euro_no_margin" model="report.paperformat">
    <field name="name">European A4 (no margins)</field>
    <field name="default" eval="True" />
    <field name="format">A4</field>
    <field name="page_height">0</field>
    <field name="page_width">0</field>
    <field name="orientation">Portrait</field>
    <field name="margin_top">0</field>
    <field name="margin_bottom">0</field>
    <field name="margin_left">0</field>
    <field name="margin_right">0</field>
    <field name="header_line" eval="False"/>
    <field name="header_spacing">0</field>
    <field name="dpi">90</field>
</record>

I assigned the paperformat to my custom report and I checked it is working fine:

<report
    id="report_product_template_labels_custom"
    string="Custom Products Labels"
    model="product.template"
    report_type="qweb-pdf"
    name="my_module.report_producttemplatelabel_custom"
    file="my_module.report_producttemplatelabel_custom"/>

<record id="report_product_template_labels_custom" model="ir.actions.report.xml">
    <field name="paperformat_id" ref="paperformat_euro_no_margin"/>
</record>

However, it was annoying to see how Odoo ignores margin_left and margin_right attributes when they value 0 (I thought it had been reported and fixed by Odoo S.A.). I had to apply a style of margin-left:-5mm; and margin-right:-5mm; to the page of the report to avoid the defaults to overwrite my values. Is there a cleaner way to fix that?

But my main question comes here. This is my report code:

<template id="report_producttemplatelabel_custom">
    <t t-call="report.html_container">
        <div class="page" style="margin-left:-5mm !important; margin-right:-5mm !important;">
            <t t-foreach="docs" t-as="template">
                <t t-foreach="template.product_variant_ids" t-as="product">
                    <t t-call="my_module.report_simple_label_custom">
                        <t t-set="product" t-value="product"/>
                    </t>
                </t>
            </t>
        </div>
    </t>
</template>

As you can see, for each variant of the product, I show a customized Qweb template (report_simple_label_custom). The code of this is the following:

<template id="report_simple_label_custom">
    <div style="position: relative; display: block; width:70mm; height:42.4mm; float:left; margin:0; padding:0;">
        <div style="width:100%; height:100%; border: 1px solid white; background: green;"></div>
    </div>
</template>

The code is very simple. If the product has 21 variants, I should see 21 green divs, 3 per row, which perfectly fit the A4. However, when I print the report, they do not fit at all. The height of each div seems to be much less than 42.4mm. I've tried modifying paperformat dpi but I was not able to find a value which makes CSS milimeters to be the same as PDF milimeters.

After trying a lot of things, I've decided to ignore milimeters and use percentages, to make labels fit great to the PDF size.

  • Div width = 100 / 3 ~ 33.2%
  • Div height = 100 / 7 ~ 14.28%

After this modifications, when I print the report in HTML, everything is looking great, but when I print it in PDF, width is OK but not height (divs are overlapped). This is because I guess there is some parent element with height different from 100%. I applied this height to the page div, but the problem persists.

So, can anyone give me a hint of how can I solve this problem? I just want an A4 filled in with rectangles of the same size, three per row, and seven per column (if there are less than 21 variants, the left space must be blank).


Solution

  • Finally I've found a good solution for the whole problem.

    Solution to avoid seeing an annoying margin on the left and right size of the PDF

    Wkhtmltopdf was not the guilty in this case. When I printed my report in HTML format, I realized that it also had the space on both margins. In fact it is a padding, due to the .container class. So the solution was overwriting that class attribute on my report, this way:

    <template id="report_simple_label_custom">
        <style type="text/css">
            .container {
                padding: 0 !important;
            }
        </style>
        <div style="position: relative; display: block; width:70mm; height:42.4mm; float:left; margin:0; padding:0;">
            <div style="width:100%; height:100%; border: 1px solid white; background: green;"></div>
        </div>
    </template>
    

    Solution to convert HTML sizes to PDF sizes with precision

    When Odoo executes wkhtmltopdf to turn the HTML code into a PDF, it does not use the option --disable-smart-shrinking. If we execute wkhtmltopdf directly (out of Odoo) to convert any HTML code into PDF, we can pass that argument to get accurate results. So the question was: how to make Odoo pass that argument when converting our report? The easy answer is installing an official OCA module from the repo reporting-engine. Its name is report_wkhtmltopdf_param. It allows you to tell Odoo to pass additional arguments to wkhtmltopdf when printing a specific paperformat.

    So I have to add this code to pass the argument to my paperformat report, and of course, make my module depend on report_wkhtmltopdf_param app:

    <record id="paperformat_euro_no_margin" model="report.paperformat">
        <field name="name">European A4 (no margins)</field>
        <field name="default" eval="True" />
        <field name="format">custom</field>
        <field name="page_height">297</field>
        <field name="page_width">210</field>
        <field name="orientation">Portrait</field>
        <field name="margin_top">0</field>
        <field name="margin_bottom">0</field>
        <field name="margin_left">0</field>
        <field name="margin_right">0</field>
        <field name="header_line" eval="False"/>
        <field name="header_spacing">0</field>
        <field name="dpi">96</field>
    </record>
    
    <record id="paperformat_euro_no_margin_disable_smart_shrinking" model="report.paperformat.parameter">
        <field name="paperformat_id" ref="my_module.paperformat_euro_no_margin"/>
        <field name="name">--disable-smart-shrinking</field>
    </record>
    

    NOTE: the dpi of the paperformat must be 96 to get the best and more accurate results.