Holger Brunn
8 years ago
committed by
Stefan Rijnhart
11 changed files with 271 additions and 0 deletions
-
62report_qweb_pdf_watermark/README.rst
-
4report_qweb_pdf_watermark/__init__.py
-
18report_qweb_pdf_watermark/__openerp__.py
-
30report_qweb_pdf_watermark/demo/report.xml
-
5report_qweb_pdf_watermark/models/__init__.py
-
14report_qweb_pdf_watermark/models/ir_actions_report_xml.py
-
87report_qweb_pdf_watermark/models/report.py
-
BINreport_qweb_pdf_watermark/static/description/icon.png
-
4report_qweb_pdf_watermark/tests/__init__.py
-
32report_qweb_pdf_watermark/tests/test_report_qweb_pdf_watermark.py
-
15report_qweb_pdf_watermark/views/ir_actions_report_xml.xml
@ -0,0 +1,62 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
============= |
||||
|
Pdf watermark |
||||
|
============= |
||||
|
|
||||
|
This module was written to add watermarks (backgrounds) to PDF reports. |
||||
|
|
||||
|
This is necessary because of the way wkhtmltopdf handles headers and footers, in the current versions if quite impossible to have a background for the complete page. |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
To use this module, you need to: |
||||
|
|
||||
|
#. go to your report |
||||
|
#. select a PDF or image to use as watermark. Note that resolutions and size must match, otherwise you'll have funny results |
||||
|
#. advanced users (members of group technical settings) can also fill in an expression that returns the data (base64 encoded) to be used as watermark |
||||
|
|
||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
||||
|
:alt: Try me on Runbot |
||||
|
:target: https://runbot.odoo-community.org/runbot/143/8.0 |
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Bugs are tracked on `GitHub Issues |
||||
|
<https://github.com/OCA/reporting-engine/issues>`_. In case of trouble, please |
||||
|
check there if your issue has already been reported. If you spotted it first, |
||||
|
help us smashing it by providing a detailed and welcomed feedback. |
||||
|
|
||||
|
Credits |
||||
|
======= |
||||
|
|
||||
|
Images |
||||
|
------ |
||||
|
|
||||
|
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Holger Brunn <hbrunn@therp.nl> |
||||
|
|
||||
|
Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list <mailto:community@mail.odoo.com>`_ or the `appropriate specialized mailinglist <https://odoo-community.org/groups>`_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues. |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
.. image:: https://odoo-community.org/logo.png |
||||
|
:alt: Odoo Community Association |
||||
|
:target: https://odoo-community.org |
||||
|
|
||||
|
This module is maintained by the OCA. |
||||
|
|
||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose |
||||
|
mission is to support the collaborative development of Odoo features and |
||||
|
promote its widespread use. |
||||
|
|
||||
|
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,4 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from . import models |
@ -0,0 +1,18 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
{ |
||||
|
"name": "Pdf watermark", |
||||
|
"version": "9.0.1.0.0", |
||||
|
"author": "Therp BV,Odoo Community Association (OCA)", |
||||
|
"license": "AGPL-3", |
||||
|
"category": "Reporting", |
||||
|
"summary": "Add watermarks to your QWEB PDF reports", |
||||
|
"depends": [ |
||||
|
'report', |
||||
|
], |
||||
|
"data": [ |
||||
|
"demo/report.xml", |
||||
|
"views/ir_actions_report_xml.xml", |
||||
|
], |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<report |
||||
|
id="demo_report" |
||||
|
string="Demo report" |
||||
|
model="res.users" |
||||
|
report_type="qweb-pdf" |
||||
|
name="report_qweb_pdf_watermark.demo_report_view" |
||||
|
file="report_qweb_pdf_watermark.demo_report_view" |
||||
|
paperformat="report.paperformat_euro" |
||||
|
/> |
||||
|
<record id="demo_report" model="ir.actions.report.xml"> |
||||
|
<field name="pdf_watermark_expression">docs[:1].company_id.logo</field> |
||||
|
</record> |
||||
|
<template id="demo_report_view"> |
||||
|
<t t-call="report.html_container"> |
||||
|
<t t-call="report.external_layout"> |
||||
|
<div class="page"> |
||||
|
<ul> |
||||
|
<li t-foreach="docs" t-as="doc"> |
||||
|
<t t-esc="doc.name" /> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</t> |
||||
|
</t> |
||||
|
</template> |
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from . import ir_actions_report_xml |
||||
|
from . import report |
@ -0,0 +1,14 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from openerp import fields, models |
||||
|
|
||||
|
|
||||
|
class IrActionsReportXml(models.Model): |
||||
|
_inherit = 'ir.actions.report.xml' |
||||
|
|
||||
|
pdf_watermark = fields.Binary('Watermark') |
||||
|
pdf_watermark_expression = fields.Char( |
||||
|
'Watermark expression', help='An expression yielding the base64 ' |
||||
|
'encoded data to be used as watermark. \n' |
||||
|
'You have access to variables `env` and `docs`') |
@ -0,0 +1,87 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from base64 import b64decode |
||||
|
from logging import getLogger |
||||
|
from pyPdf import PdfFileWriter, PdfFileReader |
||||
|
from pyPdf.utils import PdfReadError |
||||
|
from PIL import Image |
||||
|
from StringIO import StringIO |
||||
|
from openerp import api, models, tools |
||||
|
logger = getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class Report(models.Model): |
||||
|
_inherit = 'report' |
||||
|
|
||||
|
@api.v7 |
||||
|
def get_pdf( |
||||
|
self, cr, uid, ids, report_name, html=None, data=None, context=None |
||||
|
): |
||||
|
# pylint: disable=R8110 |
||||
|
# this override cannot be done in v8 api |
||||
|
result = super(Report, self).get_pdf( |
||||
|
cr, uid, ids, report_name, html=html, data=data, |
||||
|
context=context |
||||
|
) |
||||
|
report = self._get_report_from_name(cr, uid, report_name) |
||||
|
watermark = None |
||||
|
if report.pdf_watermark: |
||||
|
watermark = b64decode(report.pdf_watermark) |
||||
|
else: |
||||
|
env = api.Environment(cr, uid, context) |
||||
|
watermark = tools.safe_eval( |
||||
|
report.pdf_watermark_expression or 'None', |
||||
|
dict(env=env, docs=env[report.model].browse(ids)), |
||||
|
) |
||||
|
if watermark: |
||||
|
watermark = b64decode(watermark) |
||||
|
|
||||
|
if not watermark: |
||||
|
return result |
||||
|
|
||||
|
pdf = PdfFileWriter() |
||||
|
pdf_watermark = None |
||||
|
try: |
||||
|
pdf_watermark = PdfFileReader(StringIO(watermark)) |
||||
|
except PdfReadError: |
||||
|
# let's see if we can convert this with pillow |
||||
|
try: |
||||
|
image = Image.open(StringIO(watermark)) |
||||
|
pdf_buffer = StringIO() |
||||
|
if image.mode != 'RGB': |
||||
|
image = image.convert('RGB') |
||||
|
resolution = image.info.get( |
||||
|
'dpi', report.paperformat_id.dpi or 90 |
||||
|
) |
||||
|
if isinstance(resolution, tuple): |
||||
|
resolution = resolution[0] |
||||
|
image.save(pdf_buffer, 'pdf', resolution=resolution) |
||||
|
pdf_watermark = PdfFileReader(pdf_buffer) |
||||
|
except: |
||||
|
logger.exception('Failed to load watermark') |
||||
|
|
||||
|
if not pdf_watermark: |
||||
|
logger.error( |
||||
|
'No usable watermark found, got %s...', watermark[:100] |
||||
|
) |
||||
|
return result |
||||
|
|
||||
|
if pdf_watermark.numPages < 1: |
||||
|
logger.error('Your watermark pdf does not contain any pages') |
||||
|
return result |
||||
|
if pdf_watermark.numPages > 1: |
||||
|
logger.debug('Your watermark pdf contains more than one page, ' |
||||
|
'all but the first one will be ignored') |
||||
|
|
||||
|
for page in PdfFileReader(StringIO(result)).pages: |
||||
|
watermark_page = pdf.addBlankPage( |
||||
|
page.mediaBox.getWidth(), page.mediaBox.getHeight() |
||||
|
) |
||||
|
watermark_page.mergePage(pdf_watermark.getPage(0)) |
||||
|
watermark_page.mergePage(page) |
||||
|
|
||||
|
pdf_content = StringIO() |
||||
|
pdf.write(pdf_content) |
||||
|
|
||||
|
return pdf_content.getvalue() |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
@ -0,0 +1,4 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from . import test_report_qweb_pdf_watermark |
@ -0,0 +1,32 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Therp BV <http://therp.nl> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from openerp.tests.common import TransactionCase |
||||
|
|
||||
|
|
||||
|
class TestReportQwebPdfWatermark(TransactionCase): |
||||
|
def test_report_qweb_pdf_watermark(self): |
||||
|
# with our image, we have three |
||||
|
self._test_report_images(3) |
||||
|
|
||||
|
self.env.ref('report_qweb_pdf_watermark.demo_report').write({ |
||||
|
'pdf_watermark_expression': False, |
||||
|
}) |
||||
|
# without, we have two |
||||
|
self._test_report_images(2) |
||||
|
|
||||
|
self.env.ref('report_qweb_pdf_watermark.demo_report').write({ |
||||
|
'pdf_watermark': self.env.user.company_id.logo, |
||||
|
}) |
||||
|
# and now we should have three again |
||||
|
self._test_report_images(3) |
||||
|
|
||||
|
def _test_report_images(self, number): |
||||
|
pdf = self.registry['report'].get_pdf( |
||||
|
self.cr, |
||||
|
self.uid, |
||||
|
self.env['res.users'].search([]).ids, |
||||
|
'report_qweb_pdf_watermark.demo_report_view', |
||||
|
context={} |
||||
|
) |
||||
|
self.assertEqual(pdf.count('/Subtype /Image'), number) |
@ -0,0 +1,15 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<record id="act_report_xml_view" model="ir.ui.view"> |
||||
|
<field name="model">ir.actions.report.xml</field> |
||||
|
<field name="inherit_id" ref="base.act_report_xml_view" /> |
||||
|
<field name="arch" type="xml"> |
||||
|
<field name="attachment" position="after"> |
||||
|
<field name="pdf_watermark" attrs="{'invisible': [('report_type', '!=', 'qweb-pdf')]}" /> |
||||
|
<field name="pdf_watermark_expression" attrs="{'invisible': [('report_type', '!=', 'qweb-pdf')]}" groups="base.group_no_one" /> |
||||
|
</field> |
||||
|
</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue