"""fo_helpers.py: Helper objects for producing XSL-FO $Revision: 1.4 $ $Date: 2002/11/15 00:24:06 $ NB: All dimensions must adhere to XSL-FO conventions. USAGE: import sys from fo_helpers import * from foldifier import * ... f = Foldifier ( sys.stdout ) yourDims = pageDimSet["letter"] # or whatever size fot = FlowTree ( yourDims ) pageSequence = BlockTag ( fot.root, "fo:page-sequence" ) pageSequence.addAttr ( "master-reference", MASTER_NAME ) header = BlockTag ( pageSequence, "fo:static-content" ) header.addAttr ( "flow-name", "xsl-region-before" ) headerBlock = BlockTag ( header, "fo:block" ) headerBlock.addAttr ( "text-align", "start" ) headerBlock.add ( "Place running head here" ) footer = BlockTag ( pageSequence, "fo:static-content" ) footer.addAttr ( "flow-name", "xsl-region-after" ) footerBlock = BlockTag ( footer, "fo:block" ) footerBlock.addAttr ( "text-align-last", "center" ) folio = EmptyTag ( footerBlock, "fo:page-number" ) flow = BlockTag ( pageSequence, "fo:flow" ) flow.addAttr ( "flow-name", "xsl-region-body" ) # All further content is added to flow by constructs like: frodoBlock = BlockTag ( flow, "fo:block" ) frodoBlock.addAttr ( "font-family", "serif" ) frodoBlock.addAttr ( "font-size", "11pt" ) frodoBlock.add ( "Fisssh! We likes it." ) class Box: Represents a rectangle Box ( wide, high ): [ if (wide and high are positive dimensions) -> return a Box representing those dimensions .wide: [ as passed to constructor ] .high: [ as passed to constructor ] class MarginSet: Represents one set of margins MarginSet ( top="0.0in", bot="0.0in", left="0.0in", right="0.0in" ): [ if (top is the top margin as an XPath dimension) and (bot is the bot margin as an XPath dimension) and (left is the left margin as an XPath dimension) and (right is the right margin as an XPath dimension) -> return a new MarginSet object with those values ] .top: [ as passed to constructor ] .bot: [ as passed to constructor ] .left: [ as passed to constructor ] .right: [ as passed to constructor ] class PageDim: Represents a page layout PageDim ( pageBox=None, pageMargins=None, frameMargins=None, bodyMargins=None ): [ if ( ( pageBox is Box giving the overall page dimensions, defaulting to 8.5x11" ) and ( pageMargins is a MarginSet representing the page's overall margins, default to zero margins ) and ( frameMargins is a MarginSet whose top margin represents the header height and whose bottom margin represents the footer height, defaulting to 1" top and bottom and 0" left and right ) and ( bodyMargins is a MarginSet whose top and bottom margins represent the space above and below the page body, defaulting to zero margins ) ) -> return a new PageDim object representing that set of dimensions ] .pageBox: [ as passed to constructor ] .pageMargins: [ as passed to constructor ] .frameMargins: [ as passed to constructor ] .bodyMargins: [ as passed to constructor ] NB: In the frameMargins argument, .left and .right are ignored. pageDimSet: Represents a set of predefined page layouts. [ a dictionary whose keys are codes for page layouts such as "letter", "legal", or "3x5", and whose corresponding values are PageDim objects describing those page layouts ] class FlowTree: Represents a document as an XSL-FO flow object tree FlowTree ( pageDim ): [ if pageDim is a PageDim object -> return a new FlowTree with pageDim's layout and no content ] .pageDim: [ as passed to constructor; read/write ] .doc: [ the document as a gen_xml Document object ] .root: [ the element as a gen_xml Tag object ] .xml(f): [ if f is a Foldifier object -> f +:= self as XSL-FO ] NB: The fo:layout-master-set element is automatically added to self.root; the user need only use gen_xml to add fo:page-sequence elements self.root. If this turns out to be onerous, see file fo_orphans for an alternative design. """ #================================================================ # IMPORTS #---------------------------------------------------------------- from gen_xml import * # My XML generation objects #================================================================ # MANIFEST CONSTANTS #---------------------------------------------------------------- MASTER_NAME = "simple" # fo:simple-page-master's master-name # - - - - - c l a s s B o x - - - - - class Box: "Represents a rectangle's dimensions." def __init__ ( self, wide, high ): "Constructor for Box" self.wide = wide self.high = high # - - - - - c l a s s M a r g i n S e t - - - - - class MarginSet: "Represents one set of four margin sizes." def __init__ ( self, top="0.0in", bot="0.0in", left="0.0in", right="0.0in" ): "Constructor for MarginSet" self.top = top self.bot = bot self.left = left self.right = right # - - - - - c l a s s P a g e D i m - - - - - class PageDim: "Represents one page layout with overall margins, header, and footer." def __init__ ( self, pageBox=None, pageMargins=None, frameMargins=None, bodyMargins=None ): "Constructor for PageDim" if pageBox is None: self.pageBox = Box ( "8.5in", "11in" ) else: self.pageBox = pageBox if pageMargins is None: self.pageMargins = MarginSet () else: self.pageMargins = pageMargins if frameMargins is None: self.frameMargins = MarginSet ( top="1.0in", bot="1.0in", left="0.0in", right="0.0in" ) else: self.frameMargins = frameMargins if bodyMargins is None: self.bodyMargins = MarginSet() else: self.bodyMargins = bodyMargins # - - - - - c l a s s P a g e D i m S e t - - - - - pageDimSet = { "letter": PageDim ( pageBox=Box("8.5in", "11in"), pageMargins=MarginSet(top="0.5in", bot="0.5in", left="0.5in", right="0.5in"), frameMargins=MarginSet(top="3pc", bot="3pc"), bodyMargins=MarginSet(top="3pc") ), "legal": PageDim ( pageBox=Box("8.5in", "14in"), pageMargins=MarginSet(top="0.5in", bot="0.5in", left="0.5in", right="0.5in"), frameMargins=MarginSet(top="3pc", bot="3pc"), bodyMargins=MarginSet(top="3pc") ), "3x5": PageDim ( pageBox=Box("3in", "5in"), pageMargins=MarginSet(top="0.1in", bot="0.1in", left="0.25in", right="0.25in"), frameMargins=MarginSet(top="1.5pc", bot="1pc"), bodyMargins=MarginSet(top="1pc", bot="1pc") ) } # - - - - - c l a s s F l o w T r e e - - - - - class FlowTree: "Represents an XSL-FO rendering of a document as a flow object tree." # - - - F l o w T r e e . _ _ i n i t _ _ - - - def __init__ ( self, pageDim ): "Constructor for FlowTree" #-- 1 -- self.pageDim = pageDim #-- 2 -- # [ self.doc := a new, empty gen_xml Document object # self.root := a gen_xml fo:root element in that Document, # specifying XSL-FO output ] self.doc = Document ( ) self.doc.root = self.root = BlockTag ( None, "fo:root" ) self.doc.root.addAttr ( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" ) #-- 3 -- # [ self.root := self.root with an fo:layout-master-set # element added with dimensions taken from self.pageDim # and a master named MASTER_NAME ] self.__buildLayout ( ) # - - - F l o w T r e e . _ _ b u i l d L a y o u t - - - def __buildLayout ( self ): """Build the fo:layout-master-set and its children [ if (self.pageDim is a PageDim object descripting a page layout) -> self.root := self.root with an fo:layout-master-set element added with dimensions taken from self.pageDim and a master named MASTER_NAME ] """ #-- 1 -- # [ self.root := self.root with a new, empty fo:layout-master-set # element added # layoutMasterSet := that new element ] layoutMasterSet = BlockTag ( self.root, "fo:layout-master-set" ) #-- 2 -- # [ layoutMasterSet +:= a new fo:simple-page-master element # simplePageMaster := that element ] simplePageMaster = BlockTag ( layoutMasterSet, "fo:simple-page-master" ) #-- 3 -- # [ simplePageMaster +:= page layout attributes from self.pageDim ] dim = self.pageDim simplePageMaster.addAttr ( "master-name", MASTER_NAME ) simplePageMaster.addAttr ( "page-height", dim.pageBox.high ) simplePageMaster.addAttr ( "page-width", dim.pageBox.wide ) simplePageMaster.addAttr ( "margin-top", dim.pageMargins.top ) simplePageMaster.addAttr ( "margin-bottom", dim.pageMargins.bot ) simplePageMaster.addAttr ( "margin-left", dim.pageMargins.left ) simplePageMaster.addAttr ( "margin-right", dim.pageMargins.right ) #-- 4 -- # [ simplePageMaster +:= region definitions from dim ] self.__buildRegionLayouts ( simplePageMaster, dim ) # - - - F l o w T r e e . _ _ b u i l d R e g i o n L a y o u t s - - - def __buildRegionLayouts ( self, simplePageMaster, dim ): """Build the fo:-region-{body|before|after} elements [ if (simplePageMaster is a fo:simple-page-master element as a gen_xml Tag object) and (dim is a PageDim object) -> simplePageMaster +:= fo:region-{body|before|after} elements with dimensions taken from dim ] """ #-- 1 -- # [ simplePageMaster +:= a new, empty fo:region-body tag, # with its margins taken from dim.pageMargins ] regionBody = EmptyTag ( simplePageMaster, "fo:region-body" ) regionBody.addAttr ( "margin-top", dim.bodyMargins.top ) regionBody.addAttr ( "margin-bottom", dim.bodyMargins.bot ) regionBody.addAttr ( "margin-left", dim.bodyMargins.left ) regionBody.addAttr ( "margin-right", dim.bodyMargins.right ) #-- 2 -- # [ simplePageMaster +:= a new, empty fo:region-before tag, # with its extent taken from dim.frameMargin.top ] regionBefore = EmptyTag ( simplePageMaster, "fo:region-before" ) regionBefore.addAttr ( "extent", dim.frameMargins.top ) #-- 3 -- # [ simplePageMaster +:= a new, empty fo:region-after tag, # with its extent taken from dim.frameMargin.bot ] regionAfter = EmptyTag ( simplePageMaster, "fo:region-after" ) regionAfter.addAttr ( "extent", dim.frameMargins.bot ) # - - - F l o w T r e e . x m l - - - def xml ( self, f ): "Output self as XML" self.doc.str(f)