2015-07-29 5 views
2

Я пытаюсь загрузить объект matplotlib в reportlab. Вот мой код:loading matplotlib object в reportlab

from reportlab.pdfgen import canvas 
from reportlab.lib.utils import ImageReader 
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Image 
from matplotlib import pyplot as plt 

def __get_img_data(): 
    """ 
    returns the binary image data of the plot 
    """ 
    img_file = NamedTemporaryFile(delete=False) 
    plt.savefig(img_file.name) 
    img_data = open(img_file.name + '.png', 'rb').read() 
    os.remove(img_file.name) 
    os.remove(img_file.name + '.png') 
    return img_data 

def get_plot(): 
    # HERE I PLOT SOME STUFF 
    img_data = __get_img_data() 
    plt.close() 
    return img_data 

class NumberedCanvas(canvas.Canvas): 
    def __init__(self): 
     pass 

class ReportTemplate: 
    def __init__(self): 
     pass 
    def _header_footer(self, canvas, doc): 
     pass 

    def get_data(self): 
     elements = [] 
     elements.append('hello') 
     ## HERE I WANT TO ADD THE IMAGE 
     imgdata = get_plot() 
     with open('/tmp/x.png', 'wb') as fh: 
      fh.write(imgdata) 
     im = Image('/tmp/x.png', width=usable_width, height=usable_width) 
     elements.append(im) 
     os.remove('/tmp/x.png') 
     ###### 
     doc.build(elements, onFirstPage=self._header_footer,\ 
        onLaterPages=self._header_footer,\ 
        canvasmaker=NumberedCanvas) 
     # blah blah 
     return obj 

Моя цель состоит в том, чтобы вставить участок изображения в отчет. Это нормально работает, но я не хочу писать во временный файл. Я попытался установить PIL, потому что я читал, что некоторые люди делают это с библиотекой изображений PIL, но как только я устанавливаю PIL, я еще одна часть моего кода ломается из-за несовместимых версий Pillow.

ответ

8

pdfrw документация сосет

подошвы причина, по которой pdfrw example обсуждается в the first answer to this question, является немного klunky, потому что pdfrw документация плохо всасывает. Из-за сосательного документа автор этого примера @ Ларри-Мейн использовал расширение vectorpdf для rst2pdf как отправную точку, а , что расширение также не документировано, и должны иметь дело с причудами rst2pdf, а также pdfrw (и является более общим, чем вам нужно, поскольку он может позволить rst2pdf отображать произвольный прямоугольник со страницы арбитража существующего PDF). Удивительно, что Ларри удалось заставить его работать вообще, и моя шляпа к нему.

Я вполне компетентен, чтобы сказать это, потому что я являюсь автором pdfrw и внес несколько вкладов в rst2pdf, включая это расширение vectorpdf.

Но вы, вероятно, хотите использовать pdfrw все равно

Я не был на самом деле обращать внимание не на StackOverflow, пока месяц назад, и pdfrw сам томился в течение нескольких лет, но теперь я здесь, и я думаю, что это вам хотелось бы еще раз взглянуть на pdfrw, хотя документация все еще сосет.

Почему? Потому что, если вы выведете файл png, ваше изображение будет растеризованным, и если вы используете pdfrw, он останется в векторном формате, что означает, что он будет выглядеть красиво в любом масштабе.

Так я изменил пример PNG вашего ответа в

Вашего PNG пример не совсем полная программа - параметры, doc.build не были определены, стили не был определен, он отсутствовал несколько импорт и т. д. Но это было достаточно близко, чтобы занять какое-то намерение и заставить его работать.

Редактировать - Я просто заметил, что этот пример был фактически измененная версия примера Ларри, так that example еще очень ценно, потому что это немного более полнофункциональный, чем это в некотором роде.

После того, как я исправил эти проблемы и получил некоторый результат, я добавил вариант, чтобы иметь возможность использовать png или pdf, чтобы вы могли видеть разницу.В приведенной ниже программе будут созданы два разных файла PDF, и вы можете сравнить результаты для себя.

import cStringIO 
from matplotlib import pyplot as plt 
from reportlab.pdfgen import canvas 
from reportlab.lib.utils import ImageReader 
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Image, Flowable 
from reportlab.lib.units import inch 
from reportlab.lib.styles import getSampleStyleSheet 

from pdfrw import PdfReader, PdfDict 
from pdfrw.buildxobj import pagexobj 
from pdfrw.toreportlab import makerl 

styles = getSampleStyleSheet() 
style = styles['Normal'] 

def form_xo_reader(imgdata): 
    page, = PdfReader(imgdata).pages 
    return pagexobj(page) 


class PdfImage(Flowable): 
    def __init__(self, img_data, width=200, height=200): 
     self.img_width = width 
     self.img_height = height 
     self.img_data = img_data 

    def wrap(self, width, height): 
     return self.img_width, self.img_height 

    def drawOn(self, canv, x, y, _sW=0): 
     if _sW > 0 and hasattr(self, 'hAlign'): 
      a = self.hAlign 
      if a in ('CENTER', 'CENTRE', TA_CENTER): 
       x += 0.5*_sW 
      elif a in ('RIGHT', TA_RIGHT): 
       x += _sW 
      elif a not in ('LEFT', TA_LEFT): 
       raise ValueError("Bad hAlign value " + str(a)) 
     canv.saveState() 
     img = self.img_data 
     if isinstance(img, PdfDict): 
      xscale = self.img_width/img.BBox[2] 
      yscale = self.img_height/img.BBox[3] 
      canv.translate(x, y) 
      canv.scale(xscale, yscale) 
      canv.doForm(makerl(canv, img)) 
     else: 
      canv.drawImage(img, x, y, self.img_width, self.img_height) 
     canv.restoreState() 

def make_report(outfn, use_pdfrw): 
    fig = plt.figure(figsize=(4, 3)) 
    plt.plot([1,2,3,4],[1,4,9,26]) 
    plt.ylabel('some numbers') 
    imgdata = cStringIO.StringIO() 
    fig.savefig(imgdata, format='pdf' if use_pdfrw else 'png') 
    imgdata.seek(0) 
    reader = form_xo_reader if use_pdfrw else ImageReader 
    image = reader(imgdata) 

    doc = SimpleDocTemplate(outfn) 
    style = styles["Normal"] 
    story = [Spacer(0, inch)] 
    img = PdfImage(image, width=200, height=200) 

    for i in range(10): 
     bogustext = ("Paragraph number %s. " % i) 
     p = Paragraph(bogustext, style) 
     story.append(p) 
     story.append(Spacer(1,0.2*inch)) 

    story.append(img) 

    for i in range(10): 
     bogustext = ("Paragraph number %s. " % i) 
     p = Paragraph(bogustext, style) 
     story.append(p) 
     story.append(Spacer(1,0.2*inch)) 

    doc.build(story) 

make_report("hello_png.pdf", False) 
make_report("hello_pdf.pdf", True) 

Каковы недостатки этого подхода?

Первым очевидным недостатком является то, что в настоящее время существует потребность в pdfrw, но это доступно в PyPI.

Следующий недостаток заключается в том, что если вы помещаете много графиков matplotlib в документ, я думаю, что этот метод будет реплицировать ресурсы, такие как шрифты, потому что я не верю, что reportlab достаточно умен, чтобы заметить дубликаты.

Я считаю, что эту проблему можно решить, выведя все ваши графики в different pages of a single PDF. Я на самом деле не пробовал это с помощью matplotlib, но pdfrw отлично умеет конвертировать each page of an existing pdf to a separate flowable.

Итак, если у вас много сюжетов, и это делает ваш окончательный PDF слишком большой, вы можете посмотреть на это или просто попробовать один из оптимизаторов PDF и посмотреть, помогает ли он. В любом случае, это другая проблема для другого дня.

+0

Это очень хороший момент. Ярлыки оси графика отображаются как текст, подтверждающий, что вы правы - pdfrw сохраняет фактическую информацию в pdf. Я не был уверен в этом. Спасибо – max

+0

yep, pdfew docs suck. Создание PDF в Python в извините ... – Oz123

+0

Это приятно, какая разница в качестве. Спасибо, что поделился. – Tunn

1

Я нашел 2 решения:

1: используя пакет под названием pdfrw: Is there a matplotlib flowable for ReportLab?

2: простой уборщик способ:

class PdfImage(Flowable): 
    def __init__(self, img_data, width=200, height=200): 
     self.img_width = width 
     self.img_height = height 
     self.img_data = img_data 

    def wrap(self, width, height): 
     return self.img_width, self.img_height 

    def drawOn(self, canv, x, y, _sW=0): 
     if _sW > 0 and hasattr(self, 'hAlign'): 
      a = self.hAlign 
      if a in ('CENTER', 'CENTRE', TA_CENTER): 
       x += 0.5*_sW 
      elif a in ('RIGHT', TA_RIGHT): 
       x += _sW 
      elif a not in ('LEFT', TA_LEFT): 
       raise ValueError("Bad hAlign value " + str(a)) 
     canv.saveState() 
     canv.drawImage(self.img_data, x, y, self.img_width, self.img_height) 
     canv.restoreState() 


def make_report(): 
    fig = plt.figure(figsize=(4, 3)) 
    plt.plot([1,2,3,4],[1,4,9,26]) 
    plt.ylabel('some numbers') 
    imgdata = cStringIO.StringIO() 
    fig.savefig(imgdata, format='png') 
    imgdata.seek(0) 
    image = ImageReader(imgdata) 

    doc = SimpleDocTemplate("hello.pdf") 
    style = styles["Normal"] 
    story = [Spacer(0, inch)] 
    img = PdfImage(image, width=200, height=200) 

    for i in range(10): 
     bogustext = ("Paragraph number %s. " % i) 
     p = Paragraph(bogustext, style) 
     story.append(p) 
     story.append(Spacer(1,0.2*inch)) 

    story.append(img) 

    for i in range(10): 
     bogustext = ("Paragraph number %s. " % i) 
     p = Paragraph(bogustext, style) 
     story.append(p) 
     story.append(Spacer(1,0.2*inch)) 

    doc.build(story, onFirstPage=myFirstPage, onLaterPages=myLaterPages, canvasmaker=PageNumCanvas) 
Смежные вопросы