How to achieve text wrapping and word seperation inside python reportlab platypus frames?

1.4k views Asked by At

I would like to create some pdf files from text and data. I set up some frames with platypus reportlab but when i try to fill them up the text is just written beyond the page margin in a single frame.

Using several options from the ParagraphStyle() class does not wrap the text at the end of the frame.

But since there appears to be some kind of wrapping operation with the given text i am confident that this would also work within the frame borders..

Another thing which troubles me is, that I can just write on one single frame. Using different "stories" allows me write on all of them.

Does anyone knows why i face these troubles?

This script below produces the following pdf file snippet:

resultpdf_snippet

Any help apprechiated. Regards

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.platypus import Paragraph, Frame
from reportlab.lib.colors import Color, black, blue, red
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm


c = canvas.Canvas('platypus_text_to_pdf.pdf', pagesize=(A4))

styles = getSampleStyleSheet() 
fiddle_style = ParagraphStyle('fiddle_style',
                           fontName="Helvetica-Bold",
                           backColor = '#FFFF00',
                           alignment = 0,
                           allowOrphans = 0,
                           allowWidows = 0,
                           borderColor = None,
                           borderPadding = 0,
                           borderRadius = None,
                           borderWidth = 100,
                           bulletAnchor = 'start',
                           bulletFontName = 'Helvetica',
                           bulletFontSize = 10,
                           bulletIndent = 0,
                           embeddedHyphenation = 0,
                           endDots = None,
                           firstLineIndent = 0,
                           fontSize = 10,
                           justifyBreaks = 0,
                           justifyLastLine = 0,
                           leading = 12,
                           leftIndent = 0,
                           linkUnderline = 0,
                           rightIndent = 20,
                           spaceAfter = 0,
                           spaceBefore = 0,
                           spaceShrinkage = 0.05,
                           splitLongWords = 1,
                           strikeGap = 1,
                           strikeOffset = 0.25,
                           strikeWidth =1,
                           textColor = Color(0,0,0,1),
                           textTransform = None,
                           underlineGap = 1,
                           underlineOffset = -0.125,
                           underlineWidth =1,
                           uriWasteReduce = 0,
                           wordWrap = None,
                           parent=styles['Heading2'],
                           )

dn_frame = Frame(A4[1]/3, A4[1]/3+(A4[1]/3)+(A4[1]/3-100),A4[1]*(2/3),100, showBoundary=1) #Dish name and serves
ingr_frame = Frame(A4[1]/3, (A4[1]*2/3)-(A4[1]/3-100),A4[1]*(2/3),2*(A4[1]/3-100), showBoundary=1) #ingredients
blw_pic_frame = Frame(0, (A4[1]*2/3)-(A4[1]/3-100),A4[1]*(1/3),(A4[1]/3-100), showBoundary=1)
facts_frame = Frame(0, 0,100,100+(A4[1]*1/3), showBoundary=1)
todo_frame = Frame(100, 0,A4[0]-100,100+(A4[1]*1/3), showBoundary=1)

text= '''
        „Lorem ipsum dolor sit amet, consectetur adipisici elit, …" ist ein Blindtext, der nichts bedeuten soll, sondern als Platzhalter im Layout verwendet wird, um einen Eindruck vom fertigen Dokument zu erhalten. Die Verteilung der Buchstaben und der Wortlängen des pseudo-lateinischen Textes entspricht in etwa der natürlichen lateinischen Sprache. Der Text ist absichtlich unverständlich, damit der Betrachter nicht durch den Inhalt abgelenkt wird.
    '''
dn_story = []
dn_story.append(Paragraph(text, fiddle_style))

dn_frame.addFromList(dn_story,c)
ingr_frame.addFromList(dn_story,c)
blw_pic_frame.addFromList(dn_story,c)
facts_frame.addFromList(dn_story,c)
todo_frame.addFromList(dn_story,c)

c.save()
2

There are 2 answers

0
symbolinsight On BEST ANSWER

You can feel for the remaining frame space with framename._aH you know the space requirement from floating objects with floatcontentname.wrap(todo_frame._aW,todo_frame._aH)[1]

Example for my ingr_frame:

#create the frame
ingr_frame = Frame(A4[1]/3, 100+(A4[1]*1/3), A4[0]-A4[1]/3, A4[1]-100-(100+(A4[1]*1/3))-50, showBoundary=0)
#create a text
ingr_text = Paragraph('hello', styleN)
#get text heigth requirement
ingr_text.wrap(ingr_frame._aW,ingr_frame._aH)[1]
#get available frame heigh space
ingr_frame._aH 
0
symbolinsight On
  1. I was not able to write on all frames since my text would not fit on them properly
  2. Text wrapping did not work because (and this is a guess) I messed up the correct width values while frame creation and also didn't set explicit margin paddings.

The below code now produces this PDF as an example: https://i.stack.imgur.com/7BW0l.jpg

Now, is there any way to feel for the remaining space in the frames? I want to reduce the text size based on this remaning space check since I must stay on one DIN A4 page with any recipe I try to generate

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.platypus import Paragraph, Frame, Spacer
from reportlab.lib.colors import Color
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_JUSTIFY, TA_LEFT, TA_CENTER


c = canvas.Canvas('platypus_text_to_pdf.pdf', pagesize=(A4))

styles = getSampleStyleSheet() 

styleN = styles["BodyText"]
styleN.alignment = TA_LEFT 

styleH = styles["Heading2"]
styleH.alignment = TA_LEFT


#Recipe Image
recipie_image = "recipe_images/Rote Beete Carpaccio mit Walnüssen und Pinienkernen.png"
c.drawImage(recipie_image, 0, A4[2]*2/3, A4[2]/3, A4[2]/3)
                                                      

dn_frame = Frame(A4[2]/3, A4[2]-100, A4[0]-A4[2]/3, 100, leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, showBoundary=1) #Dish name and serves
ingr_frame = Frame(A4[2]/3, 100+(A4[2]*1/3), A4[0]-A4[2]/3, A4[2]-100-(100+(A4[2]*1/3)), leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, showBoundary=1) #ingredients
blw_pic_frame = Frame(0, 100+(A4[2]*1/3), A4[2]*(1/3), A4[2]/3-100, leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, showBoundary=1)
facts_frame = Frame(0, 0,100,100+(A4[2]*1/3), leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, showBoundary=1)
todo_frame = Frame(100, 0,A4[0]-100,100+(A4[2]*1/3), showBoundary=1)




text= '''
        „Lorem ipsum dolor sit amet, consectetur adipisici elit, …" ist ein Blindtext, der nichts bedeuten soll, sondern als Platzhalter im Layout verwendet wird, um einen Eindruck vom fertigen Dokument zu erhalten. Die Verteilung der Buchstaben und der Wortlängen des pseudo-lateinischen Textes entspricht in etwa der natürlichen lateinischen Sprache. Der Text ist absichtlich unverständlich, damit der Betrachter nicht durch den Inhalt abgelenkt wird.
    '''
dn_text = Paragraph('Rote Beete Carpaccio mit Walnüssen und Pinienkernen', styleH)
dn_subtext = Paragraph('Rezept für 4 Portionen', styleN)     
dn_story = []
dn_story.append(dn_text)
dn_story.append(Spacer(1,0.2*cm))
dn_story.append(dn_subtext)
dn_frame.addFromList(dn_story,c)

ingr_story = []
ingr_text = Paragraph(text, styleN)
ingr_story.append(Paragraph('Zutaten', styleH))
ingr_story.append(ingr_text)
ingr_story.append(Spacer(1,0.2*cm))
ingr_story.append(ingr_text)
ingr_story.append(Spacer(1,0.2*cm))
ingr_story.append(ingr_text)
ingr_frame.addFromList(ingr_story,c)

blw_pic_story = []
blw_pic_text = Paragraph(text, styleN)
blw_pic_story.append(Paragraph('Health Data', styleH))
blw_pic_story.append(blw_pic_text)
blw_pic_story.append(Spacer(1,0.2*cm))
blw_pic_frame.addFromList(blw_pic_story,c)

facts_story = []
s=0
while s < 4:
    facts_story.append(Paragraph("Preis $$$\n Kochzeit: 40 min",styleN))
    facts_story.append(Paragraph('Hier kommt ein Bild', styleH))
    facts_story.append(Paragraph("Why so serious?",styleN))
    s=s+1
facts_frame.addFromList(facts_story,c)

todo_story = []
j=0
while j < 4:
    todo_story.append(Paragraph('Schritt: '+str(j+1)+'\n',styleH))
    todo_story.append(Paragraph(10* "Schütteln sie den Salat ",styleN))
    j=j+1
todo_frame.addFromList(todo_story,c)

c.save()