使用 ReportLab 旋转文档(垂直文本)

问题描述 投票:0回答:6

我正在尝试获取类似以下代码片段的内容来实际绘制旋转 90 度的文档。页面大小已经是我最终想要的样子,但文本仍然是水平的。如何旋转文本使其垂直?

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)
python reportlab
6个回答
15
投票

这是这个问题的最佳谷歌结果,所以我想我应该发布一个更好的解决方案。我的答案直接来自这里

如果您使用文档模板,则可以使用非常轻量级的 Flowable,它将在特定的表格单元格中创建垂直文本。

# 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)

然后在文档中使用它:

# 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)

这给出了:


3
投票

我找到了一个简单的方法来解决这个问题,使用canvas.drawString()。它是一个常规的 python 函数。下面示例中的“无论文本”都会在 pdf 上垂直渲染文本。阅读代码中的注释以充分理解如何实现这一点。

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

重要的是它只在最后,在你的 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

2
投票

受到 KJYDavis 发布的解决方案的启发,我制作了一个 VerticalParagraph,以便它支持段落具有的样式功能,例如 ParagraphStyle、XML 标记标签和段落内标记(Reportlab 文档第 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

它的使用方式与使用 Paragraph flowable 的方式相同,尽管我只在表格中尝试过。

stylesheet = getSampleStyleSheet()
text = VerticalParagraph('<para align="CENTER">Some <b>BOLD</b> <i>Text</i></para>', stylesheet['BodyText'])

另一个好处是,此版本不需要直接访问 Canvas 的私有成员,例如

_fontname
_fontsize
_leading
,如果您的项目使用 pylint 或类似的东西,这会很好。


1
投票

也许您可以通过添加构建方法来做到这一点。

是这样的。

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()

0
投票

如果您不需要

Paragraph
的文本换行功能,但您想要比原始画布更流畅的支持,则可以将旋转文本放入
Drawing
中。这是一个简单的例子:

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)

-2
投票

如果你想将水平文本转换为垂直文本,我找到了一个简单直接的方法。

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))
© www.soinside.com 2019 - 2024. All rights reserved.