missmely missmely - 3 months ago 13
Python Question

Is it possible to get a Flowable's coordinate position once it's rendered using ReportLab.platypus?

My main goal is to have all the Image flowables on a page to act as though they are clickable links. In order to do this, I would create a canvas.linkRect() and place it over the rendered Image. Here's an example of how I use canvas.linkRect():

canvas.linkURL(
url='url_goes_here',
rect=(x1, y1, x2, y2), #(x1, y1) is the bottom left coordinate of the rectangle, (x2, y2) is the top right
thickness=0, relative=1
)


After looking in the BaseDocTemplate class, I found a method called afterFlowable(self, flowable). I overrode that method and called dir() on the flowable passed in, resulting in this:

['__call__', '__doc__', '__init__', '__module__', '_doctemplateAttr',
'_drawOn', '_fixedHeight', '_fixedWidth', '_frameName', '_hAlignAdjust',
'_showBoundary', '_traceInfo', 'action', 'apply', 'draw', 'drawOn', 'encoding',
'getKeepWithNext', 'getSpaceAfter', 'getSpaceBefore', 'hAlign', 'height',
'identity', 'isIndexing', 'locChanger', 'minWidth', 'split', 'splitOn', 'vAlign',
'width', 'wrap', 'wrapOn', 'wrapped']


It has a width and height attribute I could use to determine how big the linkRect() should be (what x2 and y2 should be), but no information on where the flowable starts (what should x1 and y1 be?).

If all else fails, I thought of somehow pairing a Frame and an Image Flowable together since a Frame has the information I want to create a linkRect(). However, it seems it would be a hassle to know when and how to order a list of Frames with it's respective list of Flowables, on top of having to know exactly where to put those Frames for Images. Is there another way to achieve this or is it not possible?

Thanks!

Answer

After working on this all day today, I finally figured out a nice way to do this! Here's how I did it for anyone else who would like this feature of hyperlinked Image flowables in their PDFs.

Basically, reportlab.platypus.flowables has a class called Flowable that Image inherits from. Flowable has a method called drawOn(self, canvas, x, y, _sW=0) that I override in a new class I created called HyperlinkedImage.

from reportlab.platypus import Image

class HyperlinkedImage(Image, object):

    # The only variable I added to __init__() is hyperlink. I default it to None for the if statement I use later.
    def __init__(self, filename, hyperlink=None, width=None, height=None, kind='direct', mask='auto', lazy=1):
        super(HyperlinkedImage, self).__init__(filename, width, height, kind, mask, lazy)
        self.hyperlink = hyperlink

    def drawOn(self, canvas, x, y, _sW=0):
        if self.hyperlink: # If a hyperlink is given, create a canvas.linkURL()
            x1 = self.hAlignAdjust(x, _sW) # This is basically adjusting the x coordinate according to the alignment given to the flowable (RIGHT, LEFT, CENTER)
            y1 = y
            x2 = x1 + self._width
            y2 = y1 + self._height
            canvas.linkURL(url=self.hyperlink, rect=(x1, y1, x2, y2), thickness=0, relative=1)
        super(HyperlinkedImage, self).drawOn(canvas, x, y, _sW)

Now instead of creating a reportlab.platypus.Image as your image flowable, use the new HyperlinkedImage instead :)

Comments