reporting-engine/report_qweb_pdf_watermark/models/report.py

150 lines
5.0 KiB
Python
Raw Normal View History

2024-12-03 13:17:35 +00:00
# © 2016 Therp BV <http://therp.nl>
# 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()