[reportlab-users] Drawing an image in the background of a frame

Robin Becker robin at reportlab.com
Fri Sep 27 09:15:09 EDT 2019


Hi Lele,

it seems you will need to determine if the flowables will fit into the space you provide.

You can do that by creating a fake document. It will have a single template with two frames. It should have _doSave=0 
set on it to inhibit automatically writing the document.

First frame is the size you want with padding set as you desire. Second frame is infinitely sized (or at least much 
bigger than your flowabels require).

You feed the flowables into a story and pass that to your fake document. When the whole story is processed you can check 
to see what the document's current flowable is. If it points to the first frame you know that your content will fit into 
a frame of the size you chose.

An alternative to the above you can try to measure your content using the function 
reportlab.platypus.flowables._listWrapOn. Which is used by some of the container methods.

So now you can decide what you will do with the main document story.

Suppose you decide to do the image + frame.

The outline of how this should be done is as follows.

0) let the inner space width and height be ws, hs
1) Draw the image you need to assume that image height >= hs and image width >= ws

You need a flowable class, MAGIC which takes your flowables as an argument. This is a zero height flowable (ie it uses 
no space vertically). That means it can draw at y - inner frame height. Its draw method will draw the flowables itself 
rather than rely of automatic layout.

class Magic(Flowable):
	_ZEROSIZE=1

	def __init__(self, F, ws=0, hs=0, imw=0):
		self._F = F
		self._ws = ws
		self._hs = hs
		self._imw = imw

	def wrap(self,aW,aH):
		return 0, 0

	def drawOn(self, canv, x0, y0, _sW=0):
		#we assume the drawing was at x=x0 and ended at y=y0
		x = x0 + self._imw - self._ws
		y = y0 - self._hs
		#we now draw all of our content with an appropriate shift
		for f in F:
			w, h = f.wrap(ws,hs)
			y -= h
			f.drawOn(canv,x,y)


I put this into action as follows and it seems to work

from reportlab.platypus import Flowable
from reportlab.lib.colors import toColor
from reportlab.platypus import SimpleDocTemplate, Paragraph, Image
from reportlab.lib.styles import getSampleStyleSheet
class Magic(Flowable):
	_ZEROSIZE=1

	def __init__(self, F, ws=0, hs=0, imw=0, bgFill='pink', bgStroke=None):
		self._F = F
		self._ws = ws
		self._hs = hs
		self._imw = imw
		self._bgStroke = bgStroke
		self._bgFill = bgFill

	def wrap(self,aW,aH):
		return 0, 0

	def drawOn(self, canv, x0, y0, _sW=0):
		#we assume the drawing was at x=x0 and ended at y=y0
		x = x0 + self._imw - self._ws
		y = y0 + self._hs
		if self._bgStroke or self._bgFill:
			canv.saveState()
			if self._bgStroke:
				canv.setStrokeColor(toColor(self._bgStroke))
			if self._bgFill:
				canv.setFillColor(toColor(self._bgFill))
			canv.rect(x,y0,self._ws,self._hs,fill=1 if self._bgFill else 0, stroke=1 if self._bgStroke else 0)
			canv.restoreState()

		print('x=%s y=%s' % (x,y))
		#we now draw all of our content with an appropriate shift
		for f in self._F:
			w, h = f.wrap(self._ws,self._hs)
			print('w=%s h=%s' % (w,h))
			y -= h
			f.drawOn(canv,x,y)

def lele_test():
	ss = getSampleStyleSheet()
	N = ss['Normal']
	H3 = ss['Heading3']

	#create some flowables for the space
	F = [
		Paragraph('Heading1',H3),
		Paragraph('This is a long paragraph to show that we will wrap as normal.',N),
		Paragraph('Heading2',H3),
		Paragraph('This is another longer paragraph to show that we will wrap as normal. Hello World!',N),
		]

	doc = SimpleDocTemplate('tlele.pdf')
	S = [
		Image('python_logo.png', width=5*72, height=7*72, hAlign='LEFT'),
		Magic(F, 2.5*72, 4*72, 5*72),
		]
	doc.build(S)

if __name__=='__main__':
	lele_test()


On 26/09/2019 19:17, Lele Gaifax wrote:
> Tim Roberts <timr at probo.com> writes:
> 
>> Lele Gaifax wrote:
>>> I'm asked if it would be possible to obtain something like the following
>>> sketch, where I have a frame with a "background" image and, in a specified
>>> area (a "subframe"), a superimposed list of flowables (paragraphs of text)
>>> with a "white" background:
>>
>> Are you trying to do an automated magazine layout of some kind? That's an
>> extremely difficult thing to automate.  People, as a rule, always do a
>> better job of determining the visual appeal of a layout.
> 
> Yes, sort of. But I provide just the layout machinery, then there is person
> who refines the image and the description, until the result please the eye.
> 
>>> The problem is that I should omit the image *iff* the flowables do not fit in
>>> the given area, possibly "trying" further combinations of image+flowables
>>> until one fits, so I cannot simply draw the image and then do a
>>> "subframe.add(flowable)".
>>
>> There are some conflicting requirements here.  Does the size of the image
>> represent a bounding box for the flowables?
> 
> No, basically there will be an area that will be filled by the image, and an
> overlapping "sub area" where the flowables shall fit, with a reasonable
> background to make it readable (say, white). The two boxes are of fixed sizes,
> independent from the image and flowables, and always in the same position.
> 
>> There are ways to determine the height of a flowable for some fixed width,
>> and that may be all you need to know.
> 
> Yes, I know about that: I will try to code it so that it first does a "fit
> test" on the flowable (mimicking the check done in Frame.add(), pity that it's
> not a separate method, disjunct from the actual drawing), and only if that
> succeeds will lay down the image, clear the "sub area" filling it with a
> background color and eventually draw the flowable there, if anything cleverer
> doesn't come up.
> 
> Thanks&bye, lele.
> 


-- 
Robin Becker


More information about the reportlab-users mailing list