ReportLab变量nextPageTemplate的

ReportLab变量nextPageTemplate的

我正在将数据库从pfaf.org(面向未来的工厂)转换为pdf书。
关于pageTemplates,我遇到了一个绊脚石。

每个工厂都可以从左对齐或右对齐的页面开始;并可能是两页或更多页。
我有两个模板用于工厂的第一页(左和右),还有两个用于潜在的后续页面的模板。

当前,它的处理方式如下(例如):

for i, plant in enumerate(plants):
  #the first plant is printed on a right-hand page
  if i % 2 == 0:
    template = 'left'
    second = 'right'
  else:
    template = 'right'
    second = 'left'

  Story.append(nextPageTemplate(template,'*', 'secondary_'+template, 'secondary_'+second))
  Story.append(PageBreak())
  #append various paragraphs, jumping between frames, etc...


该代码(您可能会说)对单页工厂很好用。
对于多页工厂,它也可以按预期运行(半)。

但是,您也可能会看到,两(或四等)页面工厂将破坏模板排列,因为上面的代码假定页面位置基于工厂编号而不是页面编号。

在上面的代码位置(即在Story.append周期中),我看不到此解决方案-到那时,我无法确定工厂是否使用了一页以上的内容,因此无法确定我使用的是哪一页目前如此。

我希望我可以从我的自定义docTemplate中调整nextPageTemplate结构,但是我不知道这是否可行。

是吗?还是有其他解决方案?非常感谢您的帮助。已经阅读了很多,但是我能找到的最好的例子并没有完全涵盖这种情况。

如有任何疑问,请询问。
谢谢



谢谢你,尼兹:
麻烦在于我不知道每个工厂要占用多少页。
例如,一个新工厂从一个奇数页开始,因此我给它提供了一个模板循环(“ right”,“ *”,“ secondaryLeft”,“ secondaryRight”)。 [辅助页面只是一个具有适当页边距的框架。]

如果该工厂长一页,没问题,则下一个工厂将具有与上述相反的模板周期。
但是,如果工厂有两页,它将导致下一个工厂再次落在奇数页上,因此模板周期不应更改……我希望这是有道理的。

这是我无法解决的情况...如果您按照我的意思做,则不允许有多个页面工厂。我的大部分代码如下:虽然我尝试将其缩小一些:)希望它仍然包含所有相关内容,并且没有太多不必要的内容。

import os
import sys
import MySQLdb

from reportlab.platypus import Spacer, Image, Table, TableStyle, PageBreak, FrameBreak, paraparser
from reportlab.platypus.doctemplate import BaseDocTemplate, PageTemplate, NextPageTemplate, _doNothing
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.frames import Frame
from reportlab.platypus.flowables import KeepInFrame
from reportlab.platypus.paragraph import Paragraph

from reportlab.lib.units import mm, cm
from reportlab.lib.pagesizes import A4, A5
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER, TA_RIGHT
from reportlab.lib.styles import StyleSheet1, ParagraphStyle as PS
from reportlab.lib import colors

from reportlab.graphics.shapes import Drawing, Rect, String

from reportlab.pdfbase.pdfmetrics import registerFont, stringWidth
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.rl_config import warnOnMissingFontGlyphs
warnOnMissingFontGlyphs = 0

registerFont(TTFont('Museo_', '/home/wuwei/.fonts/Museo300-Regular.ttf'))
registerFont(TTFont('Museo_M', '/home/wuwei/.fonts/Museo500-Regular.ttf'))
registerFont(TTFont('Trebuchet', '/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS.ttf'))
registerFont(TTFont('Trebuchet_I', '/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Italic.ttf'))

## SOME VARIABLE DEFINITIONS ##

titleFont = "Museo_M"
subtitleFont = "Museo_"
stdFont = "Trebuchet"
stdItalic = "Trebuchet_I"
#stdSize = 14

"""CREATE GLOBALS"""
_w, _h = A4
_head_w = 17.5*cm
_head_pad = 0.2*cm
_main_w = 17.2*cm
_budge = 0.3*cm

_left_margin = 1.5*cm
_right_margin = 2.0*cm
_top_margin = 1.5*cm
_bottom_margin = 2.0*cm

_latinFontS = 18

#reset superFraction to style 'common name' placement
paraparser.superFraction = 0.15
paraparser.sizeDelta = 0


###########################################################################################################
#########################################                   ###############################################
########################################     DB FUNCTIONS     #############################################
#########################################                   ###############################################
###########################################################################################################

def connectToDB():
    try:
        connection = MySQLdb.connect (host = "localhost",
                                user = "root",
                                passwd = "****************",
                                db = "pfaf")
    except MySQLdb.Error, e:
        print "I guess, either you don't have a local copy of the pfaf db"
        print "or something is wrong with your connection details."
        print "Error %d: %s" % (e.args[0], e.args[1])
        sys.exit (1)

    return connection

def close(item, exit=0):
    #used to close both database cursors and connections
    item.close()
    if exit == 1:
        sys.exit (0)

def runQuery(q, conn):
    results = conn.cursor(MySQLdb.cursors.DictCursor)
    results.execute (q)
    return results

def Fetch(results, fetchAll=0):
    if fetchAll:
        print "fetchAll"
        # FETCHALL option:
        rows = results.fetchall()
        #cursor.close()
        #conn.close()
        '''for row in rows:
            print "%s, %s" % (row["Latin Name"], row["Hardyness"])
        print "%d rows were returned" % results.rowcount'''
        return rows
    else:
        # FETCHONE option:
        ##--- Print some debug info to command line ---##
        print "Latin Name  -  Common Name  -  Hardyness"
        while (1):
            row = results.fetchone()
            if row == None:
                break

            latin_name = row["Latin Name"]
            common_name = row["Common name"]
            hardyness = row["Hardyness"]
            family = row["Family"]
            synonyms = row["Synonyms"]

        ##--- Print some more useful debug info to command line ---##
            print "%s  -  %s  -  %s" % (latin_name, common_name, hardyness)
        print row

        if results.rowcount != 1:
            print "%d rows were returned" % results.rowcount
        else:
            print "%d row was returned" % results.rowcount

        return row


###########################################################################################################
#########################################                   ###############################################
########################################  STORY PROCESSING    #############################################
#########################################                   ###############################################
###########################################################################################################

def drawBorders(canv, side):
    canv.saveState()
    d = Drawing(0,0)

    #header border#
    r = Rect( side-_budge, _h-(2.4*cm), _head_w+(_budge*2), 1.2*cm, rx=5, ry=5 )
    r.strokeColor = colors.black
    r.fillColor = colors.white
    r.strokeWidth = 1.5
    d.add(r)

    #hardyness border#
    rad = 5
    hWidth = 1.4*cm
    if side == _left_margin:
        hPos = -rad
    else:
        hPos = _w - hWidth + rad
    r = Rect( hPos, _h-(3.8*cm), hWidth, 1.2*cm, rx=rad, ry=rad )
    r.strokeColor = colors.black
    r.fillColor = colors.white
    r.strokeWidth = 1.5
    d.add(r)

    d.drawOn(canv, 0, 0)
    canv.restoreState()

def drawFooter(canv, doc):
    canv.saveState()
    canv.setFont(stdFont,10)
    canv.drawCentredString((_w/2.0), 1.5*cm, "%d - %s" % (doc.page, doc.latinName))
    canv.restoreState()


class LeftPageTemplate(PageTemplate):
    def __init__(self):
        #allow a bigger margin on the right for binding
        latinF =    Frame(_left_margin, 27.5*cm,  _head_w,  0.8*cm,     id='latinL',    showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        hardyF =    Frame(0.1*cm, 26.05*cm,   cm,  cm,  id='hardL',       showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        synF =      Frame(_left_margin, 26.65*cm,   _main_w,  0.55*cm,  id='synL',       showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        otherF =    Frame(_left_margin, 22.1*cm,   12.4*cm,  4.5*cm,  id='otherL',       showBoundary=1)
        calF =      Frame(14.2*cm, 22.1*cm,   4.5*cm,  4.5*cm,  id='calL',       showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        flowF =     Frame(_left_margin, 2.0*cm,   _main_w,  19.85*cm,  id='flowL',     showBoundary=1)

        PageTemplate.__init__(self,
                              id='left',
                              frames=[latinF, hardyF, synF, otherF, calF, flowF],
                              pagesize=A4)

    def beforeDrawPage(self, canv, doc):
        drawBorders(canv, _left_margin)

    def afterDrawPage(self, canv, doc):
        drawFooter(canv, doc)


class RightPageTemplate(PageTemplate):
    def __init__(self):
        #allow a bigger margin on the left for binding
        latinF =    Frame(_right_margin, 27.5*cm,  _head_w,  0.8*cm,     id='latinR',    showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        hardyF =    Frame(_w-1.1*cm, 26.05*cm,   cm,  cm,  id='hardR',       showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        synF =      Frame(_right_margin+_budge, 26.65*cm,   _main_w,  0.55*cm,  id='synR',       showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        calF =      Frame(_right_margin+_budge, 22.1*cm,   4.5*cm,  4.5*cm,  id='calR',       showBoundary=0,
                          rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
        otherF =    Frame(_right_margin+5.1*cm, 22.1*cm,   12.4*cm,  4.5*cm,  id='otherR',       showBoundary=1)
        flowF =     Frame(_right_margin+_budge, 2.0*cm,   _main_w,  19.85*cm,  id='flowR',     showBoundary=1)

        PageTemplate.__init__(self,
                              id='right',
                              frames=[latinF, hardyF, synF, otherF, calF, flowF],
                              pagesize=A4)

    def beforeDrawPage(self, canv, doc):
        drawBorders(canv, _right_margin)

    def afterDrawPage(self, canv, doc):
        drawFooter(canv, doc)


class MyDocTemplate(BaseDocTemplate):
    _invalidInitArgs = ('pageTemplates',)

    def __init__(self, filename, **kw):
        self.allowSplitting = 0
        BaseDocTemplate.__init__(self, filename, **kw)

        self.latinName = "(none initially)"
        self.latinWidth = 0 #(none initially)

    def afterInit(self):
        self._calc() #in case we have changed margin sizes etc

        self.leftMargin = _left_margin
        self.rightMargin = _right_margin
        self.topMargin = _top_margin
        self.bottomMargin = _bottom_margin
        self.width = _w - self.leftMargin - self.rightMargin
        self.height = _h - self.topMargin - self.bottomMargin

        frameStd = Frame(cm, self.bottomMargin, (_w - 2*cm), (_h - 3*cm), id='cvr', showBoundary=0)
        frameToC = Frame(self.rightMargin, self.bottomMargin, self.width, self.height, id='tocFrame', showBoundary=0)
        frameL = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='secLeftFrame', showBoundary=1)
        frameR = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='secRightFrame', showBoundary=1)

        self.addPageTemplates( [PageTemplate(id='Cover', frames=frameStd, onPage=coverPage, pagesize=self.pagesize),
                                PageTemplate(id='ToC', frames=frameToC, onPage=tocPage, pagesize=self.pagesize),
                                PageTemplate(id='blank', frames=frameStd, onPage=_doNothing, pagesize=self.pagesize),
                                LeftPageTemplate(),
                                RightPageTemplate(),
                                PageTemplate(id='secondary_left', frames=frameL, onPage=_doNothing, pagesize=self.pagesize),
                                PageTemplate(id='secondary_right', frames=frameR, onPage=_doNothing, pagesize=self.pagesize)
                               ] )

    def afterFlowable(self, flowable):
        """Registers ToC entries - and captures latin name for footer"""
        if isinstance(flowable, Paragraph):
            style = flowable.style.name
            key = None
            firstWord = style.split('_',1)[0]
            if (style == 'LatinName') or (style == 'LatinNameR') or (firstWord == 'LatinName'):
                level = 0
                key = 'latin-%s' % self.seq.nextf('LatinName')
                self.canv.bookmarkPage(key)

                wholeStr = flowable.getPlainText()
                if self.page % 2 == 0: #even numbers are on left pages
                    latinOnly = wholeStr.split('\xc2\xa0\xc2\xa0')[0] #looks for '&nbsp&nbsp' as divider
                else:
                    latinOnly = wholeStr.split('\xc2\xa0\xc2\xa0')[1]
                self.latinName = latinOnly
                E = [level, latinOnly, self.page]
                if key is not None: E.append(key)
                self.notify('TOCEntry', tuple(E))
                '''elif (style == 'CommonName'):
                self.commonName = flowable.getPlainText()
                self.commonWidth = stringWidth(self.commonName, styles['common'].fontName, styles['common'].fontSize)'''
            else:
                return


""" coverPage and otherPages are intended for non-flowing (i.e standard) parts of the pages """
def coverPage(canvas, doc):
    Title = "Plants for a Future"
    pageinfo = "The full database collected as a printable book"
    canvas.setTitle(Title + " : " + pageinfo)

    print "creating cover page..."
    canvas.saveState()
    d = Drawing(0,0)

    r = Rect( 0, 0, 12*cm, 4*cm, rx=5, ry=5 )
    r.strokeColor = colors.black
    r.fillColor = colors.white
    r.strokeWidth = 3
    d.add(r)
    d.drawOn(canvas, (_w/2.0)-6*cm, _h-(6.2*cm))

    canvas.setFont(stdFont, 30)
    canvas.drawCentredString(_w/2.0, _h-108, Title)
    canvas.setFont(stdItalic, 14)
    canvas.drawCentredString(_w/2.0, _h-150, pageinfo)
    canvas.restoreState()

def tocPage(canvas, doc):
    canvas.saveState()
    canvas.setFont(stdFont,10)
    canvas.drawCentredString((_w/2.0), 1.5*cm, "Table of Contents")
    canvas.restoreState()


def getMedicinal(plant):
    p = plant
    initial = p["Medicinal"]
    return initial


""" Run after 'Story' has been fully populated """
def go():
    doc = MyDocTemplate('result01.pdf')
    passes = doc.multiBuild(Story)

########################################################################

"""Build StyleSheet"""
styles = buildStyle()

h1 = PS(name = 'HeadingOne',
        fontName = stdFont,
        fontSize = 14,
        leading = 16)

h2 = PS(name = 'HeadingTwo',
        fontName = stdFont,
        fontSize = 12,
        leading = 14,
        leftIndent = 1*cm)

Story=[]

a = Story.append
a(NextPageTemplate('blank'))
a(PageBreak())
a(NextPageTemplate('ToC'))
a(PageBreak())

toc = TableOfContents()
toc.levelStyles = [ h1, h2 ]
a(toc)

a(NextPageTemplate('blank'))
a(PageBreak())


"""###LEFT PAGES SHOULD BE STYLED RIGHT-ALIGNED, AND RIGHT PAGES LEFT-ALIGNED###"""
#print type(plants)
for i, plant in enumerate(plants):
    ### THIS INITIAL CHECK BREAKS AS IT NEEDS TO BE BASED ON PAGE NUMBER, NOT PLANT NUMBER!!! ###
    if i %2 == 0: #IF FIRST PLANT APPEARS ON A RIGHTSIDE PAGE, ELSE REVERSE THE R and L
        page='R'
        template = 'right'
        second = 'left'
    else:
        page='L'
        template ='left'
        second = 'right'

    #FIRST THINGS FIRST:
    #Make sure the page templates flow nicely for each plant "chapter"
    a(NextPageTemplate([template, '*', ('secondary_'+template), ('secondary_'+second) ]))
    a(PageBreak())

    '''CAPTURE PLANT INFO IN OBJECTS'''
    p = plant

    '''for info in plant:
        print info, p[info]'''


    '''Header'''
    latin = p["Latin Name"]
    common = p["Common name"]
    family = p["Family"]
    syn = p["Synonyms"]
    """X. congestum. (Lour.)Merrill.  X. racemosum. Miq.  Apactis japonica.  Croton congestum.
                Flacourtia japonica. Walp.  Hisingera japonica.  H. racemosa."""
    hardy = str(p["Hardyness"])

    '''Basic Info'''
    author = p["Author"]
    botanicalrefs = p["Botanical references"]
    width = p["Width"]
    height = p["Height"]
    habit = p["Habit"]

    planttype = clean("Deciduous/Evergreen", p)

    plantrange = p["Range"]
    habitat = p["Habitat"]
    soil = clean("Soil", plant)
    shade = p["Shade"]
    moisture = p["Moisture"]
    drained = p["Well-drained"]
    nf = p["Nitrogen fixer"]
    pH = p["pH"]
    acid = p["Acid"]
    alkaline = p["Alkaline"]
    saline = p["Saline"]
    wind = p["Wind"]

    rate = clean("Growth rate", plant)
    pollution = p["Pollution"]
    poorsoil = p["Poor soil"]
    drought = p["Drought"]
    heavyclay = p["Heavy clay"]
    tender = clean("FrostTender", plant)

    inleaf = p["In leaf"]
    flowering = p["Flowering time"]
    seedripens = p["Seed ripens"]
    flowertype = p["Flower Type"]
    pollinators = p["Pollinators"]
    selffertile = clean("Self-fertile", plant)

    hazards = p["Known hazards"]

    rating_edible = p["Rating"]
    rating_med = p["Medicinal Rating"]
    edibleuses = p["Edible uses"]
    medicinaluses = getMedicinal(plant)
    otheruses = p["Uses notes"]
    #the following encoding allows for special characters such as degree symbol
    cultivation = unicode(p["Cultivation details"], 'latin-1')#'ISO-8859-1')
    propagation = p["Propagation 1"]

    scented = p["Scented"] #boolean - requires further lookup in `ScentedPlants` table

    string = '''%s is %s %s growing to %gm by %gm at a %s rate.<br/>
                It's habitats are %s <br/><br/> Range: %s
                    <br/><br/>
                Suitable for %s soils. <br/><br/>
                Shade: %s, Moisture: %s <br/>
                Well-drained: %d, Nitrogen fixer: %d <br/> ph: %s <br/>
                Acid: %d, Alkaline: %d, Saline: %d <br/>
                Wind: %s
                    <br/><br/>
                Author: %s <br/> Botanical References: %s''' % (
                    latin, planttype, habit.lower(), width, height, rate,
                    habitat[0].lower()+habitat[1:], plantrange,
                    soil, shade, moisture, drained,
                    nf, pH, acid, alkaline, saline, wind, author, botanicalrefs )
    string = unicode(string, 'latin-1')

    latinW = stringWidth(latin, styles['latin'].fontName, styles['latin'].fontSize)
    commonW = stringWidth(common, styles['common'].fontName, styles['common'].fontSize)

    if (latinW + commonW + (_head_pad*3)) > _head_w:
        styleName = "LatinName_" + str(i)
        latinStyle = PS( name=styleName,
                         parent=styles['Normal'],
                         fontName=titleFont,
                         fontSize=_latinFontS,
                         leading=22,
                         spaceAfter=0)
        j = 1
        #should the latin string be too long, attempt to shrink until it fits
        while (latinW + commonW + (_head_pad*3)) > _head_w:
            #change the font size until ok...
            latinStyle.fontSize = _latinFontS -j
            latinW = stringWidth(latin, latinStyle.fontName, latinStyle.fontSize)
            j += 0.2
    else:
        latinStyle = styles['LatinName']

    if page == 'L':
        headerText = '''<para align="left">
                            %s
                            <font face="%s" size="%d">&nbsp;&nbsp;<super>%s</super></font>
                        </para>''' % (latin, subtitleFont, 12, common)
    else:
        headerText = '''<para align="right">
                            <font face="%s" size="%d"><super>%s</super>&nbsp;&nbsp;</font>
                            %s
                        </para>''' % (subtitleFont, 12, common, latin)
    latinPara = Paragraph(headerText, latinStyle)

    a(FrameBreak('latin'+page))
    a(latinPara)

    a(FrameBreak('syn'+page))
    a(KeepInFrame(_main_w, 1.5*cm,
                  [Paragraph(syn, styles['syn'+page])],
                  mode="shrink")) #can be shrink, truncate or overflow

    a(FrameBreak('hard'+page))
    a(Paragraph(hardy, styles['hardy']))

    a(FrameBreak('cal'+page))
    #SHALL BE ULTIMATELY POPULATED VIA DATABASE#
    greyOut = [ [0,0,1,1,1,1,1,0,0,0,0,0], [0,0,0,0,0,1,1,1,1,0,0,0], [0,0,0,0,0,0,0,0,1,1,1,0] ]

    cal = drawCalendar(greyOut)
    a(cal)

    a(FrameBreak('flow'+page))
    a(Paragraph(string, styles['Normal']))
    a(Paragraph("Edible Uses", styles['title']))
    a(Paragraph("Medicinal Uses", styles['title']))
    a(Paragraph("Other Uses", styles['title']))

    a(Paragraph("Cultivation", styles['title']))
    a(Paragraph(cultivation, styles['Normal']))

    a(Paragraph("Propagation", styles['title']))
    a(Paragraph(propagation, styles['Normal']))


##ASSEMBLE PDF###
go()

最佳答案

如果只是在“左”模板和“右”模板之间切换,则可以尝试使用_handle_nextPageTemplate类的BaseDocTemplate方法。跟踪页码的一种方法是使用afterPage钩子增加页码。

from reportlab.platypus import BaseDocTemplate

class MyDocTemplate(BaseDocTemplate):
    def __init__(self, *args, **kwargs):
        BaseDocTemplate.__init__(self, *args, **kwargs)
        self.__pageNum = 1

    def afterPage(self):
        """Called after all flowables have been drawn on a page"""

        # Increment pageNum since the page has been completed
        self.__pageNum += 1

        # If the page number is even, force "left-side" template
        if self.__pageNum % 2 == 0:
            self._handle_nextPageTemplate('left_template')
        else:
            self._handle_nextPageTemplate('right_template')


我没有测试上面的代码,但是您可能需要使用beforePage,具体取决于它如何检查页面模板顺序。

关于python - ReportLab变量nextPageTemplate的,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14491169/

10-12 23:33