user1755180
user1755180

Reputation: 191

Rotated document with ReportLab (vertical text)

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

Answers (6)

vpeiter
vpeiter

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

Don Kirkby
Don Kirkby

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

Diego H. O&#39;Hagan
Diego H. O&#39;Hagan

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

Diego H. O&#39;Hagan
Diego H. O&#39;Hagan

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

KJYDavis
KJYDavis

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:

Table with vertical text.

Upvotes: 17

seleucia
seleucia

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

Related Questions