OCA reporting engine fork for dev and update.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
4.0 KiB

  1. # © 2016 Therp BV <http://therp.nl>
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  3. from base64 import b64decode
  4. from io import BytesIO
  5. from logging import getLogger
  6. from PIL import Image
  7. from odoo import api, fields, models
  8. from odoo.tools.safe_eval import safe_eval
  9. try:
  10. # we need this to be sure PIL has loaded PDF support
  11. from PIL import PdfImagePlugin # noqa: F401
  12. except ImportError:
  13. pass
  14. logger = getLogger(__name__)
  15. try:
  16. from PyPDF2 import PdfFileReader, PdfFileWriter # pylint: disable=W0404
  17. from PyPDF2.utils import PdfReadError # pylint: disable=W0404
  18. except ImportError:
  19. logger.debug("Can not import PyPDF2")
  20. class Report(models.Model):
  21. _inherit = "ir.actions.report"
  22. pdf_watermark = fields.Binary("Watermark")
  23. pdf_watermark_expression = fields.Char(
  24. "Watermark expression",
  25. help="An expression yielding the base64 "
  26. "encoded data to be used as watermark. \n"
  27. "You have access to variables `env` and `docs`",
  28. )
  29. def _render_qweb_pdf(self, res_ids=None, data=None):
  30. if not self.env.context.get("res_ids"):
  31. return super(Report, self.with_context(res_ids=res_ids))._render_qweb_pdf(
  32. res_ids=res_ids, data=data
  33. )
  34. return super(Report, self)._render_qweb_pdf(res_ids=res_ids, data=data)
  35. @api.model
  36. def _run_wkhtmltopdf(
  37. self,
  38. bodies,
  39. header=None,
  40. footer=None,
  41. landscape=False,
  42. specific_paperformat_args=None,
  43. set_viewport_size=False,
  44. ):
  45. result = super(Report, self)._run_wkhtmltopdf(
  46. bodies,
  47. header=header,
  48. footer=footer,
  49. landscape=landscape,
  50. specific_paperformat_args=specific_paperformat_args,
  51. set_viewport_size=set_viewport_size,
  52. )
  53. docids = self.env.context.get("res_ids", False)
  54. watermark = None
  55. if self.pdf_watermark:
  56. watermark = b64decode(self.pdf_watermark)
  57. elif docids:
  58. watermark = safe_eval(
  59. self.pdf_watermark_expression or "None",
  60. dict(env=self.env, docs=self.env[self.model].browse(docids)),
  61. )
  62. if watermark:
  63. watermark = b64decode(watermark)
  64. if not watermark:
  65. return result
  66. pdf = PdfFileWriter()
  67. pdf_watermark = None
  68. try:
  69. pdf_watermark = PdfFileReader(BytesIO(watermark))
  70. except PdfReadError:
  71. # let's see if we can convert this with pillow
  72. try:
  73. Image.init()
  74. image = Image.open(BytesIO(watermark))
  75. pdf_buffer = BytesIO()
  76. if image.mode != "RGB":
  77. image = image.convert("RGB")
  78. resolution = image.info.get("dpi", self.paperformat_id.dpi or 90)
  79. if isinstance(resolution, tuple):
  80. resolution = resolution[0]
  81. image.save(pdf_buffer, "pdf", resolution=resolution)
  82. pdf_watermark = PdfFileReader(pdf_buffer)
  83. except Exception as e:
  84. logger.exception("Failed to load watermark", e)
  85. if not pdf_watermark:
  86. logger.error("No usable watermark found, got %s...", watermark[:100])
  87. return result
  88. if pdf_watermark.numPages < 1:
  89. logger.error("Your watermark pdf does not contain any pages")
  90. return result
  91. if pdf_watermark.numPages > 1:
  92. logger.debug(
  93. "Your watermark pdf contains more than one page, "
  94. "all but the first one will be ignored"
  95. )
  96. for page in PdfFileReader(BytesIO(result)).pages:
  97. watermark_page = pdf.addBlankPage(
  98. page.mediaBox.getWidth(), page.mediaBox.getHeight()
  99. )
  100. watermark_page.mergePage(pdf_watermark.getPage(0))
  101. watermark_page.mergePage(page)
  102. pdf_content = BytesIO()
  103. pdf.write(pdf_content)
  104. return pdf_content.getvalue()