# © 2016 Therp BV # Copyright 2023 Onestein - Anjeel Haria # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from base64 import b64decode from io import BytesIO from logging import getLogger from PIL import Image from flectra import api, fields, models from flectra.tools.safe_eval import safe_eval logger = getLogger(__name__) try: # we need this to be sure PIL has loaded PDF support from PIL import PdfImagePlugin # noqa: F401 except ImportError: logger.error("ImportError: The PdfImagePlugin could not be imported") try: from PyPDF2 import PdfFileReader, PdfFileWriter # pylint: disable=W0404 from PyPDF2.utils import PdfReadError # pylint: disable=W0404 except ImportError: logger.debug("Can not import PyPDF2") class Report(models.Model): _inherit = "ir.actions.report" use_company_watermark = fields.Boolean( default=False, help="Use the pdf watermark defined globally in the company settings.", ) pdf_watermark = fields.Binary( "Watermark", help="Upload an pdf file to use as an watermark on this report." ) 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`", ) def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): if not self.env.context.get("res_ids"): return ( super() .with_context(res_ids=res_ids) ._render_qweb_pdf(report_ref, res_ids=res_ids, data=data) ) return super()._render_qweb_pdf(report_ref, res_ids=res_ids, data=data) @staticmethod def pdf_has_usable_pages(numpages): if numpages < 1: logger.error("Your watermark pdf does not contain any pages") return False if numpages > 1: logger.debug( "Your watermark pdf contains more than one page, " "all but the first one will be ignored" ) return True @api.model def _run_wkhtmltopdf( self, bodies, report_ref=False, header=None, footer=None, landscape=False, specific_paperformat_args=None, set_viewport_size=False, ): result = super()._run_wkhtmltopdf( bodies, report_ref=report_ref, header=header, footer=footer, landscape=landscape, specific_paperformat_args=specific_paperformat_args, set_viewport_size=set_viewport_size, ) docids = self.env.context.get("res_ids", False) report_sudo = self._get_report(report_ref) watermark = None if self.pdf_watermark or report_sudo.pdf_watermark: watermark = b64decode(self.pdf_watermark or report_sudo.pdf_watermark) elif ( self.use_company_watermark or report_sudo.use_company_watermark ) and self.env.company.pdf_watermark: watermark = b64decode(self.env.company.pdf_watermark) elif docids: watermark = safe_eval( self.pdf_watermark_expression or report_sudo.pdf_watermark_expression or "None", dict( env=self.env, docs=self.env[self.model or report_sudo.model].browse(docids), ), ) if watermark: watermark = b64decode(watermark) if not watermark: return result pdf = PdfFileWriter() pdf_watermark = None try: pdf_watermark = PdfFileReader(BytesIO(watermark)) except PdfReadError: # let's see if we can convert this with pillow try: Image.init() image = Image.open(BytesIO(watermark)) pdf_buffer = BytesIO() if image.mode != "RGB": image = image.convert("RGB") resolution = image.info.get("dpi", self.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 Exception as e: logger.exception("Failed to load watermark", e) if not pdf_watermark: logger.error("No usable watermark found, got %s...", watermark[:100]) return result if not self.pdf_has_usable_pages(pdf_watermark.numPages): return result for page in PdfFileReader(BytesIO(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 = BytesIO() pdf.write(pdf_content) return pdf_content.getvalue()