fohelpers.py: xsl-fo helper functions for pythonshipman/soft/fohelpers/fohelpers.pdf ·...

38
fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract Describes the use and implementation of a module to assist in the direct programmatic construc- tion of PDF les using the XSL Flow Objects protocol. This publication is available in Web form 1 and also as a PDF document 2 . Please forward any comments to [email protected]. Table of Contents 1. Direct construction of PDF les from Python ............................................................................. 3 2. Online les .............................................................................................................................. 3 3. Structure of the generated XSL-FO ............................................................................................ 4 4. Interface to the fohelpers module .......................................................................................... 7 4.1. The FlowTree class ...................................................................................................... 8 4.2. The E(*contents, **attrs) convention .................................................................. 9 4.3. class FoDim: General dimension ................................................................................. 9 4.4. class Box: Dene a rectangle .................................................................................... 10 4.5. class MarginSet: Four margin dimensions ............................................................... 10 4.6. class PageDim: Page layout ...................................................................................... 11 4.7. pageDimFactory(): Some pre-built PageDim instances .............................................. 11 4.8. simpleMaster(): The simple-page-master .......................................................... 12 4.9. regionBody(): Dene the page body area .................................................................... 12 4.10. regionBefore(): Dene the header area .................................................................. 12 4.11. regionAfter(): Dene the footer area ..................................................................... 13 4.12. repeatMaster(): Container for multiple alternative page masters ............................. 13 4.13. conditionalMaster(): One rule about when to use a page master ........................... 13 4.14. pageSequence(): Build the page-sequence element .............................................. 14 4.15. staticContent(): Add static content to a simple page master .................................. 14 4.16. flow(): Add a flow element ..................................................................................... 15 4.17. block(): Generate a block element ............................................................................ 15 4.18. inline(): Generate an inline element ........................................................................ 15 4.19. leader(): Generate a leader ..................................................................................... 15 1 http://www.nmt.edu/~shipman/soft/fohelpers/ 2 http://www.nmt.edu/~shipman/soft/fohelpers/fohelpers.pdf 1 fohelpers.py: XSL-FO helper functions Zoological Data Processing

Upload: hoangkhuong

Post on 19-Feb-2018

226 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

fohelpers.py: XSL-FOhelper functions for Python

John W. Shipman2013-08-29 20:13

AbstractDescribes the use and implementation of a module to assist in the direct programmatic construc-tion of PDF files using the XSL Flow Objects protocol.

This publication is available in Web form1

and also as a PDF document2. Please forward any

comments to [email protected].

Table of Contents1. Direct construction of PDF files from Python ............................................................................. 32. Online files .............................................................................................................................. 33. Structure of the generated XSL-FO ............................................................................................ 44. Interface to the fohelpers module .......................................................................................... 7

4.1. The FlowTree class ...................................................................................................... 84.2. The E(*contents, **attrs) convention .................................................................. 94.3. class FoDim: General dimension ................................................................................. 94.4. class Box: Define a rectangle .................................................................................... 104.5. class MarginSet: Four margin dimensions ............................................................... 104.6. class PageDim: Page layout ...................................................................................... 114.7. pageDimFactory(): Some pre-built PageDim instances .............................................. 114.8. simpleMaster(): The simple-page-master .......................................................... 124.9. regionBody(): Define the page body area .................................................................... 124.10. regionBefore(): Define the header area .................................................................. 124.11. regionAfter(): Define the footer area ..................................................................... 134.12. repeatMaster(): Container for multiple alternative page masters ............................. 134.13. conditionalMaster(): One rule about when to use a page master ........................... 134.14. pageSequence(): Build the page-sequence element .............................................. 144.15. staticContent(): Add static content to a simple page master .................................. 144.16. flow(): Add a flow element ..................................................................................... 154.17. block(): Generate a block element ............................................................................ 154.18. inline(): Generate an inline element ........................................................................ 154.19. leader(): Generate a leader ..................................................................................... 15

1http://www.nmt.edu/~shipman/soft/fohelpers/2http://www.nmt.edu/~shipman/soft/fohelpers/fohelpers.pdf

1fohelpers.py: XSL-FO helper functionsZoological Data Processing

About this document
This document has been generated with RenderX XEP. Visit http://www.renderx.com/ to learn more about RenderX family of software solutions for digital typography.
Page 2: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

4.20. font(): Dictionary of font properties ......................................................................... 154.21. dash(): Attribute set builder ..................................................................................... 164.22. deCamel(): Convert CamelCase names to dashed names ............................................ 16

5. fohelpers.py: Prologue ...................................................................................................... 176. Module imports ..................................................................................................................... 177. Manifest constants .................................................................................................................. 17

7.1. FO_NAMESPACE ........................................................................................................... 177.2. Dimensional units ........................................................................................................ 187.3. Standard page sizes ..................................................................................................... 18

8. class FlowTree: The entire document ................................................................................. 188.1. FlowTree.__init__(): Constructor ......................................................................... 188.2. FlowTree.write(): Serialize the document ............................................................... 19

9. class FoDim: Dimensional arithmetic ................................................................................... 199.1. FoDim.__init__(): Constructor ............................................................................... 209.2. FoDim.__str__() ..................................................................................................... 209.3. FoDim.__add__(): Add two dimensions .................................................................... 219.4. FoDim.__sub__(): Subtract dimensions ..................................................................... 219.5. FoDim.__mul__(): Multiply by a constant .................................................................. 219.6. FoDim.__div__(): Divide by a constant ..................................................................... 219.7. FoDim.__neg__(): Unary minus ............................................................................... 229.8. FoDim.convert(): Change units ............................................................................... 229.9. FoDim.confactor(): Find the conversion factor between two units ............................ 22

10. class Box: Rectangle object ................................................................................................ 2310.1. Box.__init__() ..................................................................................................... 2410.2. Box.__str__() ....................................................................................................... 24

11. class MarginSet: Four margin sizes ................................................................................. 2411.1. MarginSet.__init__(): Constructor ...................................................................... 2511.2. MarginSet.dict(): Return a dictionary of margin attributes .................................... 2511.3. MarginSet.__str__() ........................................................................................... 25

12. class PageDim: Page layout .............................................................................................. 2512.1. PageDim.__init__> ............................................................................................... 2612.2. PageDim.__str__() ............................................................................................... 26

13. pageDimFactory: Create a standard PageDim instance ........................................................ 2714. simpleMaster: Generate a simple-page-master ............................................................. 2815. regionBody(): Build a region-body element .................................................................... 2916. regionBefore(): Build a region-before element ............................................................ 2917. regionAfter(): Build a region-after element ................................................................ 2918. repeatMaster: Construct a repeatable page master .............................................................. 3019. conditionalMaster(): Generate a conditional master reference ......................................... 3020. pageSequence(): Generate a page-sequence element ...................................................... 3121. staticContent(): Generate a static-content element .................................................. 3122. flow(): Create a flow element ............................................................................................ 3123. block(): Generate a block element ..................................................................................... 3124. inline(): Generate an inline element ............................................................................... 3225. leader(): Produce a leader element .................................................................................... 3226. font(): Dictionary of font properties .................................................................................... 3227. dash(): An attribute dictionary generator ............................................................................. 3328. deCamel(): Convert Python names to XSL-FO names ........................................................... 3329. fohelptest: Test driver for fohelpers .............................................................................. 3430. confactest: Test unit conversions ...................................................................................... 36

Zoological Data Processingfohelpers.py: XSL-FO helper functions2

Page 3: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

1. Direct construction of PDF files from PythonThe Adobe PDF (Portable Document Format) standard

3is a ubiquitous documentation format. The

purpose of the fohelpers Python module is to allow Python programs to generate PDF files directly.

There are many routes to PDF, but the author has some experience with the XSL-FO standard4, the eX-

tensible Stylesheet Language Formatting Objects.

Before you read any further, you should be familiar with these technologies.

• The Python programming language5.

• XML, the ubiquitous eXtended Markup Language.

• The document Python XML processing with lxml6

is a complete, easy-to-use XML toolbase for Python.In particular, the etbuildermodule defined in this document makes the code to generate any XMLstructure straightforward and terse.

• XSL Formatting Objects (XSL-FO). The term “XSL” is somewhat ambiguous because what was origin-ally one project has fissioned into two.

XSL-FO is a way of describing specific documents. We use it here as a convenient route to PDF format.

The other XSL project, XSLT (eXtended Stylesheet Language, Transforms) is an elegant, pure func-tional programming language for transforming XML documents into other forms. The vast majorityof current practitioners of XSL-FO use XSLT to generate it, but it is outside the scope of this document.

The current document is intended to provide an alternative route to PDF that does not use XSLT, becauseXSLT operates only on XML input files. We want to provide a route for situations where the contentcomes from databases or other sources, to which XSLT has no direct interface.

For a readable, well-organized, approachable book on XSL-FO, the author recommends this one.

Pawson, Dave. XSL-FO: Making XML Look Good in Print. O'Reilly, 2002, ISBN 0-596-00355-2.

The translation of an XSL-FO document into the final PDF can be accomplished with several packages;refer to the Wikipedia page for XSL-FO

7for links. The NM Tech Computer Center has the XEP package

from RenderX8

installed under an academic license; this package produces high-quality output.

2. Online filesThese files are available online.

• fohelpers.py9: The module defined in this document.

• fohelptest10

: Test driver; see Section 29, “fohelptest: Test driver for fohelpers” (p. 34).• fohelptest.pdf

11: PDF produced by fohelptest.

• confactest12

: Test script for unit conversions in the FoDim class; see Section 30, “confactest:Test unit conversions” (p. 36).

3http://en.wikipedia.org/wiki/PDF4http://www.w3.org/TR/xsl/5http://www.python.org/6http://www.nmt.edu/tcc/help/pubs/pylxml7http://en.wikipedia.org/wiki/XSL-FO8http://www.renderx.com/9http://www.nmt.edu/~shipman/soft/fohelpers/fohelpers.py10http://www.nmt.edu/~shipman/soft/fohelpers/fohelptest11http://www.nmt.edu/~shipman/soft/fohelpers/fohelptest.pdf12http://www.nmt.edu/~shipman/soft/fohelpers/confactest

3fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 4: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

• confactest.out13

: Output of the confactest script.• fohelpers.xml

14: The DocBook source for this document.

3. Structure of the generated XSL-FOThe full XSL specification

15is truly a monster. The author once read the entire standard and handwrote

57 pages of notes on green engineering pads, five lines per inch.

As with most large packages, you probably won't need 80% of what is there. Much of the complexitycomes in when you are supporting multiple languages in the same document. The general problemXSL-FO solves is quite large. Consider the problems of formatting a page that includes English (wherelines are written left to right, and the lines progress from top to bottom) and Japanese (each line iswritten top to bottom and lines progress right to left). This leads to the important concepts of InlineProgression Direction (IPD), which is “lr” (left-to-right) for English and “tb” (top-to-bottom) for Japanese;and Block Progression Direction (BPD), “tb” for English and “rl” for Japanese. In the balance of thisdocument we will assume English (lr-tb) writing order.

Keep in mind also that the purpose of XSL-FO is to format documents within specific page sizes, whichis basic to the PDF format. Web rendering is a completely different problem: for example, a Web pagecan use any width, although most people find horizontal scrolling quite annoying, and there are lots ofpretty tiny screens out there.

The author's preferred tool for understanding the structure of XML document types like XSL-FO is theRelax NG schema language, especially the RNC form (Relax NG Compact Format). See the NM TechComputer Center's publication, Relax NG Compact Syntax (RNC)

16. RenderX

17, maker of XEP, our locally

preferred XSL-FO-to-PDF translator, has kindly made available a complete RNC schema for XSL-FO18

.

When you are generating an XSL-FO file, the first problem is understanding the overall structure of thefile. Here, in RNC format, is a highly reduced schema for the top-level elements of the document. Thisschema shows only a tiny part of the elements and attributes in the full schema; it is intended strictlyto familiarize you with the main pieces of the document.

The root element of the tree is named root. It has two kinds of children. The layout-master-setchild defines all the different ways that pages can be formatted. This is followed by one or more page-sequence elements describing the actual content that must be formatted into those page layouts.

start = rootroot = element root { layout-master-set, page-sequence+ }

The layout-master-set element must have at least one simple-page-master child; each onedescribes a specific page layout.

In most cases you will also provide a page-sequence-master element that specify what kind ofcontent is routed to each simple-page-master's layout.

layout-master-set = element layout-master-set{ simple-page-master+ & page-sequence-master? }

Simple page masters divide the page up into five regions:

13http://www.nmt.edu/~shipman/soft/fohelpers/confactest.out14http://www.nmt.edu/~shipman/soft/fohelpers/fohelpers.xml15xslStandardUrl16http://www.nmt.edu/~shipman/soft/rnc/17http://www.renderx.com/18http://www.renderx.com/tools/validators.html

Zoological Data Processingfohelpers.py: XSL-FO helper functions4

Page 5: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

The terminology here is relative to XSL-FO's generic directions: “before” and “after” refer to the BPD,and “start” and “end” are relative to the IPD.

Typically the “before” region is used for running headers; “after”, for running footers; and the contentappears in the “body” region. Unless you are using the “start” and “end” regions for marginal notes,typically they will have zero width, and the body will have the same width as the headers and footers.

Each simple-page-master has a unique name that is defined by its master-name attribute. Wewon't describe the five child elements here, but basically the region-body child describes the size ofthe text area and the margins around it; the region-before and region-after children describethe heights of the header and footer; and the region-start and region-end elements describe thewidths of the marginal note columns.

Each of these five child elements has a region-name attribute that defines its name. The static-content element (described below) will refer to this name when it provides the content for a region(other than the body region).

simple-page-master = element simple-page-master{ attribute master-name{text},region-body, region-before?, region-after?, region-start?,region-end? }

The page-sequence-master element is a container for the rules that dictate how text is assigned tothe different simple page masters.

page-sequence-master = element page-sequence-master{ (single-page-master-reference |

repeatable-page-master-reference |repeatable-page-master-alternatives)+ }

• Use a single-page-master-reference for content that consists of exactly one page, such as atitle page.

• Use a repeatable-page-master-reference for multiple pages of output that all use the samesimple page master.

• Use a repeatable-page-master-reference element if you want to use different simple pagemasters in different cases (e.g., even or odd pages, first page, etc.).

In the first two of the above cases, the element has a master-reference attribute that points to thesimple page master that has that master-name.

5fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 6: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

single-page-master-reference = element single-page-master-reference{ attribute master-reference{text}, empty }

repeatable-page-master-reference =element repeatable-page-master-reference{ attribute master-reference{text}, empty }

In the general case, where you have multiple simple page masters, use arepeatable-page-master-alternatives element. This element in turn is a container for conditional-page-master-ref-erence elements, each of which describes one situation and names the simple page master to be usedin that situation.

repeatable-page-master-alternatives =element repeatable-page-master-alternatives{ conditional-page-master-reference+ }

conditional-page-master-reference =element conditional-page-master-reference{ attribute master-reference{text},attribute page-position { "first" | "last" | "rest" | "any" }?,attribute odd-or-even { "odd" | "even" | "any" }?,attribute blank-or-not-blank { "blank" | "not-blank" | "any" }?,empty }

Within a conditional-page-master-reference:

• The master-reference attribute names the simple page master to be used when these conditionsare true.

• If there is a page-position attribute, it selects whether this page master should be used only forthe first page, only for the last page, or in the rest of the pages.

• If there is an odd-or-even attribute, it selects whether this page master is used for odd-numbered(recto) pages, even-numbered (verso) pages, or both.

• If there is a blank-or-not-blank attribute, it selects whether the master applies only to pages withno content, only to pages that do have any content, or both.

Here's an example that uses three simple masters:first for the first page,odd oreven for the remainingpages.

<layout-master-set><simple-page-master master-name="first">...</simple-page-master><simple-page-master master-name="odd">...</simple-page-master><simple-page-master master-name="even">...</simple-page-master><page-sequence-master master-name="N"><repeatable-page-master-alternatives><conditional-page-master-reference master-reference="first"

page-position="first" /><conditional-page-master-reference master-reference="odd"

page-position="rest" odd-or-even="odd" /><conditional-page-master-reference master-reference="even"

page-position="rest" odd-or-even="even" /></repeatable-page-master-alternatives>

</page-sequence-master>

Return to our schema, that completes the layout-master-set part of the document. The rest is inthe other child of the root element, the page-sequence element.

Zoological Data Processingfohelpers.py: XSL-FO helper functions6

Page 7: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

page-sequence = element page-sequence{ static-content*, flow }

Use a static-content element to specify the content of the non-body regions of the page such asheaders and footers.

static-content = element static-content{ attribute flow-name{text},block+ }

In the static-content element, use the flow-name attribute to specify where this static contentgoes. The value of this attribute is the region name you specified with the region-name attribute inthe element that defined the format of that region. Typically you will supply one block element thatdefines the content to be displayed in that region. This content can include the page-number element,which will display the page number.

For example, suppose you have a running head, and in the simple-page-master for odd-numberedpages, your region-before element had an attribute region-name="odd-head". To specify thecontent of the header, use a static-content element with a flow-name="odd-head" attribute.

The last element we'll discuss in this foreshortened grammar is the flow element.

flow = element flow {block+}

This element is the container for all the principal content of the document (excluding static content).

The content “block+” shown above is not technically correct. You may specify page body content usingany number of block-level elements, including not only the actual block element but other elementssuch as tables, lists, or graphics.

4. Interface to the fohelpersmoduleModule fohelpers.py contains various classes to assist in the creation of XSL-FO documents.

Because an XSL-FO file is an XML document, we will use the etbuilder module described in PythonXML processing with lxml

19, which is well-suited for building an XML document from scratch.

Because most of the code revolves around building the XML file, a number of the functions providedby the current package take as arguments XML element nodes represented by instances of theet.Element class from the etbuilder module (which in other Python-XML interfaces are instancesof class etree.Element). In most cases these arguments can be generated by the E() universal node-builder object from that module.

Here is the general procedure for creating a document as an XSL-FO file.

1. To create the root output XSL-FO document, use the constructor for Section 8, “class FlowTree:The entire document” (p. 18). As built, this class holds theet.ElementTree instance representingthe document, with the proper root element (whose name is root, strangely enough), and its re-quired layout-master-set child node attached. These nodes are exported as the .root and.masters attribute, respectively.

2. To the .masters node, add child nodes describing your various page layouts and when to usewhich one.

19http://www.nmt.edu/tcc/help/pubs/pylxml

7fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 8: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

• To add the one or more simple-page-master nodes describing your page layouts, use Sec-tion 14, “simpleMaster: Generate a simple-page-master” (p. 28). To define these regions,see Section 4.9, “regionBody(): Define the page body area” (p. 12); Section 4.10, “regionBe-fore(): Define the header area” (p. 12); and Section 4.11, “regionAfter(): Define the footerarea” (p. 13).

This package does not currently support the marginal notes areas in the “start” and “end” regions.• Also as child nodes of that .masters attribute, append the page-sequence-master element

by calling Section 18, “repeatMaster: Construct a repeatable page master” (p. 30) and passingthat the results of one or more repeatable-page-master-alternatives nodes constructedby Section 19, “conditionalMaster(): Generate a conditional master reference” (p. 30).

3. To the .root node in the FlowTree instance, add one or more page-sequence subtrees repres-enting the actual content of your document, using Section 20, “pageSequence(): Generate apage-sequence element” (p. 31).

4. To that page-sequence subtree, add static content (e.g., running heads) using Section 21,“staticContent(): Generate a static-content element” (p. 31).

5. Also the page-sequence subtree, add a flow element using Section 22, “flow(): Create a flowelement” (p. 31).

6. Add your content as children of the flow element. To add a block element, use Section 23,“block(): Generate a block element” (p. 31). To add inline markup inside a block element, seeSection 24, “inline(): Generate an inline element” (p. 32).

You may find all this clearer if you review pp. 30–63 of the Pawson book referenced in Section 1, “Directconstruction of PDF files from Python” (p. 3). You may also get the general idea by examining oursmall test example, Section 29, “fohelptest: Test driver for fohelpers” (p. 34).

In addition, there are several other classes and utility functions that you will likely need.

• Section 4.3, “class FoDim: General dimension” (p. 9).• Section 4.4, “class Box: Define a rectangle” (p. 10).• Section 4.5, “class MarginSet: Four margin dimensions” (p. 10).• Section 4.6, “class PageDim: Page layout” (p. 11).• Section 4.7, “pageDimFactory(): Some pre-built PageDim instances” (p. 11).• Section 4.20, “font(): Dictionary of font properties” (p. 15).• Section 4.19, “leader(): Generate a leader” (p. 15).

4.1. The FlowTree classThis constructor creates an empty XSL-FO document:

FlowTree()

There are no optional arguments. The instance has these exported attributes:

.rootThe root node of the document as an et.Element instance, where et is the lxml version of thestandard Python ElementTree XML interface.

.mastersThe layout-master-set node that contains the descriptions of the various page layouts andrules governing when to use them.

Zoological Data Processingfohelpers.py: XSL-FO helper functions8

Page 9: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

.docThe entire document as an et.ElementTree instance.

The FlowTree class has one method:

.write(outFile)Serialize the document as XML and write it to the given outFile.

4.2. The E(*contents, **attrs) conventionSeveral of these helper functions, such as body(), generate elements that can have arbitrary content.The general form of this function looks like this:

body(*contents, **attrs)

The arguments may be any of the values that can be passed to the E() factory function from the et-builder package:

• Positional arguments of type str are added as text content. (Due to the slightly screwy way thatElementTree represents XML, if the node being built has no child elements, the text becomes, or isappended to, the node's .text attribute; if the node has children, the text becomes or is appendedto the .tail attribute of the last child.)

• Positional arguments of type int are converted to str and then added as text content.• Positional arguments of type dict, or keyword arguments, become XML attributes of the node being

built.• Positional arguments that are XML elements (instances of et.Element) become child nodes of the

node being built.

4.3. class FoDim: General dimensionUse an instance of this class to represent any width or height or other dimension. The class is aware ofthe various different units that are allowed in XSL-FO dimension attributes.

The class supports a few of the common arithmetic operations that one typically needs in computingvarious document dimensions.

Dimensions are stored internally using values of Python's Decimal (fixed-point) type. This type's fixed-point arithmetic avoids many of the common pathologies of the float type. For documentation onthis type, see the The Python Standard Library

20.

The constructor has this calling sequence:

FoDim(number, units)

The number may be any type that is acceptable to the decimal.Decimal() constructor.

The units argument may take any of these values:

MeaningValueConstantInches."in"UNITS_IN

Millimeters."mm"UNITS_MM

Centimeters."cm"UNITS_CM

20http://docs.python.org/library/decimal.html

9fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 10: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

MeaningValueConstantPicas (12 points)."pc"UNITS_PC

Points (1/72.27″)."pt"UNITS_PT

Here are the methods defined on an instance of class FoDim.

.__str__(self)Return self as a string suitable for use as an XSL-FO dimension attribute.

.__add__(self, other)Assuming other is another FoDim instance, returns their sum. If the units are different, the resultwill take into account standard unit conversions. For example:

>>> import fohelpers as h>>> print h.FoDim(3, h.UNITS_MM) + h.FoDim(6, h.UNITS_CM)63mm

.__sub__(self, other)For other another FoDim instance, returns their difference as a FoDim instance.

.__mul__(self, other)Multiplies by a constant: other is any type acceptable to the decimal.Decimal() constructor.The result is a FoDim.

>>> import decimal as d>>> print h.FoDim('2.5', h.UNITS_IN) * "1.5"3.75in

.__div__(self, other)Divides by a constant; other is any type acceptable to the decimal.Decimal() constructor.

.__neg__(self)Returns self with the opposite sign.

.convert(newUnits)Returns a FoDim instance converted to newUnits, which takes the same values as the units argu-ment of the FoDim constructor.

4.4. class Box: Define a rectangleAn instance of this class represents the dimensions of a rectangular area. The constructor:

Box(wide, high)

The arguments are the width and the height, respectively, as instances of class FoDim. The resultinginstance has two attributes, .wide and .high, representing the arguments passed to the constructor.

4.5. class MarginSet: Four margin dimensionsThe purpose of this class is to represent the four margin sizes of a page master. The constructor:

MarginSet(top=T, bot=B, left=L, right=R)

Zoological Data Processingfohelpers.py: XSL-FO helper functions10

Page 11: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

Each argument defines the width or height of one margin. Each argument defaults to zero. The valuessupplied for each argument may be either an instance of class FoDim or a string that is acceptableas an XSL-FO dimension, e.g., "3.6pc".

Instances of MarginSet have these attributes and methods:

.topThe top margin height.

.botThe bottom margin height.

.leftThe left margin width.

.rightThe right margin width.

.dict()Return the instance's dimensions as a dictionary with keysmargin-top,margin-bottom,margin-left, and margin-right, suitable for describing the margin properties of several kinds of areasthat share the “box properties” group.

4.6. class PageDim: Page layoutUse an instance of this class to describe the overall dimensions of a page, as in a simple-page-master.Here is the constructor:

PageDim(pageBox, pageMargins=None, frameMargins=None,bodyMargins=None)

An instance of class Box that defines the paper size.pageBox

An instance of class MarginSet that describes the desired margins around theentire page. The default is zero margins all around.

pageMargins

Another instance of class MarginSet used to describe the heights of the pageheader (in the .top attribute) and the page footer (in .bot). The values of the .leftand .right attributes of the argument are ignored. The default value is both zero.

frameMargins

An instance of class MarginSet. The height between the header and the bodyis taken from its .top attribute, and the height between the body and footer is takenfrom its .bot attribute. The default is that both are zero.

bodyMargins

The arguments to the constructor are available as instance attributes .pageBox, .pageMargins,.frameMargins, and .bodyMargins.

A few prefabricated PageDim instances are available; see Section 4.7, “pageDimFactory(): Somepre-built PageDim instances” (p. 11).

4.7. pageDimFactory(): Some pre-built PageDim instancesUse the pageDimFactory() function to build a new instance of class PageDim in one of a fewstandard sizes. Here is the calling sequence:

pageDimFactory(paperType)

11fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 12: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

The paperType argument may be any of these values:

Letter paper in portrait orientation, 8.5″×11″.PAPER_LETTER

Letter paper in landscape orientation, 11″×8.5″.PAPER_LETTER_LAND

Letter paper, 8.5″×14″.PAPER_LEGAL

A standard file card 3″×5″ in portrait orientation.PAPER_3X5

4.8. simpleMaster(): The simple-page-masterEach call to this function generates the XML subtree that defines a simple-page-master element.The resulting subtree is added to the .masters attribute of the FlowTree instance. The function pro-totype:

simpleMaster(masterName, pageDim, *contents, **attrs)

masterNameName of the master you are defining; this becomes the master-name attribute.

pageDimA PageDim instance describing the desired page size (from pageDim.pageBox) and the page'souter margins (from pageDim.pageMargins).

The masterName argument is the name of the master you are defining. It becomes the master-nameattribute. The positional arguments in contents and the keyword arguments in attrs are added tothe simple-page-master node, using the convention described in Section 4.2, “The E(*contents,**attrs) convention” (p. 9).

In general, to supply the attributes of the simple-page-master node you are building, use keywordarguments, or supply dictionaries as positional arguments.

The nodes you supply as positional arguments will be nodes defining the dimensions of the fivestandard parts of the page. These must be supplied in a fixed order; see these helper functions, in order:

• Section 4.9, “regionBody(): Define the page body area” (p. 12) (required).• Section 4.10, “regionBefore(): Define the header area” (p. 12) (optional).• Section 4.11, “regionAfter(): Define the footer area” (p. 13) (optional).

4.9. regionBody(): Define the page body areaThis function returns aregion-body element that must be the first child of thesimple-page-master.The calling sequence:

regionBody(pageDim, *contents, **attrs)

The sole argument is a PageDim instance that defines the page layout. The pageDim.bodyMarginsattribute will define the margins of the page body. Additional attributes can be passed in using Section 4.2,“The E(*contents, **attrs) convention” (p. 9).

4.10. regionBefore(): Define the header areaThis function returns a region-before node that is a child of the simple-page-master; it describesthe layout of the page header. Its calling sequence:

Zoological Data Processingfohelpers.py: XSL-FO helper functions12

Page 13: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

regionBefore(pageDim, regName)

The pageDim argument is a PageDim instance that defines the page layout. The pa-geDim.frameMargins.top value will be used to define the extent (height) of the header.

Use theregName argument to assign a name to this region. The name will be used bystaticContent()to route the static content to the header of the correct simple page master.

4.11. regionAfter(): Define the footer areaThis function returns a region-after node, a child of simple-page-master that describes thelayout of the page footer. The calling sequence:

regionAfter(pageDim, regName)

The pageDim argument is a PageDim instance that defines the page layout. The pa-geDim.frameMargins.bot value will be used to define the extent (height) of the footer.

4.12. repeatMaster(): Container for multiple alternative page mastersMost layouts will supply multiple page masters, each of which is used in a different situation. Mostcommonly this is used for even- and odd-page layouts for double-sided printing.

This function returns a repeatable-page-master-alternatives node that is added as a child ofthe page-sequence-master node available as the .masters attribute of the FlowTree instance.Its calling sequence:

repeatMaster(masterName, *contents)

The masterName argument is a string you use to give a name to this set of alternatives. That name willpassed to the pageSequence() function to designate that this set of alternatives is to be used withthat page sequence.

The remaining positional arguments will be the nodes returned by conditionalMaster calls, onefor each alternative page layout in this master set.

4.13. conditionalMaster(): One rule about when to use a page masterThis function builds a conditional-page-master-reference node that describes one of the rulesfor selecting page masters for a given page sequence. Here is the function prototype:

conditionalMaster(masterRef, pagePos=None, oddEven=None,blankNon=None)

Definitions of these arguments:

masterRefName of the simple page master to be used in this case.

pagePosSpecifies on which page or pages of a sequence this simple page master is to be used. Values maybe any of:

Only on the first page of the sequence.first

13fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 14: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

Only on the last page of the sequence.last

On pages that are neither the first nor the last of the sequence.rest

On all the pages of the sequence.any

oddEvenSpecifies when this simple page master is used with respect to whether it is a recto (odd-numbered)or verso (even-numbered) page.

Only on the recto pages.odd

Only on the verso pages.even

On recto or verso pages.any

blankNonSpecifies whether this simple page master is to be used with respect to whether there is any contenton the page, or whether it is blank.

Only on blank pages.blank

Only on nonblank pages.not-blank

On blank or nonblank pages.any

4.14. pageSequence(): Build the page-sequence elementThis function returns a page-sequence node, which is a container for textual content (as opposed toformatting specifications). Here is the calling sequence:

pageSequence(masterRef)

The masterRef argument is either the name of a simple-page-master to be used for all the pagesin this sequence, or the name of a repeatable-page-master-alternatives node as built by therepeatMaster() function.

The resulting page-sequence node must be appended to the root element (available as the .rootattribute of the FlowTree instance), after the layout-master-set node created by the FlowTree()constructor.

4.15. staticContent(): Add static content to a simple page masterOnce a page-sequence node has been added to the document by the pageSequence() function,use the staticContent() function to add one or more child static-content nodes as children ofthe page-sequence node. Each static-content child defines content that appears in the headeror footer of pages in that sequence. Here is the prototype:

staticContent(flowName, *contents, **attrs)

The flowName specifies where this static content goes. This is the same name you supplied to the re-gionBefore() or regionAfter() function when you built the corresponding simple page master.

The remaining positional and keyword arguments are added to the static-content node accordingto the convention discussed in Section 4.2, “The E(*contents, **attrs) convention” (p. 9).

Zoological Data Processingfohelpers.py: XSL-FO helper functions14

Page 15: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

4.16. flow(): Add a flow elementThis function adds the flow element that is the parent of all the main content of the document.

flow(pageSequence)

The pageSequence argument is the node returned by Section 4.14, “pageSequence(): Build thepage-sequence element” (p. 14). This function adds a new child flownode for region xsl-region-body; it returns the flow node.

4.17. block(): Generate a block elementThis function returns ablock element, a generic block container. The items inside it are stacked vertically.The calling sequence:

block(*contents, **attrs)

For a discussion of the positional and keyword arguments, see Section 4.2, “The E(*contents,**attrs) convention” (p. 9).

4.18. inline(): Generate an inline elementThis function return aninline element with arbitrary content. The calling sequence obeys the conventiondescribed in Section 4.2, “The E(*contents, **attrs) convention” (p. 9).

inline(*contents, **attrs)

4.19. leader(): Generate a leaderIn historical typesetting parlance, a leader is any content that fills an arbitrary area. The most commonoccurrence of leaders is the row of dots separating section titles from page numbers in a table of contents.

In XSL-FO, a leader fills either a fixed-size area or an arbitrary area. The area may be blank, or it maybe filled with any of a set of repeating glyphs. The leader() function returns a leader element, andhas this calling sequence.

leader(length=None, pattern=None)

The length can be any dimension, either as a fixed string or as an instance of class FoDim; the defaultis to produce a leader that fills all the space left over in its parent element. There are quite a number ofvalues for the pattern argument; refer to Pawson's book, pp. 118–119, for details.

4.20. font(): Dictionary of font propertiesTheetbuildermodule makes it convenient to use keyword arguments to add XML attributes. However,this works only when the attribute names are valid Python identifiers. Unfortunately large numbers ofXSL-FO attribute names have hyphens in them, e.g., font-size and the other font properties.

The purpose of the font() function, then, is to allow you to create a dictionary of standard XSL-FOfont properties using keyword arguments. Here is the calling sequence:

font(fontFamily=None, fontSize=None, fontStyle=None,fontWeight=None, fontVariant=None, fontStretch=None)

15fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 16: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

The result is a dictionary. Each of the keyword arguments, if you supply it, becomes a key-value pairin this dictionary, except the key is converted to the corresponding font-related attribute. For example,an argument “fontFamily='Helvetica'”, in the result, becomes the key-value pair font-fam-ily='Helvetica'. The default for each of the arguments is not to have that key-value pair in theresulting dictionary. Here's an example.

>>> h.font(fontVariant='small-caps', fontStyle='italic',... fontSize='11pt', fontFamily='monospace'){'font-size': '11pt', 'font-family': 'monospace', 'font-style':'italic', 'font-variant': 'small-caps'}

4.21. dash(): Attribute set builderOne of the conveniences of the etbuilder module is that keyword arguments to the E() universalelement constructor become attributes of the element. So, for example, E("hr", id='c41') givesyou the element <hr id='cp1'/>.

Unfortunately, that rarely helps with XSL-FO names, most of which are multiple hyphenated words,and hyphens are an operator in Python.

So the purpose of the dash() function is to build dictionaries of XSL-FO attributes that can be passedto the E() constructor using keyword arguments that are valid Python names that map to valid XSL-FO names.

The trick is to use “camelCase” names on the Python side. This function detects each transition from alowercase character x to an uppercase character Y, and replaces that with the sequence “x-y”.

Here is the function prototype:

dash(**kw)

The result is a dictionary with the same elements as kw, but with each “camelCase” transition convertedto XSL-FO's multi-word layout. Here's an example:

>>> import fohelpers as h>>> h.dash(marginTop='2pc', marginBottom='1pc'){'margin-top': '2pc', 'margin-bottom': '1pc'}

4.22. deCamel(): Convert CamelCase names to dashed namesThis function is used by Section 4.21, “dash(): Attribute set builder” (p. 16) to transform each keyname, but it may have standalone applications in XSL-FO work. The prototype:

deCamel(name)

The name argument is a string. The function returns name but with every lowercase-to-uppercasetransition "xY" replaced by "x-y".

>>> import fohelpers as h>>> h.deCamel('simplePageMaster')'simple-page-master'

Zoological Data Processingfohelpers.py: XSL-FO helper functions16

Page 17: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

5. fohelpers.py: PrologueThe balance of this document contains the code for the fohelpers.py module in literate form. Formore information, see the author's lightweight literate programming (LLP)

21page.

Here is the module prologue. The interface is described using Cleanroom intended functions22

.fohelpers.py

'''fohelpers.py: XSL-FO helper functions

Do not edit this file directly. It is extracted automaticallyfrom the documentation:http://www.nmt.edu/~shipman/soft/fohelpers/

'''

6. Module importsfohelpers.py

# - - - - - I m p o r t s

We need the usual sys module for access to the standard streams.fohelpers.py

import sys

The etbuilder.py module is used to construct the actual XML, and comes from comes from PythonXML processing with lxml

23.

fohelpers.py

from etbuilder import et, E, subElement

Python's Decimal module handles dimensional arithmetic within the FoDim class. The second linecalls this module's getcontext() function to limit the precision of decimal values to six significantdigits.

fohelpers.py

from decimal import Decimal, getcontextgetcontext().prec = 6

7. Manifest constantsConstant names are capitalized.

fohelpers.py

# - - - - - M a n i f e s t c o n s t a n t s

7.1. FO_NAMESPACEName of the XSL-FO namespace.

21http://www.nmt.edu/~shipman/soft/litprog/22http://www.nmt.edu/~shipman/soft/clean/23http://www.nmt.edu/~shipman/soft/pylxml/

17fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 18: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

fohelpers.py

FO_NAMESPACE = "http://www.w3.org/1999/XSL/Format"

7.2. Dimensional unitsThese symbolic names enumerate all the kinds of dimensions acceptable in XSL-FO: millimeters, centi-meters, inches, picas, and points.

fohelpers.py

UNITS_MM = "mm"UNITS_CM = "cm"UNITS_IN = "in"UNITS_PC = "pc"UNITS_PT = "pt"

7.3. Standard page sizesThese constants define the prefabricated page size PageDim instances supported by Section 4.7, “pa-geDimFactory(): Some pre-built PageDim instances” (p. 11).

fohelpers.py

PAPER_LETTER = 'letter'PAPER_LETTER_LAND = 'letter-landscape'PAPER_LEGAL = 'legal'PAPER_3X5 = '3x5'

8. class FlowTree: The entire documentThis class represents a complete XSL-FO document. See Section 4.1, “The FlowTree class” (p. 8).

fohelpers.py

# - - - - - c l a s s F l o w T r e e

class FlowTree(object):'''Represents an XSL-FO document.

Exports:FlowTree(): [ return a new, empty FlowTree ].doc: [ the document as an et.ElementTree ].root: [ the document root as an et.Element ].masters:[ the layout-master-set as an et.Element ]

.write(outFile):[ outFile is a writeable file-like object ->

outFile +:= self as XML ]'''

8.1. FlowTree.__init__(): ConstructorThe constructor builds only two elements: the root and its first child, layout-master-set.

Zoological Data Processingfohelpers.py: XSL-FO helper functions18

Page 19: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

fohelpers.py

# - - - F l o w T r e e . _ _ i n i t _ _

def __init__(self):'''Constructor'''#-- 1 --# [ self.root := a new root et.Element# self.doc := a new et.ElementTree with that root ]self.root = E.root(xmlns=FO_NAMESPACE)self.doc = et.ElementTree(self.root)

#-- 2 --# [ self.root +:= a new layout-master-set child# self.masters := that child ]self.masters = subElement(self.root,

E("layout-master-set") )

8.2. FlowTree.write(): Serialize the documentThe pretty_print option indents the output canonically without changing the semantics.

fohelpers.py

# - - - F l o w T r e e . w r i t e

def write(self, outFile):'''Serialize as XML.'''self.doc.write(outFile)

9. class FoDim: Dimensional arithmeticIn a situation where you need to do arithmetic with dimensions—adding them, subtracting them—usean instance of this class to represent the dimension. See Section 4.3, “class FoDim: General dimen-sion” (p. 9).

fohelpers.py

# - - - - - c l a s s F o D i m

class FoDim(object):'''Represents a length dimension on XSL-FO.

Exports:FoDim(n, units):[ (n is a number in a form acceptable to the Decimal()constructor) and(units is in ['in', 'mm', 'cm', 'pc', 'pt']) ->return a FoDim instance representing that length

.__str__(self):[ return a string representing self as an XSL-FO length ]

.__add__(self, other):

19fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 20: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

[ other is a FoDim instance ->return a new FoDim representing self+other ]

.__sub__(self, other):[ other is a FoDim instance ->

return a new FoDim representing self-other ].__mul__(self, other):[ other is a value acceptable to the Decimal() constructor ->

return a new FoDim representing self*other ].__div__(self, other):[ other is a value acceptable to the Decimal() constructor ->

if other is zero ->raise ZeroDivisionError

else ->return a new FoDim representing self/other

.__neg__(self):[ return a new FoDim representing 0-self ]

.convert(newUnits):[ newUnits is one of the units acceptable to FoDim() ->

return a new FoDim representing self expressedin newUnits ]

State/Invariants:.n: [ as passed to constructor, read-only ].units: [ as passed to constructor, read-only ]

'''

For a program that tests the various unit conversions of this class, see Section 30, “confactest: Testunit conversions” (p. 36).

9.1. FoDim.__init__(): Constructorfohelpers.py

# - - - F o D i m . _ _ i n i t _ _

def __init__(self, n, units):'''Constructor'''self.n = Decimal(n)self.units = units

9.2. FoDim.__str__()fohelpers.py

# - - - F o D i m . _ _ s t r _ _

def __str__(self):'''Convert to a string.'''return "%s%s" % (self.n, self.units)

Zoological Data Processingfohelpers.py: XSL-FO helper functions20

Page 21: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

9.3. FoDim.__add__(): Add two dimensionsBecause the two dimensions may have different units, we use Section 9.8, “FoDim.convert(): Changeunits” (p. 22) to convert the second dimension to the units of the first.

fohelpers.py

# - - - F o D i m . _ _ a d d _ _

def __add__(self, other):'''Add two dimensions.'''#-- 1 --# [ otherCon := other expressed in units of (self.units) ]otherCon = other.convert(self.units)

#-- 2 --return FoDim(self.n + otherCon.n, self.units)

9.4. FoDim.__sub__(): Subtract dimensionsWe emulate subtraction by adding the negative of the second value.

fohelpers.py

# - - - F o D i m . _ _ s u b _ _

def __sub__(self, other):'''Subtract two dimensions'''#-- 1 --negOther = -other

#-- 2 --return self + negOther

9.5. FoDim.__mul__(): Multiply by a constantWe implement the multiply operator only for cases where the right-hand operator is a number (not aFoDim). A length times a length is an area, but this class deals only in lengths.

fohelpers.py

# - - - F o D i m . _ _ m u l _ _

def __mul__ ( self, other ):'''Multiply self by a constant.'''return FoDim ( self.n * Decimal(other), self.units )

9.6. FoDim.__div__(): Divide by a constantAs with .__mul__, the right-hand operand must be a decimal, not another FoDim instance.

fohelpers.py

# - - - F o D i m . _ _ d i v _ _

21fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 22: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

def __div__ ( self, other ):'''Divide self by a constant.'''return FoDim ( self.n / Decimal(other), self.units )

9.7. FoDim.__neg__(): Unary minusfohelpers.py

# - - - F o D i m . _ _ n e g _ _

def __neg__ ( self ):'''Negate a dimension.'''return FoDim(-self.n, self.units)

9.8. FoDim.convert(): Change unitsfohelpers.py

# - - - F o D i m . c o n v e r t

def convert(self, newUnits):'''Convert to the same value in different units (appproximately).'''

For the static method that computes the conversion factor for an arbitrary pair of XSL-FO units, seeSection 9.9, “FoDim.confactor(): Find the conversion factor between two units” (p. 22).

fohelpers.py

#-- 1 --# [ factor := the number by which self.n must be multiplied# to convert from self.units to newUnits ]factor = FoDim.confactor(self.units, newUnits)

#-- 2 --return FoDim(self.n * factor, newUnits)

9.9. FoDim.confactor(): Find the conversion factor between two unitsWe support the dimensional-unit codes defined in Section 7.2, “Dimensional units” (p. 18).

Some conversions are exact (e.g., cm to mm and pc to pt), while others are approximate. We would liketo use exact conversions where possible, so we order the units into a sequence named unitList, andgroup units together if they have exact conversions. We define another sequence factorList thatspecifies the conversion factors between two adjacent units in this sequence. Then, the problem of con-verting arbitrary units reduces to finding the position of the old and new units within this sequence,and finding the product of the conversion factors for each step.

fohelpers.py

# - - - F o D i m . c o n f a c t o r (static method)

unitList = (UNITS_PC, UNITS_PT, UNITS_IN, UNITS_CM, UNITS_MM)factorList = ( Decimal(12), # 1pc = 12pt

Decimal(1)/Decimal('72.27'), # 1pt = 1/72.27in

Zoological Data Processingfohelpers.py: XSL-FO helper functions22

Page 23: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

Decimal('2.54'), # 1in = 2.54cmDecimal(10) ) # 1cm = 10mm

@staticmethoddef confactor(fromUnits, toUnits):

'''Find any arbitrary conversion factor.

[ fromUnits and toUnits are XSL-FO dimensional units ->return the factor that must be multiplied by aquantity using fromUnits to express it as toUnits ]

'''

The first step is to locate the two units by their position in unitList. Note that this step will raiseValueError if the units are not standard XSL-FO units.

fohelpers.py

#-- 1 --# [ fromPos := position of fromUnits in unitList# toPos := position of toUnits in unitList# result := Decimal(1) ]fromPos = FoDim.unitList.index(fromUnits)toPos = FoDim.unitList.index(toUnits)result = Decimal(1)

To move from lower-numbered units to higher ones, we multiple the result by the factors for eachstep. In the reverse direction, we divide by those factors. If fromPos==toPos, the result remains atvalue 1.0.

fohelpers.py

#-- 2 --# [ if fromPos < toPos -># result *:= elements of FoDim.factorList in positions# fromPos, fromPos+1, ..., toPos-1, inclusive# else if fromPos > toPos -># result /:= elements of FoDim.factorList in positions# fromPos-1, fromPos-2, ..., toPos, inclusive ]if fromPos < toPos:

for pos in range(fromPos, toPos):result *= FoDim.factorList[pos]

elif fromPos > toPos:for pos in range(fromPos-1, toPos-1, -1):

result /= FoDim.factorList[pos]

#-- 3 --return result

10. class Box: Rectangle objectSee Section 4.4, “class Box: Define a rectangle” (p. 10).

fohelpers.py

# - - - - - c l a s s B o x

class Box(object):

23fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 24: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

'''Represents the dimensions of a rectangle.

Exports:Box(wide, high):[ wide and high are FoDim instances ->

return a new Box representing those dimensions ].wide: [ as passed to constructor ].high: [ as passed to constructor ].__str__(self): [ return self as a string ]

'''

10.1. Box.__init__()fohelpers.py

# - - - B o x . _ _ i n i t _ _

def __init__(self, wide, high):'''Constructor.'''self.wide, self.high = wide, high

10.2. Box.__str__()fohelpers.py

# - - - B o x . _ _ s t r _ _

def __str__(self):return ("<Box(w={0},h={1})>".format(

self.wide, self.high))

11. class MarginSet: Four margin sizesSee Section 4.5, “class MarginSet: Four margin dimensions” (p. 10).

fohelpers.py

# - - - - - c l a s s M a r g i n S e t

class MarginSet(object):'''Represents a set of four margin sizes.

Exports:MarginSet(top="0.0in", bot="0.0in", left="0.0in",

right="0.0in"):[ arguments are valid XSL-FO dimensions ->

return a new MarginSet instance representing topmargin (top), bottom margin (bot), left margin(left), and right margin (right) ]

.top, .bot, .left, .right: [ as passed to constructor ]

.dict():[ return a dict whose keys are "margin-top", etc.,with corresponding values from self.top, etc. ]

Zoological Data Processingfohelpers.py: XSL-FO helper functions24

Page 25: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

.__str__(): [ return self as a string ]'''

11.1. MarginSet.__init__(): Constructorfohelpers.py

# - - - M a r g i n S e t . _ _ i n i t _ _

def __init__(self, top="0.0in", bot="0.0in", left="0.0in",right="0.0in"):

'''Constructor.'''self.top = topself.bot = botself.left = leftself.right = right

11.2. MarginSet.dict(): Return a dictionary of margin attributesSee Section 4.5, “class MarginSet: Four margin dimensions” (p. 10); most of the work is done bySection 4.21, “dash(): Attribute set builder” (p. 16).

fohelpers.py

# - - - M a r g i n S e t . d i c t

def dict(self):'''Return a dictionary of margin attributes.'''return dash(marginTop=str(self.top), marginBottom=str(self.bot),

marginLeft=str(self.left), marginRight=str(self.right))

11.3. MarginSet.__str__()fohelpers.py

# - - - M a r g i n S e t . _ _ s t r _ _

def __str__(self):return("<MarginSet(t={0},b={1},l={2},r={3})>".format(

self.top, self.bot, self.left, self.right))

12. class PageDim: Page layoutAn instance of this class represents a general page layout; see Section 4.6, “class PageDim: Pagelayout” (p. 11).

fohelpers.py

# - - - - - c l a s s P a g e D i m

class PageDim(object):'''Represents a page layout.

25fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 26: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

Exports:PageDim(pageBox, pageMargins=None, frameMargins=None,

bodyMargins=None):[ (pageBox is the paper size as a Box instance) and(pageMargins represents the page margins as a MarginSetinstance, defaulting to no margins) and(frameMargins represents the header and footerheights as a MarginSet instance, defaulting to zero) and(bodyMargins represents the margin above and belowthe body as a MarginSet instance, defaulting to zero) ->return a new PageDim instance representingthat layout ]

.pageBox: [ as passed to constructor ]

.pageMargins: [ as passed to constructor ]

.frameMargins: [ as passed to constructor ]

.bodyMargins: [ as passed to constructor ]

Invariants:.pageMargins, .frameMargins, and .bodyMargins alwayscontain a MarginSet instance.

'''

12.1. PageDim.__init__>fohelpers.py

# - - - P a g e D i m . _ _ i n i t _ _

def __init__(self, pageBox, pageMargins=None, frameMargins=None,bodyMargins=None):

'''Constructor'''self.pageBox = pageBox

The next three lines use a common Python idiom meaning, in the first example: if pageMargins isnot None, set self.pageMargins to that value; otherwise set it to a new MarginSet instance withall zero values.

fohelpers.py

self.pageMargins = pageMargins or MarginSet()self.frameMargins = frameMargins or MarginSet()self.bodyMargins = bodyMargins or MarginSet()

12.2. PageDim.__str__()fohelpers.py

# - - - P a g e D i m . _ _ s t r _ _

def __str__(self):return ("<PageDim(box({0}), page({1}), frame({2}), "

"body({3}))>".format(self.pageBox, self.pageMargins, self.frameMargins,self.bodyMargins))

Zoological Data Processingfohelpers.py: XSL-FO helper functions26

Page 27: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

13. pageDimFactory: Create a standard PageDim instancefohelpers.py

# - - - p a g e D i m F a c t o r y

def pageDimFactory ( paperType ):'''Create a new PageDim instance for a standard sheet size.'''

See Section 4.7, “pageDimFactory(): Some pre-built PageDim instances” (p. 11). This function hasa somewhat amusing history. Formerly, there was a dictionary namedpageDimSetwith threePageDiminstances for the three sheet sizes. However, an earlier version of the fohelptest script set up evenand odd page masters like this:

oddDims = pageDimSet["letter"] # This failed!oddDims.pageMargins.left="2.0in"oddDims.pageMargins.right="1.0in"evenDims = pageDimSet["letter"]evenDims.pageMargins.left="1.0in"evenDims.pageMargins.right="2.0in"

Unfortunately, the names oddDims and evenDims were bound to the same instance, so both pagemasters used the even-side page layout.

The fix is this function, which creates a new instance each time it is called. To aid in the modification ofdimensions by adding and subtracting and multiplying, some dimensions are expressed in terms ofinstances of Section 9, “class FoDim: Dimensional arithmetic” (p. 19).

fohelpers.py

return paperTypeMap[paperType]()

def letterPage():return PageDim (

pageBox=Box(FoDim("8.5", "in"), FoDim("11", "in")),pageMargins=MarginSet(top=FoDim("0.5", "in"),

bot=FoDim("0.5", "in"), left=FoDim("0.5", "in"),right=FoDim("0.5", "in")),

frameMargins=MarginSet(top=FoDim("3", "pc"),bot=FoDim("3", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")),

bodyMargins=MarginSet(top=FoDim("1", "pc"),bot=FoDim("1", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")))

def letterLandPage():return PageDim (

pageBox=Box(FoDim("11", "in"), FoDim("8.5", "in")),pageMargins=MarginSet(top=FoDim("0.5", "in"),

bot=FoDim("0.5", "in"), left=FoDim("0.5", "in"),right=FoDim("0.5", "in")),

frameMargins=MarginSet(top=FoDim("3", "pc"),bot=FoDim("3", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")),

bodyMargins=MarginSet(top=FoDim("1", "pc"),bot=FoDim("1", "pc"), left=FoDim("0", "pc"),

27fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 28: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

right=FoDim("0", "pc")))

def legalPage():return PageDim (

pageBox=Box(FoDim("8.5", "in"), FoDim("14", "in")),pageMargins=MarginSet(top=FoDim("0.5", "in"),

bot=FoDim("0.5", "in"), left=FoDim("0.5", "in"),right=FoDim("0.5" "in")),

frameMargins=MarginSet(top=FoDim("3", "pc"),bot=FoDim("3", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")),

bodyMargins=MarginSet(top=FoDim("1", "pc"),bot=FoDim("1", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")))

def indexCardPage():return PageDim (

pageBox=Box(FoDim("3", "in"), FoDim("5", "in")),pageMargins=MarginSet(top=FoDim("0.1", "in"),

bot=FoDim("0.1", "in"), left=FoDim("0.25", "in"),right=FoDim("0.25", "in")),

frameMargins=MarginSet(top=FoDim("1.5", "pc"),bot=FoDim("1", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")),

bodyMargins=MarginSet(top=FoDim("1", "pc"),bot=FoDim("1", "pc"), left=FoDim("0", "pc"),right=FoDim("0", "pc")) )

paperTypeMap = {PAPER_LETTER: letterPage,PAPER_LETTER_LAND: letterLandPage,PAPER_LEGAL: legalPage,PAPER_3X5: indexCardPage }

14. simpleMaster: Generate a simple-page-masterSee Section 4.8, “simpleMaster(): The simple-page-master” (p. 12), and also Section 4.21,“dash(): Attribute set builder” (p. 16), the function that builds an attribute dictionary.

fohelpers.py

# - - - s i m p l e M a s t e r

def simpleMaster(masterName, pageDim, *contents, **attrs):'''Return a new simple-page-master tree.

[ (masterName is a valid XML name) and(pageDim is a PageDim instance) and(contents is a sequence of values acceptable to E()) and(attrs is a dictionary of attribute names and values) ->return a new simple-page-master et.Elementwith that master-name, page size from pageDim.pageBox,page margins from pageDim.pageMargins, contents (contents),and attributes (attrs) ]

Zoological Data Processingfohelpers.py: XSL-FO helper functions28

Page 29: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

'''#-- 1 --return E("simple-page-master",

dash(masterName=masterName,pageWidth=pageDim.pageBox.wide,pageHeight=pageDim.pageBox.high),pageDim.pageMargins.dict(),attrs, *contents)

15. regionBody(): Build a region-body elementSee Section 4.9, “regionBody(): Define the page body area” (p. 12) and Section 4.5, “class Margin-Set: Four margin dimensions” (p. 10).

fohelpers.py

# - - - r e g i o n B o d y

def regionBody(pageDim, *contents, **attrs):'''Creates a region-body element.'''return E("region-body", pageDim.bodyMargins.dict(),

*contents, **attrs)

16. regionBefore(): Build a region-before elementSee Section 4.10, “regionBefore(): Define the header area” (p. 12).

fohelpers.py

# - - - r e g i o n B e f o r e

def regionBefore(pageDim, regName):'''Create a region-before element.

[ (pageDim is a PageDim element) and(regName is a valid XML name) ->return a new region-before element whose extent comes frompageDim.frameMargins.top and whose region-name is regName ]

'''return E("region-before",

dash(regionName=regName, extent=pageDim.frameMargins.top))

17. regionAfter(): Build a region-after elementPretty similar to Section 16, “regionBefore(): Build a region-before element” (p. 29); see Sec-tion 4.11, “regionAfter(): Define the footer area” (p. 13).

fohelpers.py

# - - - r e g i o n A f t e r

def regionAfter(pageDim, regName):'''Create a region-after element.'''

29fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 30: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

return E("region-after",dash(regionName=regName, extent=pageDim.frameMargins.bot))

18. repeatMaster: Construct a repeatable page masterThis function creates a new page-sequence-master child to be appended to the layout-master-set after any simple-page-master elements. See Section 4.12, “repeatMaster(): Container formultiple alternative page masters” (p. 13).

This package does not currently implement thesingle-page-masterorrepeatable-page-master-reference features. If all the pages are the same, you don't need this master. If they differ accordingto position or page number or whether they're blank or not, you'll need the full-featured element, re-peatable-page-master-alternatives.

fohelpers.py

# - - - r e p e a t M a s t e r

def repeatMaster(masterRef, *condList):'''Set up a set of page master alternatives.

[ (masterName is the name of a new page-sequence-master) and(the remaining arguments are conditional-page-master-referenceelements) ->return a new page-sequence-master named masterNamecontaining a repeatable-page-master-alternatives childwhose children are condList[0], condList[1], etc. ]

'''return E("page-sequence-master", dash(masterName=masterRef),

E("repeatable-page-master-alternatives",*condList))

19. conditionalMaster(): Generate a conditional master ref-erence

See Section 4.13, “conditionalMaster(): One rule about when to use a page master” (p. 13).fohelpers.py

# - - - c o n d i t i o n a l M a s t e r

def conditionalMaster ( masterRef, pagePos=None, oddEven=None,blankNon=None):

'''Create a conditional-page-master-reference.

[ (masterRef is the name of the referenced master) and(pagePos is "first", "rest", "last", defaultingto no page position test) and(oddEven is "odd" or "even", defaulting to no test) and(blankNon is "blank" or "non-blank", defaulting to no test) ->

return a new conditional-page-master-reference elementwith those values ]

'''node = E("conditional-page-master-reference",

Zoological Data Processingfohelpers.py: XSL-FO helper functions30

Page 31: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

dash(masterReference=masterRef))if pagePos:

node.attrib["page-position"] = pagePosif oddEven:

node.attrib["odd-or-even"] = oddEvenif blankNon:

node.attrib["blank-or-not-blank"] = blankNonreturn node

20. pageSequence(): Generate a page-sequence elementSee Section 4.14, “pageSequence(): Build the page-sequence element” (p. 14).

fohelpers.py

# - - - p a g e S e q u e n c e

def pageSequence ( masterRef ):'''Create a page-sequence element.'''return E("page-sequence", dash(masterReference=masterRef))

21. staticContent(): Generate a static-content elementSee Section 4.15, “staticContent(): Add static content to a simple page master” (p. 14).

fohelpers.py

# - - - s t a t i c C o n t e n t

def staticContent ( flowName, *contents, **attrs ):'''Generate a static-content element.'''return E("static-content", dash(flowName=flowName),

attrs, *contents )

22. flow(): Create a flow elementfohelpers.py

# - - - f l o w

def flow():'''Return a flow element for xsl-region-body'''return E.flow(dash(flowName="xsl-region-body"))

23. block(): Generate a block elementSee Section 4.17, “block(): Generate a block element” (p. 15).

fohelpers.py

# - - - b l o c k

31fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 32: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

def block(*contents, **attrs):'''Generate a block element.'''return E ( "block", attrs, *contents )

24. inline(): Generate an inline elementSee Section 4.18, “inline(): Generate an inline element” (p. 15).

fohelpers.py

# - - - i n l i n e

def inline(*contents, **attrs):'''Generate an inline element.'''return E ( "inline", attrs, *contents )

25. leader(): Produce a leader elementThis element can be used to fill either a fixed or arbitrary horizontal area inside a block. The fill iswhitespace by default, but can be any value of the leader-pattern attribute. See Section 4.19,“leader(): Generate a leader” (p. 15).

fohelpers.py

# - - - l e a d e r

def leader(length=None, pattern=None):'''Create a leader element.'''#-- 1 --lengthDict = ({}

if length is Noneelse dash(leaderLength=length))

#-- 2leaderPattern = ('space'

if pattern is Noneelse pattern )

#-- 3return E.leader(lengthDict, dash(leaderPattern=leaderPattern))

26. font(): Dictionary of font propertiesSee Section 4.20, “font(): Dictionary of font properties” (p. 15).

fohelpers.py

# - - - f o n t

fontArgMap = {'fontFamily': 'font-family', 'fontSize': 'font-size','fontStyle': 'font-style', 'fontWeight': 'font-weight',

Zoological Data Processingfohelpers.py: XSL-FO helper functions32

Page 33: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

'fontVariant': 'font-variant', 'fontStretch': 'font-stretch' }def font(**kw):

'''Return a dictionary of font properties.'''result = {}for key in kw:

result[fontArgMap[key]] = kw[key]return result

27. dash(): An attribute dictionary generatorfohelpers.py

# - - - d a s h

def dash(**kw):'''XSL-FO attribute dictionary builder.

[ kw is a dictionary ->return a new dictionary with the same elements as kw,but with each key transformed by substituting "x-y"for any sequence "xY" where x is lowercase and Y isuppercase ]

'''#-- 1result = {}

See Section 28, “deCamel(): Convert Python names to XSL-FO names” (p. 33).fohelpers.py

#-- 2for key in kw:

result[deCamel(key)] = kw[key]

#-- 3return result

28. deCamel(): Convert Python names to XSL-FO namesSee Section 27, “dash(): An attribute dictionary generator” (p. 33) for a discussion of the need for thisfunction.

fohelpers.py

# - - - d e C a m e l

def deCamel(name):'''Change camelCase names to hyphenated-names.

[ name is a str ->return name, transformed by substituting "x-y"for any sequence "xY" where x is lowercase and Y isuppercase ]

'''#-- 1

33fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 34: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

last = 'A'result = []

#-- 2# [ result +:= all but the first character of (last+name)# with sequences "xY" replaced replaced by "x-y"# last := the last character of name ]for c in name:

#-- 2 bodyresult.append (("-" + c.lower())

if last.islower() and c.isupper()else c)

last = c

#-- 3return ''.join(result)

29. fohelptest: Test driver for fohelpersThis driver file generates a document with even-odd layout, a running head, and the page number inthe footer.

fohelptest

#!/usr/bin/env python#================================================================# fohelptest: Test driver for fohelpers.py# For documentation, see:# http://www.nmt.edu/~shipman/soft/fohelpers/#================================================================# Imports#----------------------------------------------------------------import sysfrom fohelpers import *

#================================================================# Manifest constants#----------------------------------------------------------------

#--# Names of the simple masters and the repeatable master#--ODD_MASTER = "odd"EVEN_MASTER = "even"REPEAT_MASTER = "all"

#--# Names of the regions#--ODD_BEFORE = "odd-before"EVEN_BEFORE = "even-before"ODD_AFTER = "odd-after"EVEN_AFTER = "even-after"

Zoological Data Processingfohelpers.py: XSL-FO helper functions34

Page 35: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

We start by setting up some PageDim instances for the two different page layouts (even and odd), andthree fonts, headFont for the header, footFont for the footer, and bodyFont for the body text.

fohelptest

# - - - - - m a i n

oddDims = pageDimFactory("letter")oddDims.pageMargins.left = "2.0in"oddDims.pageMargins.right = "1.0in"

evenDims = pageDimFactory("letter")evenDims.pageMargins.left="1.0in"evenDims.pageMargins.right="2.0in"

mainFamily = "Palatino, Paladino, serif"monoFamily = "Lucida Typewriter, monospace"

headFont = font(fontFamily=mainFamily, fontSize="12pt")footFont = font(fontFamily=mainFamily, fontSize="9pt",

fontStyle="italic")bodyFont = font(fontFamily=mainFamily, fontSize="10pt")monoFont = font(fontFamily=monoFamily, fontSize="10pt")

The tree is built from the top down: first the FlowTree instance that holds the whole document, thenthe page masters, then the main page sequence.

fohelptest

#--# Create the root element, and attach the layout-master-set# and its children: two simple-page-master elements and a# page-sequence-master with repeatable-page-master-alternatives.#--

tree = FlowTree()

oddMaster = subElement(tree.masters,simpleMaster(ODD_MASTER, oddDims,

regionBody(oddDims),regionBefore(oddDims, ODD_BEFORE),regionAfter(oddDims, ODD_AFTER)))

evenMaster = subElement(tree.masters,simpleMaster(EVEN_MASTER, evenDims,

regionBody(evenDims),regionBefore(evenDims, EVEN_BEFORE),regionAfter(evenDims, EVEN_AFTER)))

tree.masters.append(repeatMaster(REPEAT_MASTER,conditionalMaster(ODD_MASTER, oddEven="odd"),conditionalMaster(EVEN_MASTER, oddEven="even")))

The content is in a page-sequence element that is a child of the root. There are five child elementsof the page-sequence: four static-content elements defining the odd-page header, odd-pagefooter, even-page header, and even-page footer; and a flow element containing the body content.

35fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 36: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

fohelptest

#--# Append a page-sequence to the root, then pour static content# into the header and footer of the even and odd page areas.#--pages = subElement(tree.root, pageSequence(REPEAT_MASTER))

oddHead = subElement(pages,staticContent(ODD_BEFORE,

block(headFont, dash(textAlign='right'),"Running head")))

oddFoot = subElement ( pages,staticContent(ODD_AFTER,

block(footFont, dash(textAlign='right'),E("page-number"))))

evenHead = subElement ( pages,staticContent(EVEN_BEFORE,

block(headFont, dash(textAlign='left'),"Running head")))

evenFoot = subElement ( pages,staticContent(EVEN_AFTER,

block(footFont, dash(textAlign='left'),E("page-number"))))

Into the flow element we pour ten paragraphs, each of which is twenty copies of a sample sentence.fohelptest

sampleText = ( "Now is the time for all good organisms to ""come to the aid of their planet. " )

flow = subElement(pages, E.flow(dash(flowName="xsl-region-body")))for i in range(10):

parBlock = subElement(flow, block(bodyFont))parBlock.attrib["space-before"] = "10pt"parBlock.text = sampleText*20

tree.write(sys.stdout)

30. confactest: Test unit conversionsThis is a small test driver to exercise all the possible unit conversions within the FoDim class.

confactest

#!/usr/bin/env python#================================================================# confactest: Test driver for FoDim.confactor()# For documentation, see:# http://www.nmt.edu/~shipman/soft/fohelpers/#----------------------------------------------------------------

import fohelpers as h

Zoological Data Processingfohelpers.py: XSL-FO helper functions36

Page 37: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

u = h.FoDim.unitList

for fromPos in range(0, len(u)):for toPos in range(0, len(u)):

factor = h.FoDim.confactor(u[fromPos], u[toPos])print ( "To convert from %s to %s, multiply by %s" %

(u[fromPos], u[toPos], factor) )

37fohelpers.py: XSL-FO helper functionsZoological Data Processing

Page 38: fohelpers.py: XSL-FO helper functions for Pythonshipman/soft/fohelpers/fohelpers.pdf · fohelpers.py: XSL-FO helper functions for Python John W. Shipman 2013-08-29 20:13 Abstract

Zoological Data Processingfohelpers.py: XSL-FO helper functions38