是否有一个matplotlib可流动的ReportLab?

dauxcl2d  于 2023-06-30  发布在  其他
关注(0)|答案(5)|浏览(124)

我想直接将matplotlib图表嵌入到ReportLab生成的PDF中-即不保存为PNG第一,然后嵌入到PDF的PNG(我想我会得到更好的质量输出)。
有人知道ReportLab是否有matplotlib可流动性吗?
谢谢

pzfprimi

pzfprimi1#

下面是一个使用pdfrw的解决方案:

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. """matplotlib_example.py
  4. An simple example of how to insert matplotlib generated figures
  5. into a ReportLab platypus document.
  6. """
  7. import matplotlib
  8. matplotlib.use('PDF')
  9. import matplotlib.pyplot as plt
  10. import cStringIO
  11. from pdfrw import PdfReader
  12. from pdfrw.buildxobj import pagexobj
  13. from pdfrw.toreportlab import makerl
  14. from reportlab.platypus import Flowable
  15. from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
  16. from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
  17. from reportlab.lib.styles import getSampleStyleSheet
  18. from reportlab.rl_config import defaultPageSize
  19. from reportlab.lib.units import inch
  20. PAGE_HEIGHT=defaultPageSize[1]; PAGE_WIDTH=defaultPageSize[0]
  21. styles = getSampleStyleSheet()
  22. class PdfImage(Flowable):
  23. """PdfImage wraps the first page from a PDF file as a Flowable
  24. which can be included into a ReportLab Platypus document.
  25. Based on the vectorpdf extension in rst2pdf (http://code.google.com/p/rst2pdf/)"""
  26. def __init__(self, filename_or_object, width=None, height=None, kind='direct'):
  27. from reportlab.lib.units import inch
  28. # If using StringIO buffer, set pointer to begining
  29. if hasattr(filename_or_object, 'read'):
  30. filename_or_object.seek(0)
  31. page = PdfReader(filename_or_object, decompress=False).pages[0]
  32. self.xobj = pagexobj(page)
  33. self.imageWidth = width
  34. self.imageHeight = height
  35. x1, y1, x2, y2 = self.xobj.BBox
  36. self._w, self._h = x2 - x1, y2 - y1
  37. if not self.imageWidth:
  38. self.imageWidth = self._w
  39. if not self.imageHeight:
  40. self.imageHeight = self._h
  41. self.__ratio = float(self.imageWidth)/self.imageHeight
  42. if kind in ['direct','absolute'] or width==None or height==None:
  43. self.drawWidth = width or self.imageWidth
  44. self.drawHeight = height or self.imageHeight
  45. elif kind in ['bound','proportional']:
  46. factor = min(float(width)/self._w,float(height)/self._h)
  47. self.drawWidth = self._w*factor
  48. self.drawHeight = self._h*factor
  49. def wrap(self, aW, aH):
  50. return self.drawWidth, self.drawHeight
  51. def drawOn(self, canv, x, y, _sW=0):
  52. if _sW > 0 and hasattr(self, 'hAlign'):
  53. a = self.hAlign
  54. if a in ('CENTER', 'CENTRE', TA_CENTER):
  55. x += 0.5*_sW
  56. elif a in ('RIGHT', TA_RIGHT):
  57. x += _sW
  58. elif a not in ('LEFT', TA_LEFT):
  59. raise ValueError("Bad hAlign value " + str(a))
  60. xobj = self.xobj
  61. xobj_name = makerl(canv._doc, xobj)
  62. xscale = self.drawWidth/self._w
  63. yscale = self.drawHeight/self._h
  64. x -= xobj.BBox[0] * xscale
  65. y -= xobj.BBox[1] * yscale
  66. canv.saveState()
  67. canv.translate(x, y)
  68. canv.scale(xscale, yscale)
  69. canv.doForm(xobj_name)
  70. canv.restoreState()
  71. Title = "Hello world"
  72. pageinfo = "platypus example"
  73. def myFirstPage(canvas, doc):
  74. canvas.saveState()
  75. canvas.setFont('Times-Bold',16)
  76. canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
  77. canvas.setFont('Times-Roman',9)
  78. canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)
  79. canvas.restoreState()
  80. def myLaterPages(canvas, doc):
  81. canvas.saveState()
  82. canvas.setFont('Times-Roman',9)
  83. canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo))
  84. canvas.restoreState()
  85. def go():
  86. fig = plt.figure(figsize=(4, 3))
  87. plt.plot([1,2,3,4])
  88. plt.ylabel('some numbers')
  89. imgdata = cStringIO.StringIO()
  90. fig.savefig(imgdata,format='PDF')
  91. doc = SimpleDocTemplate("document.pdf")
  92. Story = [Spacer(1,2*inch)]
  93. style = styles["Normal"]
  94. for i in range(5):
  95. bogustext = ("This is Paragraph number %s. " % i) *20
  96. p = Paragraph(bogustext, style)
  97. Story.append(p)
  98. Story.append(Spacer(1,0.2*inch))
  99. pi = PdfImage(imgdata)
  100. Story.append(pi)
  101. Story.append(Spacer(1,0.2*inch))
  102. doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages)
  103. if __name__ == '__main__':
  104. go()
展开查看全部
niknxzdl

niknxzdl2#

pdfrw的作者Patrick Maupin在另一个question中提供了一个更简单、更不复杂的答案。(感谢他对我之前的回答的赞美之词。)他还提到,在使用pdfrw提取之前,将matplotlib数据保存到多页PDF中,可以通过减少重复资源来减少最终reportlab PDF的大小。因此,这里是他的代码示例的修改,它演示了如何通过首先写入多页matplotlib PDF来减小PDF文件大小。对于此示例,文件大小减少了约80%。

  • 注意:这是专门用于matplotlib图形的。*
  1. import os
  2. from matplotlib import pyplot as plt
  3. from matplotlib.backends.backend_pdf import PdfPages
  4. from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Flowable
  5. from reportlab.lib.units import inch
  6. from reportlab.lib.styles import getSampleStyleSheet
  7. from pdfrw import PdfReader, PdfDict
  8. from pdfrw.buildxobj import pagexobj
  9. from pdfrw.toreportlab import makerl
  10. try:
  11. from cStringIO import StringIO as BytesIO
  12. except ImportError:
  13. from io import BytesIO
  14. styles = getSampleStyleSheet()
  15. style = styles['Normal']
  16. class PdfImage(Flowable):
  17. """
  18. Generates a reportlab image flowable for matplotlib figures. It is initialized
  19. with either a matplotlib figure or a pointer to a list of pagexobj objects and
  20. an index for the pagexobj to be used.
  21. """
  22. def __init__(self, fig=None, width=200, height=200, cache=None, cacheindex=0):
  23. self.img_width = width
  24. self.img_height = height
  25. if fig is None and cache is None:
  26. raise ValueError("Either 'fig' or 'cache' must be provided")
  27. if fig is not None:
  28. imgdata = BytesIO()
  29. fig.savefig(imgdata, format='pdf')
  30. imgdata.seek(0)
  31. page, = PdfReader(imgdata).pages
  32. image = pagexobj(page)
  33. self.img_data = image
  34. else:
  35. self.img_data = None
  36. self.cache = cache
  37. self.cacheindex = cacheindex
  38. def wrap(self, width, height):
  39. return self.img_width, self.img_height
  40. def drawOn(self, canv, x, y, _sW=0):
  41. if _sW > 0 and hasattr(self, 'hAlign'):
  42. a = self.hAlign
  43. if a in ('CENTER', 'CENTRE', TA_CENTER):
  44. x += 0.5*_sW
  45. elif a in ('RIGHT', TA_RIGHT):
  46. x += _sW
  47. elif a not in ('LEFT', TA_LEFT):
  48. raise ValueError("Bad hAlign value " + str(a))
  49. canv.saveState()
  50. if self.img_data is not None:
  51. img = self.img_data
  52. else:
  53. img = self.cache[self.cacheindex]
  54. if isinstance(img, PdfDict):
  55. xscale = self.img_width / img.BBox[2]
  56. yscale = self.img_height / img.BBox[3]
  57. canv.translate(x, y)
  58. canv.scale(xscale, yscale)
  59. canv.doForm(makerl(canv, img))
  60. else:
  61. canv.drawImage(img, x, y, self.img_width, self.img_height)
  62. canv.restoreState()
  63. class PdfImageCache(object):
  64. """
  65. Saves matplotlib figures to a temporary multi-page PDF file using the 'savefig'
  66. method. When closed the images are extracted and saved to the attribute 'cache'.
  67. The temporary PDF file is then deleted. The 'savefig' returns a PdfImage object
  68. with a pointer to the 'cache' list and an index for the figure. Use of this
  69. cache reduces duplicated resources in the reportlab generated PDF file.
  70. Use is similar to matplotlib's PdfPages object. When not used as a context
  71. manager, the 'close()' method must be explictly called before the reportlab
  72. document is built.
  73. """
  74. def __init__(self):
  75. self.pdftempfile = '_temporary_pdf_image_cache_.pdf'
  76. self.pdf = PdfPages(self.pdftempfile)
  77. self.cache = []
  78. self.count = 0
  79. def __enter__(self):
  80. return self
  81. def __exit__(self, *args):
  82. self.close()
  83. def close(self, *args):
  84. self.pdf.close()
  85. pages = PdfReader(self.pdftempfile).pages
  86. pages = [pagexobj(x) for x in pages]
  87. self.cache.extend(pages)
  88. os.remove(self.pdftempfile)
  89. def savefig(self, fig, width=200, height=200):
  90. self.pdf.savefig(fig)
  91. index = self.count
  92. self.count += 1
  93. return PdfImage(width=width, height=height, cache=self.cache, cacheindex=index)
  94. def make_report(outfn, nfig=5):
  95. """
  96. Makes a dummy report with nfig matplotlib plots.
  97. """
  98. doc = SimpleDocTemplate(outfn)
  99. style = styles["Normal"]
  100. story = [Spacer(0, inch)]
  101. for j in range(nfig):
  102. fig = plt.figure(figsize=(4, 3))
  103. plt.plot([1, 2, 3, 4], [1, 4, 9, 26])
  104. plt.ylabel('some numbers')
  105. plt.title('My Figure %i' % (j+1))
  106. img = PdfImage(fig, width=400, height=400)
  107. plt.close()
  108. for i in range(10):
  109. bogustext = ("Paragraph number %s. " % i)
  110. p = Paragraph(bogustext, style)
  111. story.append(p)
  112. story.append(Spacer(1, 0.2*inch))
  113. story.append(img)
  114. for i in range(10):
  115. bogustext = ("Paragraph number %s. " % i)
  116. p = Paragraph(bogustext, style)
  117. story.append(p)
  118. story.append(Spacer(1, 0.2*inch))
  119. doc.build(story)
  120. def make_report_cached_figs(outfn, nfig=5):
  121. """
  122. Makes a dummy report with nfig matplotlib plots using PdfImageCache
  123. to reduce PDF file size.
  124. """
  125. doc = SimpleDocTemplate(outfn)
  126. style = styles["Normal"]
  127. story = [Spacer(0, inch)]
  128. with PdfImageCache() as pdfcache:
  129. for j in range(nfig):
  130. fig = plt.figure(figsize=(4, 3))
  131. plt.plot([1, 2, 3, 4], [1, 4, 9, 26])
  132. plt.ylabel('some numbers')
  133. plt.title('My Figure %i' % (j+1))
  134. img = pdfcache.savefig(fig, width=400, height=400)
  135. plt.close()
  136. for i in range(10):
  137. bogustext = ("Paragraph number %s. " % i)
  138. p = Paragraph(bogustext, style)
  139. story.append(p)
  140. story.append(Spacer(1, 0.2*inch))
  141. story.append(img)
  142. for i in range(10):
  143. bogustext = ("Paragraph number %s. " % i)
  144. p = Paragraph(bogustext, style)
  145. story.append(p)
  146. story.append(Spacer(1, 0.2*inch))
  147. doc.build(story)
  148. make_report("hello_pdf.pdf", 50)
  149. make_report_cached_figs("hello_pdf_cached_figs.pdf", 50)

由于matplotlib的PdfPages只接受文件路径作为输入,因此PdfImageCache对象将多页PDF写入临时文件。在记忆中做这件事会花费更多的工作。

展开查看全部
q3aa0525

q3aa05253#

没有一个,但我在自己使用MatPlotLib和ReportLab时所做的是生成PNG,然后嵌入PNG,这样我就不需要使用PIL。但是,如果您使用PIL,我相信您应该能够使用MatPlotLib和ReportLab生成和嵌入EPS。

ckocjqey

ckocjqey4#

Python 3的解决方案,并将matplotlib图形嵌入为矢量图像(无光栅化)

  1. import matplotlib.pyplot as plt
  2. from io import BytesIO
  3. from reportlab.pdfgen import canvas
  4. from reportlab.graphics import renderPDF
  5. from svglib.svglib import svg2rlg
  6. fig = plt.figure(figsize=(4, 3))
  7. plt.plot([1,2,3,4])
  8. plt.ylabel('some numbers')
  9. imgdata = BytesIO()
  10. fig.savefig(imgdata, format='svg')
  11. imgdata.seek(0) # rewind the data
  12. drawing=svg2rlg(imgdata)
  13. c = canvas.Canvas('test2.pdf')
  14. renderPDF.draw(drawing,c, 10, 40)
  15. c.drawString(10, 300, "So nice it works")
  16. c.showPage()
  17. c.save()

svglib可从Conda-Forge获得。

展开查看全部
e1xvtsh3

e1xvtsh35#

我创建了一个示例Flowable,用于ReportLab的商业RML模板语言。这里的示例非常有用,但需要稍微调整一下才能作为RML plugInFlow元素工作。
GitHub上有一个工作示例。
下面是Flowable本身:

  1. class SvgFlowable(Flowable):
  2. """Convert byte stream containing SVG into a Reportlab Flowable."""
  3. def __init__(self, svg: BytesIO) -> None:
  4. """Convert SVG to RML drawing on initializtion."""
  5. svg.seek(0)
  6. self.drawing: Drawing = svg2rlg(svg)
  7. self.width: int = self.drawing.minWidth()
  8. self.height: int = self.drawing.height
  9. self.drawing.setProperties({"vAlign": "CENTER", "hAlign": "CENTER"})
  10. def wrap(self, *_args):
  11. """Return diagram size."""
  12. return (self.width, self.height)
  13. def draw(self) -> None:
  14. """Render the chart."""
  15. renderPDF.draw(self.drawing, self.canv, 0, 0)
展开查看全部

相关问题