svglib icon indicating copy to clipboard operation
svglib copied to clipboard

SvgImage(Flowable) or SvgReader class that provides convenience functions to scale or wrap the image to a specified space using reportlab.platypus?

Open skidzo opened this issue 8 years ago • 2 comments

Something like this would be nice for a future release:

SvgImage(Flowable):
    def __init__(self,fname)
        pass
    def drawOn(self, canv, x, y, _sW=0):
        pass

Maybe it is sufficient to just provide a SvgReader() class, that gives the freedom to implement this in a flexible way...

The wrap() can be overloaded, whereas the wrapOn() method should not be touched.

I found an example somewhere that explains what it could look like, only that this shows how to embed a PDF file:

from reportlab.platypus import Flowable
from pdfrw import PdfReader,PdfDict
from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl

class PdfAsset(Flowable):
    def __init__(self,fname,width=None,height=None,kind='direct'):

        self.page = PdfReader(fname=fname, decompress=False).pages[0]
        self.xobj = pagexobj(self.page)

        self.imageWidth = width
        self.imageHeight = height
        x1, y1, x2, y2 = self.xobj.BBox

        self._w, self._h = x2 - x1, y2 - y1
        if not self.imageWidth:
            self.imageWidth = self._w
        if not self.imageHeight:
            self.imageHeight = self._h
        self.__ratio = float(self.imageWidth)/self.imageHeight
        if kind in ['direct','absolute'] or width==None or height==None:
            self.drawWidth = width or self.imageWidth
            self.drawHeight = height or self.imageHeight
        elif kind in ['bound','proportional']:
            factor = min(float(width)/self._w,float(height)/self._h)
            self.drawWidth = self._w*factor
            self.drawHeight = self._h*factor

    def wrap(self, width, height):
        return self.imageWidth, self.imageHeight

    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.xobj
        if isinstance(img, PdfDict):
            xscale = self.imageWidth / img.BBox[2]
            yscale = self.imageHeight / img.BBox[3]
            canv.translate(x, y)
            canv.scale(xscale, yscale)
            canv.doForm(makerl(canv, img))
        else:
            #canv.drawInlineImage(img, x, y-self.imageHeight, self.imageWidth, self.imageHeight)
            canv.drawImage(img, x, y, self.imageWidth, self.imageHeight)
        canv.restoreState()

skidzo avatar Jan 23 '17 13:01 skidzo

Not sure if this is what you want, but I think that the reportlab.lib.utils.ImageReader class now accepts drawings as inputs so any svglib drawing can be used as an image. In any case drawings were already flowables so can be added to a story.

replabrobin avatar Feb 20 '17 10:02 replabrobin

Well that's partly because I don't really know what I want, I just looked at Image Reader "Wraps up either PIL or Java to get data from bitmaps" class in my open source version=3.3.0, and there is no such fancy drawing acceptance as you described. I don't have any Problem to add a drawing to the "story", I was just thinking that it would be fancy to have a possibility to have my own SvgImage class that accepts a file or file like object as Input and where I can implement what it's drawOn method would do.

Without having to (re)write a lot of code, this seems to be impossible, using reportlab and svglib...

for now I have found that this function is needed to (re)scale a drawing returned from svg2rlg


def scaleDrawing(drawing,factor,showBoundary=False):
    """
    scale a reportlab.graphics.shapes.Drawing() object, 
    leaving its aspect ratio unchanged
    """
    sx=sy=factor
    drawing.width,drawing.height = drawing.minWidth()*sx, drawing.height*sy
    drawing.scale(sx,sy)
    if showBoundary:
        drawing._showBoundary = True
    
    return drawing

skidzo avatar Feb 20 '17 22:02 skidzo