I'm using the Table of Report Lab library to print a table on a PDF report. I would like to know if it's possible to configure the table to perform an automatic wrapping of the content of a cell.
For example, I have a text that doesn't fit on a cell inside a column. I would like that the table performs the wrap automatically adjusting the content of the cells to fit on the columns width. Is it possible?
You can put any flowable in a table element. It is probably good practice to have all table elements as flowables, so they can be styled the same. For your case you will most likely need a Paragraph flowable. eg.
styles = getSampleStyleSheet()
text = Paragraph("long line",
styles['Normal'])
You can put `text' into the data you feed to a table and it will automatically wrap.
My solution, force newline in the string:
def __chopLine(line, maxline):
cant = len(line) / maxline
cant += 1
strline = ""
index = maxline
for i in range(1,cant):
index = maxline * i
strline += "%s\n" %(line[(index-maxline):index])
strline += "%s\n" %(line[index:])
return strline
*whole code of word wrap
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph, Table, TableStyle
from reportlab.lib.enums import TA_JUSTIFY, TA_LEFT, TA_CENTER
from reportlab.lib import colors
# bodytext style used for wrapping data on flowables
styles = getSampleStyleSheet()
styleN = styles["BodyText"]
#used alignment if required
styleN.alignment = TA_LEFT
styleBH = styles["Normal"]
styleBH.alignment = TA_CENTER
hdescrpcion = Paragraph('''<b>descrpcion</b>''', styleBH)
hpartida = Paragraph('''<b>partida</b>''', styleBH)
descrpcion = Paragraph('long long long long long long long long long long long long long long long long long long long long line ', styleN)
partida = Paragraph('1', styleN)
data= [[hdescrpcion, hpartida],
[partida ,descrpcion]]
table = Table(data)
table.setStyle(TableStyle([
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
]))
c = canvas.Canvas("a.pdf", pagesize=A4)
table.wrapOn(c, 50, 50)
table.drawOn(c, 100,600)
c.save()
Related
Goal
I am trying to add a text to a table cell where the text is a combination of 2 strings and the space between the strings of variable size so that the final text has the same length and it appears as if the second string is right aligned.
I can either use format or ljust to combine the strings in python.
period = "from Monday to Friday"
item_text = "Some txt"
item_text2 = "Some other txt"
t1 = "t1: {:<30}{:0}".format(item_text,period)
t2 = "t2: {:<30}{:0}".format(item_text2,period)
t3 = f"t3: {item_text.ljust(30)}{period}"
t4 = f"t4: {item_text2.ljust(30)}{period}"
from pprint import pprint
pprint(t1)
pprint(t2)
pprint(t3)
pprint(t4)
Text in python with variable space length between strings
However, if I add this text to a docx table, the space between the strings changes.
from docx import Document
doc = Document()
# Creating a table object
table = doc.add_table(rows=2, cols=2, style="Table Grid")
table.rows[0].cells[0].text = f"{item_text.ljust(30)}{period}"
table.rows[1].cells[0].text = f"{item_text2.ljust(30)}{period}"
def set_col_widths(table):
widths = tuple( Cm(val) for val in [15,8])
for row in table.rows:
for idx, width in enumerate(widths):
row.cells[idx].width = width
set_col_widths(table)
doc.save("test_whitespace.docx")
Text in word. Space between strings changed.
Note
I am aware that I could add a table to the table cell and left adjust the left and right adjust the right but that seems like way more code to write.
Question
Why is the spacing changing in the word document and how can I create the text differently to get the desired goal?
I'm trying to insert a large amount of images on a MS Word document using python docx library, but it is too slow. I have around 900 images.
My procedure to do this is create a dictionary with a label and the location of the image. Then I create a table (2 columns) with the labels to positionate the images and with a function I defined, I replace the labels with the images.
import docx
import os
document = docx.Document("./" + template)
def insert_image2(image_path, image_label, document, image_inches = 3):
###
#we insert the image path and the image label on the text. The function replaces the label on the text by the image.
#one example for a label could be image_label = "[image]""
###
from docx import Document
from docx.shared import Inches
for table in document.tables:
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
if image_label in paragraph.text:
paragraph.text = paragraph.text.strip().replace(image_label, "")
run = paragraph.add_run()
run.add_picture(image_path, width=Inches(image_inches))
return
polar_values = os.listdir("./plots/polar")
l_fil = [x for x in polar_values if "foundation" not in x]
polar_keys = [i.split("WT", 1)[1].split(".png")[0].strip() for i in l_fil]
polar_dict = dict(zip(polar_keys,polar_values))
even = polar_keys[1:][::2] #even indexed numbers
odd = polar_keys[::2] #odd indexed numbers
#Adding table
table = document.add_table(rows=1, cols=2)
for ind in range(len(odd)):
row = table.add_row().cells
row[0].add_paragraph(odd[ind])
row[1].add_paragraph(even[ind])
for key, value in polar_dict.items():
insert_image2("./plots/polar/" + value, key, document)
document.save(out_file)
EDIT
I have decided to append the image to the bottom of the document with the command "run.add_picture(image_path)".
I think you over-engineered this thing. If you want to insert a bunch of images into a Word document, just use VBA for the task.
Either...select folder with images...
Sub InsertImages()
Dim doc As Word.Document
Dim fd As FileDialog
Dim vItem As Variant
Dim mg1 As Range
Dim mg2 As Range
Set fd = Application.FileDialog(msoFileDialogFilePicker)
Set doc = ActiveDocument
With fd
.Filters.Add "Images", "*.gif; *.jpg; *.jpeg", 1
.FilterIndex = 1
If .Show = -1 Then
For Each vItem In .SelectedItems
Set mg2 = ActiveDocument.Range
mg2.Collapse wdCollapseEnd
doc.InlineShapes.AddPicture _
FileName:=vItem, _
LinkToFile:=False, SaveWithDocument:=True, Range:=mg2
Set mg1 = ActiveDocument.Range
mg1.Collapse wdCollapseEnd
mg1.Text = vbCrLF & vbCrLf
Next vItem
End If
End With
Set fd = Nothing
End Sub
Or...don't select folder; map straight to it...
Sub GetPictures()
Dim sPic As String
Dim sPath As String
sPath = "c:\myfolder\"
sPic = Dir(sPath & "*.jpg")
Do While sPic <> ""
Selection.InlineShapes.AddPicture _
FileName:=sPath & sPic, _
LinkToFile:=False, SaveWithDocument:=True
sPic = Dir
Selection.TypeParagraph
Selection.TypeParagraph
Loop
End Sub
Post back if you have additional questions related to this. If you want to do something quite different, post a different question.
I have a little code and I would like to wrap my long string in every 10th character and then add it into a PDF using reportlab:
This is how I try:
text = '*long_text_long_text_long_text_long_text*'
text = "\n".join(wrap(text, 10))
canvas.drawString(5,227, text)
My pdf was created but where I want to break the lines I can only see black rectangles. You can see the attached picture:
Can you help me? Thank you!
drawString draws a single line. so you will need to adjust the coordinate for each line in a loop.
y = 227
for line in wrap(text, 10):
canvas.drawString(5, y, line)
y += 15
An alternative to placing each line individually is using Paragraph:
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A5
from reportlab.platypus import Paragraph
text = "long text<br />long text<br />long text<br />"
text_width=A5[0] / 2
text_height=A5[1] / 2
x = A5[0]/4
y = A5[1]/4
pdf = canvas.Canvas(filename="test.pdf", pagesize=A5)
styles = getSampleStyleSheet()
p = Paragraph(text, styles["Normal"])
p.wrapOn(pdf, text_width, text_height)
p.drawOn(pdf, x, y)
pdf.save()
In addition to supporting manually put line breaks, the Paragraph also supports automatic line breaks.
Using ReportLab, I want to render a block of text with a large font size. Right now, my code places the text within a Paragraph so it can be word wrapped. However, the text turns out crammed together when rendered.
It seems like the height I specified for the Paragraph object is not being taken into account. Is there an attribute for Paragraph that I can add to fix this?
My Code Below:
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.platypus import Paragraph
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER
doc = canvas.Canvas('test.pdf')
p = ParagraphStyle('test')
p.textColor = 'black'
p.borderColor = 'black'
p.borderWidth = 1
p.alignment = TA_CENTER
p.fontSize = 100
para = Paragraph("THIS IS A REALLY LONG AND BIG STRING OF TEXT RIGHT HERE!!!!!", p)
para.wrapOn(doc,1200,1000)
para.drawOn(doc, 0.5*inch, 6*inch)
doc.save()
The answer is to set the leading attribute to 120:
p.leading = 120
By default, a style has a fontSize of 10 with a leading value of 12. The leading parameter specifies the distance down to move when advancing from one text line to the next.
I have a block of text that is dynamically pulled from a database and is placed in a PDF before being served to a user. The text is being placed onto a lined background, much like notepad paper. I want to space the text so that only one line of text is between each background line.
I was able to use the following code to create a vertical spacing between paragraphs (used to generate another part of the PDF).
style = getSampleStyleSheet()['Normal']
style.fontName = 'Helvetica'
style.spaceAfter = 15
style.alignment = TA_JUSTIFY
story = [Paragraph(choice.value,style) for choice in chain(context['question1'].itervalues(),context['question2'].itervalues())]
generated_file = StringIO()
frame1 = Frame(50,100,245,240, showBoundary=0)
frame2 = Frame(320,100,245,240, showBoundary=0)
page_template = PageTemplate(frames=[frame1,frame2])
doc = BaseDocTemplate(generated_file,pageTemplates=[page_template])
doc.build(story)
However, this won't work here because I have only a single, large paragraph.
Pretty sure what yo u want to change is the leading. From the user manual in chapter 6.
To get double-spaced text, use a high
leading. If you set
autoLeading(default "off") to
"min"(use observed leading even if
smaller than specified) or "max"(use
the larger of observed and specified)
then an attempt is made to determine
the leading on a line by line basis.
This may be useful if the lines
contain different font sizes etc.
Leading is defined earlier in chapter 2:
Interline spacing (Leading)
The vertical offset between the point
at which one line starts and where the
next starts is called the leading
offset.
So try different values of leading, for example:
style = getSampleStyleSheet()['Normal']
style.leading = 24
Add leading to ParagraphStyle
orden = ParagraphStyle('orden')
orden.leading = 14
orden.borderPadding = 10
orden.backColor=colors.gray
orden.fontSize = 14
Generate PDF
buffer = BytesIO()
p = canvas.Canvas(buffer, pagesize=letter)
text = Paragraph("TEXT Nro 0001", orden)
text.wrapOn(p,500,10)
text.drawOn(p, 45, 200)
p.showPage()
p.save()
pdf = buffer.getvalue()
buffer.close()
The result