2

私はデータベースをpfaf.org(将来の植物)からpdfブックに変換しているところです。pageTemplatesに関して少しつまずきました。

各植物は、左揃えまたは右揃えのページから開始できます。2ページ以上になる可能性があります。最初の植物ページ(左と右)用に2つのテンプレートがあり、後続の潜在的なページ用にさらに2つのテンプレートがあります。

現在、これは次のように処理されます(たとえば)。

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...

コード(おそらくお分かりのように)は、単一ページのプラントで正常に機能します。また、マルチページプラントで期待どおりに(半)機能します。

ただし、おそらくおわかりのように、2ページ(または4ページなど)の植物はテンプレートの配置を壊します。これは、上記のコードがページ番号ではなく植物番号に基づいてページ位置を想定しているためです。

上記のコードの場所(つまり、Story.appendサイクル中)では、これに対する解決策を見つけることができません-その時点では、植物が複数のページを使用したかどうか、したがって私がどのページであるかを知ることができません現在、そのように。

カスタムdocTemplateからnextPageTemplate構造を微調整できることを期待していましたが、これが可能かどうかはわかりません。

それは...ですか?または別の解決策はありますか?本当に助けていただければ幸いです。ずっと読んでいますが、私が見つけることができる最良の例は、このシナリオを完全にはカバーしていません。

ご不明な点がございましたら、お問い合わせください。ありがとうございました


ありがとう、ニッツル:
問題は、各植物が何ページを占めるかわからないことです。たとえば、新しい植物は奇数ページから始まるので、テンプレートのサイクル('right'、'*'、'secondaryLeft'、'secondaryRight')を与えます。[セカンダリページは、適切なマージンのある1つのフレームです。]

その植物が1ページの長さであれば、問題ありません。次の植物は、上記とは逆のテンプレートサイクルになります。ただし、プラントにたとえば2ページがある場合、次のプラントが再び奇数ページに落ちるため、テンプレートサイクルは変更されないはずです...これが理にかなっていることを願っています。

これは私が解決するのに苦労している状況です...あなたが言うように私がそうするならば、それは複数のページの植物を許しません。私のコードのほとんどは次のとおりです。私はそれを少しスリムにしようとしましたが:)うまくいけば、それはまだすべての関連するものを含み、あまり不必要ではありません。

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

1 に答える 1

3

「左」と「右」のテンプレートを切り替えるだけの場合は、クラスの_handle_nextPageTemplateメソッドを使用してみてください。BaseDocTemplateページ番号を追跡する1つの方法は、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ページテンプレートの順序を確認する方法によっては、代わりに使用する必要がある場合があります。

于 2013-01-25T14:49:46.583 に答える