tools/docco/rl_doc_utils.py
author robin
Mon, 18 Nov 2013 17:28:48 +0000
branchpy33
changeset 3789 5bc95e1f3dd4
parent 3723 99aa837b6703
child 3811 f7eb60441a60
permissions -rw-r--r--
changes to userguide code

#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2012
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/docco/rl_doc_utils.py
__version__=''' $Id$ '''


__doc__ = """
This module contains utilities for generating guides
"""

import os, sys, glob
import string

from .rltemplate import RLDocTemplate
from .stylesheet import getStyleSheet
styleSheet = getStyleSheet()

#from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter, A4, A5, A3  # latter two for testing
from reportlab.rl_config import defaultPageSize
from reportlab.platypus import figures
from reportlab.platypus import Paragraph, Spacer, Preformatted,\
            PageBreak, CondPageBreak, Flowable, Table, TableStyle, \
            NextPageTemplate, KeepTogether, Image, XPreformatted
from reportlab.platypus.xpreformatted import PythonPreformatted
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.sequencer import getSequencer

from . import examples

appmode=0

from .t_parse import Template
QFcodetemplate = Template("X$X$", "X")
QFreptemplate = Template("X^X^", "X")
codesubst = "%s<font name=Courier>%s</font>"
QFsubst = "%s<font name=Courier><i>%s</i></font>"


def quickfix(text):
    """inside text find any subsequence of form $subsequence$.
       Format the subsequence as code.  If similarly if text contains ^arg^
       format the arg as replaceable.  The escape sequence for literal
       $ is $\\$ (^ is ^\\^.
    """
    for (template,subst) in [(QFcodetemplate, codesubst), (QFreptemplate, QFsubst)]:
        fragment = text
        parts = []
        try:
            while fragment:
                try:
                    (matches, index) = template.PARSE(fragment)
                except: raise ValueError
                else:
                    [prefix, code] = matches
                    if code == "\\":
                        part = fragment[:index]
                    else:
                        part = subst % (prefix, code)
                    parts.append(part)
                    fragment = fragment[index:]
        except ValueError:
            parts.append(fragment)
        text = ''.join(parts)
    return text
#print quickfix("$testing$ testing $one$ ^two^ $three(^four^)$")



H1 = styleSheet['Heading1']
H2 = styleSheet['Heading2']
H3 = styleSheet['Heading3']
H4 = styleSheet['Heading4']
B = styleSheet['BodyText']
BU = styleSheet['Bullet']
Comment = styleSheet['Comment']
Centred = styleSheet['Centred']
Caption = styleSheet['Caption']

#set up numbering
seq = getSequencer()
seq.setFormat('Chapter','1')
seq.setFormat('Section','1')
seq.setFormat('Appendix','A')
seq.setFormat('Figure', '1')
seq.chain('Chapter','Section')
seq.chain('Chapter','Figure')

lessonnamestyle = H2
discussiontextstyle = B
exampletextstyle = styleSheet['Code']
# size for every example
examplefunctionxinches = 5.5
examplefunctionyinches = 3
examplefunctiondisplaysizes = (examplefunctionxinches*inch, examplefunctionyinches*inch)

def getJustFontPaths():
    '''return afm and pfb for Just's files'''
    import reportlab
    folder = os.path.dirname(reportlab.__file__) + os.sep + 'fonts'
    return os.path.join(folder, 'DarkGardenMK.afm'), os.path.join(folder, 'DarkGardenMK.pfb')

# for testing
def NOP(*x,**y):
    return None

def CPage(inches):
    getStory().append(CondPageBreak(inches*inch))

def newPage():
    getStory().append(PageBreak())

def nextTemplate(templName):
    f = NextPageTemplate(templName)
    getStory().append(f)

def disc(text, klass=Paragraph, style=discussiontextstyle):
    text = quickfix(text)
    P = klass(text, style)
    getStory().append(P)

def restartList():
    getSequencer().reset('list1')

def list1(text, doBullet=1):
    text=quickfix(text)
    if doBullet:
        text='<bullet><seq id="list1"/>.</bullet>'+text
    P = Paragraph(text, BU)
    getStory().append(P)

def bullet(text):
    text='<bullet><font name="Symbol">\xe2\x80\xa2</font></bullet>' + quickfix(text)
    P = Paragraph(text, BU)
    getStory().append(P)

def eg(text,before=0.1,after=0):
    space(before)
    disc(text, klass=PythonPreformatted, style=exampletextstyle)
    space(after)

def space(inches=1./6):
    if inches: getStory().append(Spacer(0,inches*inch))

def EmbeddedCode(code,name='t'):
    eg(code)
    disc("produces")
    exec(code+("\ngetStory().append(%s)\n"%name))

def startKeep():
    return len(getStory())

def endKeep(s):
    S = getStory()
    k = KeepTogether(S[s:])
    S[s:] = [k]

def title(text):
    """Use this for the document title only"""
    disc(text,style=styleSheet['Title'])

#AR 3/7/2000 - defining three new levels of headings; code
#should be swapped over to using them.

def headingTOC(text='Table of contents'):
    getStory().append(PageBreak())
    p = Paragraph(text, H1)
    getStory().append(p)

def heading1(text):
    """Use this for chapters.  Lessons within a big chapter
    should now use heading2 instead.  Chapters get numbered."""
    getStory().append(PageBreak())
    p = Paragraph('Chapter <seq id="Chapter"/> ' + quickfix(text), H1)
    getStory().append(p)

def Appendix1(text,):
    global appmode
    getStory().append(PageBreak())
    if not appmode:
        seq.setFormat('Chapter','A')
        seq.reset('Chapter')
        appmode = 1
    p = Paragraph('Appendix <seq id="Chapter"/> ' + quickfix(text), H1)
    getStory().append(p)

def heading2(text):
    """Used to be 'lesson'"""
    getStory().append(CondPageBreak(inch))
    p = Paragraph('<seq template="%(Chapter)s.%(Section+)s "/>' + quickfix(text), H2)
    getStory().append(p)

def heading3(text):
    """Used to be most of the plain old 'head' sections"""
    getStory().append(CondPageBreak(inch))
    p = Paragraph(quickfix(text), H3)
    getStory().append(p)

def image(path, width=None, height=None ):
    s = startKeep()
    space(.2)
    import reportlab
    rlDocImageDir = os.path.join(os.path.dirname(reportlab.__file__), 'docs','images')
    getStory().append(Image(os.path.join(rlDocImageDir,path),width,height))
    space(.2)
    endKeep(s)

def heading4(text):
    """Used to be most of the plain old 'head' sections"""
    getStory().append(CondPageBreak(inch))
    p = Paragraph(quickfix(text), H4)
    getStory().append(p)

def todo(text):
    """Used for notes to ourselves"""
    getStory().append(Paragraph(quickfix(text), Comment))

def centred(text):
    getStory().append(Paragraph(quickfix(text), Centred))

def caption(text):
    getStory().append(Paragraph(quickfix(text), Caption))

class Illustration(figures.Figure):
    """The examples are all presented as functions which do
    something to a canvas, with a constant height and width
    used.  This puts them inside a figure box with a caption."""

    def __init__(self, operation, caption, width=None, height=None):
        stdwidth, stdheight = examplefunctiondisplaysizes
        if not width:
            width = stdwidth
        if not height:
            height = stdheight
        #figures.Figure.__init__(self, stdwidth * 0.75, stdheight * 0.75)
        figures.Figure.__init__(self, width, height,
                    'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption))
        self.operation = operation

    def drawFigure(self):
        #shrink it a little...
        #self.canv.scale(0.75, 0.75)
        self.operation(self.canv)


def illust(operation, caption, width=None, height=None):
    i = Illustration(operation, caption, width=width, height=height)
    getStory().append(i)


class GraphicsDrawing(Illustration):
    """Lets you include reportlab/graphics drawings seamlessly,
    with the right numbering."""
    def __init__(self, drawing, caption):
        figures.Figure.__init__(self,
                                  drawing.width,
                                  drawing.height,
                    'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
                                  )
        self.drawing = drawing

    def drawFigure(self):
        d = self.drawing
        d.wrap(d.width, d.height)
        d.drawOn(self.canv, 0, 0)

def draw(drawing, caption):
    d = GraphicsDrawing(drawing, caption)
    getStory().append(d)

class ParaBox(figures.Figure):
    """Illustrates paragraph examples, with style attributes on the left"""
    descrStyle = ParagraphStyle('description',
                                fontName='Courier',
                                fontSize=8,
                                leading=9.6)

    def __init__(self, text, style, caption):
        figures.Figure.__init__(self, 0, 0, caption)
        self.text = text
        self.style = style
        self.para = Paragraph(text, style)

        styleText = self.getStyleText(style)
        self.pre = Preformatted(styleText, self.descrStyle)

    def wrap(self, availWidth, availHeight):
        """Left 30% is for attributes, right 50% for sample,
        10% gutter each side."""
        self.x0 = availWidth * 0.05  #left of box
        self.x1 = availWidth * 0.1   #left of descriptive text
        self.x2 = availWidth * 0.5   #left of para itself
        self.x3 = availWidth * 0.9   #right of para itself
        self.x4 = availWidth * 0.95  #right of box
        self.width = self.x4 - self.x0
        self.dx = 0.5 * (availWidth - self.width)

        paw, self.pah = self.para.wrap(self.x3 - self.x2, availHeight)
        self.pah = self.pah + self.style.spaceBefore + self.style.spaceAfter
        prw, self.prh = self.pre.wrap(self.x2 - self.x1, availHeight)
        self.figureHeight = max(self.prh, self.pah) * 10.0/9.0
        return figures.Figure.wrap(self, availWidth, availHeight)

    def getStyleText(self, style):
        """Converts style to preformatted block of text"""
        lines = []
        for key, value in style.__dict__.items():
            lines.append('%s = %s' % (key, value))
        lines.sort()
        return '\n'.join(lines)

    def drawFigure(self):

        #now we fill in the bounding box and before/after boxes
        self.canv.saveState()
        self.canv.setFillGray(0.95)
        self.canv.setDash(1,3)
        self.canv.rect(self.x2 - self.x0,
                       self.figureHeight * 0.95 - self.pah,
                       self.x3-self.x2, self.para.height,
                       fill=1,stroke=1)

        self.canv.setFillGray(0.90)
        self.canv.rect(self.x2 - self.x0, #spaceBefore
                       self.figureHeight * 0.95 - self.pah + self.para.height,
                       self.x3-self.x2, self.style.spaceBefore,
                       fill=1,stroke=1)

        self.canv.rect(self.x2 - self.x0, #spaceBefore
                       self.figureHeight * 0.95 - self.pah - self.style.spaceAfter,
                       self.x3-self.x2, self.style.spaceAfter,
                       fill=1,stroke=1)

        self.canv.restoreState()
        #self.canv.setFillColor(colors.yellow)
        self.para.drawOn(self.canv, self.x2 - self.x0,
                         self.figureHeight * 0.95 - self.pah)
        self.pre.drawOn(self.canv, self.x1 - self.x0,
                         self.figureHeight * 0.95 - self.prh)


    def getStyleText(self, style):
        """Converts style to preformatted block of text"""
        lines = []
        for key, value in style.__dict__.items():
            if key not in ('name','parent'):
                lines.append('%s = %s' % (key, value))
        return '\n'.join(lines)


class ParaBox2(figures.Figure):
    """Illustrates a paragraph side-by-side with the raw
    text, to show how the XML works."""
    def __init__(self, text, caption):
        figures.Figure.__init__(self, 0, 0, caption)
        descrStyle = ParagraphStyle('description',
                                fontName='Courier',
                                fontSize=8,
                                leading=9.6)
        textStyle = B
        self.text = text
        self.left = Paragraph('<![CDATA[' + text + ']]>', descrStyle)
        self.right = Paragraph(text, B)


    def wrap(self, availWidth, availHeight):
        self.width = availWidth * 0.9
        colWidth = 0.4 * self.width
        lw, self.lh = self.left.wrap(colWidth, availHeight)
        rw, self.rh = self.right.wrap(colWidth, availHeight)
        self.figureHeight = max(self.lh, self.rh) * 10.0/9.0
        return figures.Figure.wrap(self, availWidth, availHeight)

    def drawFigure(self):
        self.left.drawOn(self.canv,
                         self.width * 0.05,
                         self.figureHeight * 0.95 - self.lh
                         )
        self.right.drawOn(self.canv,
                         self.width * 0.55,
                         self.figureHeight * 0.95 - self.rh
                         )

def parabox(text, style, caption):
    p = ParaBox(text, style,
                'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
                )
    getStory().append(p)

def parabox2(text, caption):
    p = ParaBox2(text,
                'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
                )
    getStory().append(p)

def pencilnote():
    getStory().append(examples.NoteAnnotation())


from reportlab.lib.colors import tan, green
def handnote(xoffset=0, size=None, fillcolor=tan, strokecolor=green):
    getStory().append(examples.HandAnnotation(xoffset,size,fillcolor,strokecolor))


#make a singleton, created when requested rather
#than each time a chapter imports it.
_story = []
def setStory(story=[]):
    global _story
    _story = story
def getStory():
    return _story