svglib
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?
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()
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.
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