Reputation: 191
I'm trying to get something like the following snippet to actually draw this document rotated by 90 degrees. The pagesize is already the way I want it in the end, but the text is still horizontal. How can I rotate the text making it vertical?
style = getSampleStyleSheet()
normal = style["Normal"]
normal.alignment = TA_CENTER
normal.fontName = "Helvetica"
normal.fontSize = 15
pdf = SimpleDocTemplate("testplatypus.pdf", pagesize = (80, 250), rightMargin=10, leftMargin=10, topMargin=20,bottomMargin=20)
story = []
text = "I really need this to be wrapped and vertical!"
para = Paragraph(text, normal)
story.append(para)
pdf.build(story)
Upvotes: 12
Views: 13368
Reputation: 63
Inspired by the solution posted by KJYDavis I made a VerticalParagraph, so that it supports the styling features that a Paragraph has, like ParagraphStyle, XML Markup Tags and Intra-paragraph markup (Reportlab Docs Chapter 6)
from reportlab.platypus import Paragraph
class VerticalParagraph(Paragraph):
"""Paragraph that is printed vertically (rotated 90 degrees counterclockwise"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.horizontal_position = -self.style.leading
def draw(self):
""" Draw text """
canvas = self.canv
canvas.rotate(90)
canvas.translate(1, self.horizontal_position)
super().draw()
def wrap(self, available_width, _):
""" Wrap text in table """
string_width = self.canv.stringWidth(
self.getPlainText(), self.style.fontName, self.style.fontSize
)
self.horizontal_position = - (available_width + self.style.leading) / 2
height, _ = super().wrap(availWidth=1 + string_width, availHeight=available_width)
return self.style.leading, height
It can be used the same way you would use a Paragraph flowable, although I have only tried it within tables.
stylesheet = getSampleStyleSheet()
text = VerticalParagraph('<para align="CENTER">Some <b>BOLD</b> <i>Text</i></para>', stylesheet['BodyText'])
An additional benefit is that this version doesn't need direct access to private members of Canvas such as _fontname
, _fontsize
or _leading
, which is good if your project uses pylint or something similar.
Upvotes: 2
Reputation: 56620
If you don't need the text wrapping features of Paragraph
, but you want more flowable support than the raw canvas, you can put rotated text in a Drawing
. Here's a simple example:
from reportlab.graphics.shapes import Drawing, Group, String
from reportlab.lib.enums import TA_CENTER
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate, Paragraph
style = getSampleStyleSheet()
normal = style["Normal"]
normal.alignment = TA_CENTER
normal.fontName = "Helvetica"
normal.fontSize = 15
pdf = SimpleDocTemplate("testplatypus.pdf",
pagesize=(80, 250),
rightMargin=10,
leftMargin=10,
topMargin=20,
bottomMargin=20)
story = []
text_lines = ["I really need this to be",
"wrapped and vertical!"]
para = Paragraph('Basic text.', normal)
story.append(para)
drawing = Drawing(50, 110)
group = Group()
group.rotate(-90)
for i, line in enumerate(text_lines):
group.add(String(-100, 15*(2-i), line, fontName='Helvetica'))
drawing.add(group)
story.append(drawing)
para2 = Paragraph('More text.', normal)
story.append(para2)
pdf.build(story)
Upvotes: 0
Reputation: 117
I found an easy way around this USING canvas.drawString(). It is in a regular python function. The "WHATEVER TEXT" in the example below will RENDER TEXT VERTICALLY on your pdf. Read the comments in the code to fully understand how to achieve this.
def lista_privados(request, pk):
g1 = get_object_or_404(Grupo, pk=pk)
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="{}"'.format(g1)
buffer = BytesIO()
c = canvas.Canvas(buffer, pagesize=A4)
c.setPageSize(landscape(letter))
#Whatever your other code is
Important it only works at the very end, before your showPage()
c.rotate(90)
c.drawString(16*cm, -14*cm, "WHATEVER TEXT 1")
c.drawString(16*cm, -14.4*cm, "WHATEVER TEXT 2")
#c.drawString(x*cm, y*cm, "TEXT YOU WANT TO DISPLAY VERICALLY")
#Note the '-' is VERY IMPORTANT OR IT WON'T WORK
#ONLY WRITE THE c.rotate(90) one, you can list as many 'c.drawString(16*cm, -14*cm, "WHATEVER TEXT")' as you wish
#The font size will be the last one you set on your code, so if for example you do "c.setFont('Helvetica', 5)" just the "c.rotate(90)", "WHATEVER TEXT" will be Helvetica, 5.
c.showPage() #save page
c.save()
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
template = 'privados/imp_list.html'
return response
Upvotes: 3
Reputation: 117
If you want to convert horizontal text into vertical text, I found an easy straight way.
def your_function_name(request, pk):
g1 = get_object_or_404(Grupo, pk=pk)
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=Plista2.pdf'
buffer = BytesIO()
c = canvas.Canvas(buffer, pagesize=A4)
c.setPageSize(landscape(letter))
Upvotes: -2
Reputation: 283
This is the top google result for this question, so I thought I'd post a better solution. My answer comes straight from here
If you are using a doc template, you can use a very lightweight Flowable that will create vertical text in a particular table cell.
# rotatedtext.py
from reportlab.platypus.flowables import Flowable
class verticalText(Flowable):
'''Rotates a text in a table cell.'''
def __init__(self, text):
Flowable.__init__(self)
self.text = text
def draw(self):
canvas = self.canv
canvas.rotate(90)
fs = canvas._fontsize
canvas.translate(1, -fs/1.2) # canvas._leading?
canvas.drawString(0, 0, self.text)
def wrap(self, aW, aH):
canv = self.canv
fn, fs = canv._fontname, canv._fontsize
return canv._leading, 1 + canv.stringWidth(self.text, fn, fs)
Then to use it in a doc:
# vertical_text_table.py
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib import colors
from reportlab.lib.colors import HexColor
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.platypus import (
BaseDocTemplate, Frame, Paragraph, NextPageTemplate,
PageBreak, PageTemplate, Image, Table, TableStyle, Spacer)
from rotatedtext import verticalText
document = BaseDocTemplate(
'Vertical.pdf')
Elements = []
titleFrame_1 = Frame(
0.5*inch, 0.75*inch, 7*inch, 9*inch, id='col1', showBoundary=0)
titleTemplate_1 = PageTemplate(
id='OneCol', frames=titleFrame_1)
document.addPageTemplates([titleTemplate_1])
cw = [1.2*inch] + [1*inch]*6
rh = [0.25*inch] + [.6*inch] + [0.25*inch]*7
data = [
['Some', 'Reporting', '', 'Data', '', 'Here', ''],
['', verticalText('Vertical'), verticalText('Text'),
verticalText('Vertical'), verticalText('Text'),
verticalText('Vertical'), verticalText('Text')],
['Row1', '0', '0', '69', '803', '20751', '11627'],
['Row2', '0', '0', '1', '0', '1096', '103'],
['Row3', '0', '0', '0', '0', '233', '1'],
['Row4', '0', '0', '0', '0', '694', '38'],
['Row5', '0', '0', '23', '2', '1319', '2'],
['Row6', '0', '0', '0', '0', '0', '0'],
['TOTAL', '0', '0', '93', '805', '24093', '11771'],
]
ts = [
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
('SPAN', (1, 0), (2, 0)),
('SPAN', (3, 0), (4, 0)),
('SPAN', (5, 0), (6, 0)),
('SPAN', (0, 0), (0, 1)),
('ALIGN', (0, 0), (-1, 1), 'CENTER'),
('ALIGN', (0, 2), (-1, -1), 'RIGHT'),
('VALIGN', (0, 0), (-1, -2), 'MIDDLE'),
('FONT', (0, 0), (-1, 1), 'Helvetica-Bold', 7, 7),
('FONT', (0, 2), (0, -2), 'Helvetica-Bold', 7, 7),
('FONT', (1, 2), (-1, -2), 'Helvetica', 7, 7),
('FONT', (0, -1), (-1, -1), 'Helvetica-Bold', 8, 8),
('TEXTCOLOR', (0, -1), (-1, -1), colors.white),
('BACKGROUND', (0, -1), (-1, -1), colors.black),
]
t = Table(
data, style=ts,
colWidths=cw, rowHeights=rh)
Elements.append(t)
document.build(Elements)
Which gives:
Upvotes: 17
Reputation: 1056
Maybe you can do that with adding build methods.
It is something like that.
pdf = SimpleDocTemplate("testplatypus.pdf", pagesize = (80, 250), rightMargin=10, leftMargin=10, topMargin=20,bottomMargin=20)
pdf.build(Story, onFirstPage = myFirstPage, onLaterPages=myLaterPages)
def myFirstPage(canvas, doc):
canvas.saveState()
canvas.rotate(90)
canvas.restoreState()
def myLaterPages(canvas, doc):
canvas.saveState()
canvas.rotate(90)
canvas.restoreState()
Upvotes: 1