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.

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