dabo Commit
Revision 5223
Date: 2009-05-17 19:52:14 -0700 (Sun, 17 May 2009)
Author: Paul
Trac: http://trac.dabodev.com/changeset/5223
Changed:
U trunk/dabo/lib/caselessDict.py
U trunk/dabo/lib/reportWriter.py
A trunk/dabo/lib/reporting_tests/
D trunk/dabo/lib/reporting_tests/invoice_demo/
A trunk/dabo/lib/reporting_tests/invoice_demo/
D trunk/dabo/lib/reporting_tests/invoice_demo/bizcard01_medium.jpg
A trunk/dabo/lib/reporting_tests/invoice_demo/bizcard01_medium.jpg
D trunk/dabo/lib/reporting_tests/invoice_demo/invoice.py
A trunk/dabo/lib/reporting_tests/invoice_demo/invoice.py
D trunk/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml
A trunk/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml
D trunk/dabo/lib/reporting_tests/invoice_demo/logo_2cannisters_medium.jpg
A trunk/dabo/lib/reporting_tests/invoice_demo/logo_2cannisters_medium.jpg
Log:
Merged in my reporting branch. Reportwriter now handles paragraphs that span
multiple pages. There are issues still, but they are minor.
Diff:
Modified: trunk/dabo/lib/caselessDict.py
===================================================================
--- trunk/dabo/lib/caselessDict.py 2009-05-18 02:32:03 UTC (rev 5222)
+++ trunk/dabo/lib/caselessDict.py 2009-05-18 02:52:14 UTC (rev 5223)
@@ -24,7 +24,7 @@
return dict.__getitem__(self, key.lower())
def __delitem__(self, key):
- dict.__delitem__(self, key)
+ dict.__delitem__(self, key.lower())
del(self._OriginalCase[key.lower()])
def __contains__(self, key):
Modified: trunk/dabo/lib/reportWriter.py
===================================================================
--- trunk/dabo/lib/reportWriter.py 2009-05-18 02:32:03 UTC (rev 5222)
+++ trunk/dabo/lib/reportWriter.py 2009-05-18 02:52:14 UTC (rev 5223)
@@ -462,11 +462,18 @@
def initAvailableProps(self):
super(Band, self).initAvailableProps()
self.AvailableProps["Height"] = toPropDict(float, 0.0,
- """Specifies the height of the band.
+ """Specifies the height of the band, not
including growable objects.
If the height evaluates to None, the height of
the band will size
itself dynamically at runtime.""")
+ self.AvailableProps["TotalHeight"] = toPropDict(float, 0.0,
+ """Specifies the height of the band, including
growable objects.
+
+ Read-only/calculated at runtime. Specifies the
height of the band
+ on the page, and gets reevaluated for each page
the band continues
+ printing on.""")
+
self.AvailableProps["DesignerLock"] = toPropDict(bool, False,
"""Specifies whether the band height can be
changed interactively.
@@ -693,6 +700,7 @@
"""Represents a frameset."""
def initAvailableProps(self):
super(Frameset, self).initAvailableProps()
+
self.AvailableProps["FrameId"] = toPropDict(str, None,
"""(to remove)""")
@@ -717,8 +725,6 @@
self.AvailableProps["ColumnCount"] = toPropDict(int, 1,
"""Specifies the number of columns in the
frame.""")
- self.AvailableProps["calculatedHeight"] = toPropDict(float, 0,
- """(to remove)""")
def insertRequiredElements(self):
"""Insert any missing required elements into the frameset."""
@@ -807,19 +813,21 @@
"""
_clearMemento = True
-
- def draw(self, obj, origin, getNeededHeight=False):
+ def draw(self, obj, origin=(0,0), availableHeight=None,
deferred=None):
"""Draw the given object on the Canvas.
The object is a dictionary containing properties, and origin
is the (x,y)
tuple where the object will be drawn.
+
+ availableHeight is the height available on the current page;
deferred is
+ the contents of the object partially printed on the last page
that needs
+ to continue printing now (paragraph story).
"""
- neededHeight = 0
-
## (Can't get x,y directly from object because it may have been
modified
## by the calling program to adjust for band position, and
draw()
## doesn't know about bands.)
-
+ neededHeight = 0
+ objType = obj.__class__.__name__
c = self.Canvas
x,y = origin
@@ -828,18 +836,11 @@
## returns between c.saveState() and c.restoreState()!
c.saveState()
+ neededHeight = 0
+
## These properties can apply to all objects:
width = self.getPt(obj.getProp("width"))
- try:
- height = obj.getProp("calculatedHeight")
- except ValueError:
- height = None
- if height is not None:
- height = self.getPt(height)
- else:
- height = self.getPt(obj.getProp("Height"))
-
rotation = obj.getProp("rotation")
hAnchor = obj.getProp("hAnchor").lower()
vAnchor = obj.getProp("vAnchor").lower()
@@ -848,15 +849,16 @@
x = x - width
elif hAnchor == "center":
x = x - (width / 2)
-
- if vAnchor == "top":
- y = y - height
- elif vAnchor == "middle":
- y = y - (height / 2)
+ if objType != "Frameset":
+ height = self.getPt(obj.getProp("Height"))
+ if vAnchor == "top":
+ y = y - height
+ elif vAnchor == "middle":
+ y = y - (height / 2)
+
## Do specific things based on object type:
- objType = obj.__class__.__name__
if objType == "Rectangle":
d = shapes.Drawing(width, height)
d.rotate(rotation)
@@ -1009,17 +1011,43 @@
elif objType == "Frameset":
# A frame is directly related to reportlab's platypus
Frame.
+
borderWidth = self.getPt(obj.getProp("borderWidth"))
borderColor = obj.getProp("borderColor")
- frameId = obj.getProp("frameId")
+ columnCount = obj.getProp("columnCount")
+ columnWidth = width/columnCount
padLeft = self.getPt(obj.getProp("padLeft"))
padRight = self.getPt(obj.getProp("padRight"))
padTop = self.getPt(obj.getProp("padTop"))
padBottom = self.getPt(obj.getProp("padBottom"))
- columnCount = obj.getProp("columnCount")
-
- columnWidth = width/columnCount
+ frameId = obj.getProp("frameId")
+ if deferred:
+ story = deferred
+ neededHeight = sum([s[1] for s in story])
+ else:
+ story, neededHeight = self.getStory(obj)
+ printStory = story
+ deferredStory = []
+ tot_p_height = 0
+ printStoryHeight = deferredStoryHeight = 0
+ if neededHeight >= availableHeight:
+ printStory = []
+ for p, p_height in story:
+ tot_p_height += p_height
+ if tot_p_height + padTop + padBottom >=
availableHeight:
+ deferredStory.append((p,
p_height))
+ deferredStoryHeight += p_height
+ else:
+ printStory.append((p, p_height))
+ printStoryHeight += p_height
+ neededHeight = printStoryHeight + padTop +
padBottom
+
+ if vAnchor == "top":
+ y = y - neededHeight
+ elif vAnchor == "middle":
+ y = y - (neededHeight / 2)
+
## Set canvas props based on our props:
c.translate(x, y)
c.rotate(rotation)
@@ -1031,79 +1059,17 @@
else:
boundary = 0
- story = []
-
- styles_ = styles.getSampleStyleSheet()
-
- objects = obj["Objects"]
- story = []
- for fobject in objects:
- objNeededHeight = 0
-
- t = fobject.__class__.__name__
- s = styles_[fobject.getProp("style")]
- expr = fobject.getProp("expr",
returnException=True)
-
- if isinstance(s, basestring):
- expr = expr.encode(self.Encoding)
- else:
- expr = unicode(expr)
- s = copy.deepcopy(s)
-
- if fobject.has_key("fontSize"):
- s.fontSize = fobject.getProp("fontSize")
-
- if fobject.has_key("fontName"):
- s.fontName = fobject.getProp("fontName")
-
- if fobject.has_key("leading"):
- s.leading = fobject.getProp("leading")
-
- if fobject.has_key("spaceAfter"):
- s.spaceAfter =
fobject.getProp("spaceAfter")
-
- if fobject.has_key("spaceBefore"):
- s.spaceBefore =
fobject.getProp("spaceBefore")
-
- if fobject.has_key("leftIndent"):
- s.leftIndent =
fobject.getProp("leftIndent")
-
- if fobject.has_key("firstLineIndent"):
- s.firstLineIndent =
fobject.getProp("firstLineIndent")
-
- if t.lower() == "paragraph":
- paras = expr.split("\n")
- for para in paras:
- if len(para) == 0:
- # Blank line
- p = platypus.Spacer(0,
s.leading)
- else:
- def escapePara(para):
- words =
para.split(" ")
- for idx, word
in enumerate(words):
- if "&"
in word and ";" not in word:
-
word = word.replace("&", "&")
- if "<"
in word and ">" not in word:
-
word = word.replace("<", "<")
-
words[idx] = word
- return "
".join(words)
- para = escapePara(para)
- p = ParaClass(para, s)
- story.append(p)
- objNeededHeight +=
p.wrap(columnWidth-padLeft-padRight, None)[1]
-
- neededHeight = max(neededHeight,
objNeededHeight) + padTop + padBottom
-
for columnIndex in range(columnCount):
- f = platypus.Frame(columnIndex*columnWidth, 0,
columnWidth, height, leftPadding=padLeft,
+ f = platypus.Frame(columnIndex*columnWidth, 0,
columnWidth, neededHeight, leftPadding=padLeft,
rightPadding=padRight,
topPadding=padTop,
bottomPadding=padBottom,
id=frameId,
showBoundary=boundary)
- if getNeededHeight:
- obj["calculatedHeight"] = "%s" %
neededHeight
- else:
- f.addFromList(story, c)
+ f.addFromList([s[0] for s in printStory], c)
+ deferred = deferredStory
+ neededHeight = deferredStoryHeight
+
+
elif objType == "Image":
borderWidth = self.getPt(obj.getProp("borderWidth"))
borderColor = obj.getProp("borderColor")
@@ -1252,9 +1218,90 @@
## rotating, scaling, etc. are cumulative, not absolute and we
don't want
## to start with a canvas in an unknown state.)
c.restoreState()
- return neededHeight
+ return deferred, neededHeight
+ def getStory(self, obj):
+ width = self.getPt(obj.getProp("width"))
+ padLeft = self.getPt(obj.getProp("padLeft"))
+ padRight = self.getPt(obj.getProp("padRight"))
+ padTop = self.getPt(obj.getProp("padTop"))
+ padBottom = self.getPt(obj.getProp("padBottom"))
+ columnCount = obj.getProp("columnCount")
+ columnWidth = width/columnCount
+
+ styles_ = styles.getSampleStyleSheet()
+
+ objects = obj["Objects"]
+ story = []
+ for fobject in objects:
+ objNeededHeight = 0
+
+ t = fobject.__class__.__name__
+ s = styles_[fobject.getProp("style")]
+ expr = fobject.getProp("expr", returnException=True)
+
+ if isinstance(s, basestring):
+ expr = expr.encode(self.Encoding)
+ else:
+ expr = unicode(expr)
+ s = copy.deepcopy(s)
+
+ if fobject.has_key("fontSize"):
+ s.fontSize = fobject.getProp("fontSize")
+
+ if fobject.has_key("fontName"):
+ s.fontName = fobject.getProp("fontName")
+
+ if fobject.has_key("leading"):
+ s.leading = fobject.getProp("leading")
+
+ if fobject.has_key("spaceAfter"):
+ s.spaceAfter = fobject.getProp("spaceAfter")
+
+ if fobject.has_key("spaceBefore"):
+ s.spaceBefore = fobject.getProp("spaceBefore")
+
+ if fobject.has_key("leftIndent"):
+ s.leftIndent = fobject.getProp("leftIndent")
+
+ if fobject.has_key("firstLineIndent"):
+ s.firstLineIndent =
fobject.getProp("firstLineIndent")
+
+ if t.lower() == "paragraph":
+ paras = expr.splitlines()
+ for idx, para in enumerate(paras):
+ if len(para) == 0:
+ # Blank line
+ p = platypus.Spacer(0,
s.leading)
+ else:
+ def escapePara(para):
+ words = para.split(" ")
+ for idx, word in
enumerate(words):
+ if "&" in word
and ";" not in word:
+ word =
word.replace("&", "&")
+ if "<" in word
and ">" not in word:
+ word =
word.replace("<", "<")
+ words[idx] =
word
+ return " ".join(words)
+ para = escapePara(para)
+ p = ParaClass(para, s)
+ p_height =
p.wrap(columnWidth-padLeft-padRight, None)[1]
+ objNeededHeight += p_height
+ story.append((p, p_height))
+
+ def hackDeferredPara():
+ """When a paragraph wraps to the next
page, the last line won't print if this isn't done."""
+ append_p = ParaClass("Hack: see
hackDeferredPara() in reportWriter.py", s)
+ p_height = p.wrap(99999, None)[1]
+ story.append((p, p_height))
+ if paras:
+ hackDeferredPara()
+
+ neededHeight = objNeededHeight + padTop + padBottom
+ return story, neededHeight
+
+
def getColorTupleFromReportLab(self, val):
"""Given a color tuple in reportlab format (values between 0
and 1), return
a color tuple in 0-255 format."""
@@ -1323,6 +1370,7 @@
the PDF file will be left open so that additional pages can be
added
with another call, perhaps after creating a different report
form.
"""
+ self._calcObjectHeights = {}
_form = self.ReportForm
if _form is None:
raise ValueError("ReportForm must be set first.")
@@ -1403,7 +1451,7 @@
self.Variables[varName] = vv["value"]
- def printBand(band, y=None, group=None):
+ def printBand(band, y=None, group=None, deferred=None):
"""Generic function for printing any band."""
_form = self.ReportForm
@@ -1425,6 +1473,12 @@
# print workingPageWidth / 72, columnWidth / 72
# print columnWidth, columnCount
+ pf = _form.get("pageFooter")
+ if pf is None:
+ pfHeight = 0
+ else:
+ pfHeight = self.getPt(pf.getProp("Height"))
+
if y is None:
y = pageHeaderOrigin[1]
@@ -1439,33 +1493,38 @@
self.ReportForm.Bands[band] = CaselessDict()
- height = bandDict.getProp("Height")
- if height is not None:
- height = self.getPt(height)
- else:
- # figure out height based on the objects in the
band.
- height = self.calculateBandHeight(bandDict)
+ bandHeight = self.getBandHeight(bandDict)
+ if not deferred:
+ y -= bandHeight
- y = y - height
width = pageWidth - ml - mr
- # Non-detail band special cases:
- if band == "pageHeader":
- x,y = pageHeaderOrigin
- elif band == "pageFooter":
- x,y = pageFooterOrigin
- elif band in ("pageBackground", "pageForeground"):
- x,y = 0,1
- width, height = pageWidth-1, pageHeight-1
+ # Set this property as quickly as possible as other
properties (y, for example)
+ # could depend on it.
+ self.ReportForm.Bands[band]["Height"] = bandHeight
+ def getTotalBandHeight():
+ maxBandHeight = bandHeight
+ if deferred:
+ for obj, obj_deferred, neededHeight in
deferred:
+ needed = neededHeight
+ maxBandHeight =
max(maxBandHeight, neededHeight)
+ else:
+ for obj in bandDict.get("Objects", []):
+ if obj.getProp("Height") is
None:
+ story =
self.getStory(obj)
+ storyheight = story[1]
+ needed = storyheight +
bandHeight - self.getPt(obj.getProp("y")) ## y could be dep. on band height.
+
+ maxBandHeight =
max(maxBandHeight, needed)
+ availableHeight = y - (pageFooterOrigin[1] +
pfHeight)
+ if maxBandHeight > availableHeight:
+ maxBandHeight = availableHeight
+ return maxBandHeight
- pf = _form.get("pageFooter")
- if pf is None:
- pfHeight = 0
- else:
- pfHeight = self.getPt(pf.getProp("Height"))
+ maxBandHeight = getTotalBandHeight()
- if band in ("detail", "groupHeader", "groupFooter"):
+ if band in ("groupHeader", "groupFooter", "detail"):
extraHeight = 0
if band == "groupHeader":
# Also account for the height of the
first detail record: don't print the
@@ -1473,52 +1532,121 @@
# printed as well. Actually, this
should be reworked so that any subsequent
# group header records get accounted
for as well...
b = _form["detail"]
- extraHeight = b.get("Height")
- if extraHeight is None:
- extraHeight =
b.AvailableProps["Height"]["default"]
- else:
- extraHeight = eval(extraHeight)
- if extraHeight is None:
- extraHeight =
self.calculateBandHeight(b)
- else:
- extraHeight =
self.getPt(extraHeight)
- if y < pageFooterOrigin[1] + pfHeight +
extraHeight:
+ extraHeight = self.getBandHeight(b)
+
+ check = pageFooterOrigin[1] + pfHeight +
extraHeight
+ if bandDict.getProp("height") is not None:
+ # band height is fixed, won't flow to
next page.
+ check += bandHeight
+
+ if y < check:
if self._currentColumn >= columnCount-1:
endPage()
beginPage()
else:
self._currentColumn += 1
y = pageHeaderOrigin[1]
+ maxBandHeight = getTotalBandHeight()
if band == "detail":
y = reprintGroupHeaders(y)
- y = y - height
+ if not deferred:
+ y -= bandHeight
+ # Non-detail band special cases:
+ if band == "pageHeader":
+ x,y = pageHeaderOrigin
+ elif band == "pageFooter":
+ x,y = pageFooterOrigin
+ elif band in ("pageBackground", "pageForeground"):
+ x,y = 0,1
+ width, height = pageWidth-1, pageHeight-1
+
x = ml + (self._currentColumn * columnWidth)
self.ReportForm.Bands[band]["x"] = x
self.ReportForm.Bands[band]["y"] = y
self.ReportForm.Bands[band]["Width"] = width
- self.ReportForm.Bands[band]["Height"] = height
-
+ self.ReportForm.Bands[band]["TotalHeight"] =
maxBandHeight
+
if self.ShowBandOutlines:
self.printBandOutline("%s (record %s)" % (band,
self.RecordNumber),
- x, y, width, height)
+ x, y, width, bandHeight)
- if bandDict.has_key("Objects"):
- for obj in bandDict["Objects"]:
- show = obj.getProp("show",
returnException=True)
- if show == False:
- continue
+ del_deferred_idxs = []
+ objects = bandDict.get("Objects", [])
+ was_deferred = False
+ if deferred:
+ was_deferred = True
+ objects = deferred
+ for idx, obj in enumerate(objects):
+ if isinstance(obj, tuple):
+ # deferred (obj, obj_deferred)
+ obj, obj_deferred, neededHeight = obj
+ else:
+ obj_deferred = None
+ neededHeight = 0
+ show = obj.getProp("show", returnException=True)
+ if show == False:
+ continue
- x1 = self.getPt(obj.getProp("x"))
- y1 = self.getPt(obj.getProp("y"))
- x1 = x + x1
+ x1 = self.getPt(obj.getProp("x"))
+ y1 = obj_y = self.getPt(obj.getProp("y"))
+ x1 = x + x1
+ if obj_deferred:
+ y1 = y
+ else:
y1 = y + y1
- self.draw(obj, (x1, y1))
-
- return y
+ availableHeight = y - (pageFooterOrigin[1] +
pfHeight)
+ obj_height = obj.getProp("height")
+ if obj_height is not None:
+ obj_height = self.getPt(obj_height)
+ if availableHeight > obj_height:
+ availableHeight = obj_height
+ if bandDict.getProp("height") is not None:
+ if availableHeight > obj_y:
+ availableHeight = obj_y
+ #availableHeight = min(availableHeight,
bandHeight+y)
+ new_obj_deferred, neededHeight = self.draw(obj,
(x1, y1), availableHeight=availableHeight,
+ deferred=obj_deferred)
+ if bandDict.getProp("height") is not None:
+ # Band height is fixed; cancel any
deferrals.
+ new_obj_deferred = None
+
+ if new_obj_deferred:
+ if obj_deferred:
+ # was already deferred, and now
deferred again. WARNING: if para longer
+ # than a page, we'll recurse
forever. FIXME.
+ deferred[idx] = (obj,
new_obj_deferred, neededHeight)
+ else:
+ # new deferral.
+ if deferred is None:
+ deferred = []
+ deferred.append((obj,
new_obj_deferred, neededHeight))
+ else:
+ if obj_deferred:
+ # need to delete the old
deferral
+ del_deferred_idxs.append(idx)
+
+ del_deferred_idxs.sort(reverse=True)
+ for idx in del_deferred_idxs:
+ del(deferred[idx])
+
+ if was_deferred and not deferred:
+ # just printed the last page of deferreds
+ return y - maxBandHeight
+
+ elif deferred:
+ # the deferred objs will print on the next
page. RECURSE WARNING.
+ dy = printBand(band=band, y=-1, group=group,
deferred=deferred)
+ return dy
+ else:
+ # no deferreds ever involved
+ y -= (maxBandHeight-bandHeight)
+ return y
+
+
def beginPage():
# Print the static bands that appear below detail in
z-order:
self._pageNumber += 1
@@ -1597,10 +1725,8 @@
brandNewPage = True ## don't
start multiple new pages
y = printBand("groupHeader", y, group)
-
# print the detail band:
y = printBand("detail", y)
-
self._recordNumber += 1
@@ -1616,27 +1742,34 @@
self._canvas = None
- def calculateBandHeight(self, bandDict):
- maxHeight = 0
- if bandDict.has_key("Objects"):
- for obj in bandDict["Objects"]:
- y = self.getPt(obj.getProp("y"))
+ def getBandHeight(self, bandDict):
+ """Return the height of the band.
- ht = obj.getProp("Height")
- if ht is None:
- ht = self.calculateObjectHeight(obj)
- ht = self.getPt(ht)
+ If the band's Height property is None, the height will be
+ calculated based on the objects in the band.
+ """
- thisHeight = y + ht
- maxHeight = max(thisHeight, maxHeight)
- return maxHeight
+ bandHeight = bandDict.getProp("Height")
+ if bandHeight is not None:
+ # explicitly-set height
+ return self.getPt(bandHeight)
-
- def calculateObjectHeight(self, obj):
- neededHeight = self.draw(obj, (0,0), getNeededHeight=True)
- return neededHeight
+ # dynamic height: figure out based on the objects in the band.
+ bandHeight = 0
+ objects = bandDict.get("objects", [])
+ for obj in objects:
+ obj_y = self.getPt(obj.getProp("y"))
+ obj_ht = obj.getProp("Height")
+ if obj_ht is None:
+ continue
+ # object height is fixed.
+ obj_ht = self.getPt(obj_ht)
+ thisHeight = obj_y + obj_ht
+ bandHeight = max(thisHeight, bandHeight)
+ return bandHeight
+
def getPageSize(self):
## Set the Page Size:
# get the string pageSize value from the spec file:
Copied: trunk/dabo/lib/reporting_tests (from rev 5222,
branches/paul_reporting/dabo/lib/reporting_tests)
Copied: trunk/dabo/lib/reporting_tests/invoice_demo (from rev 5222,
branches/paul_reporting/dabo/lib/reporting_tests/invoice_demo)
Deleted: trunk/dabo/lib/reporting_tests/invoice_demo/bizcard01_medium.jpg
Copied: trunk/dabo/lib/reporting_tests/invoice_demo/bizcard01_medium.jpg (from
rev 5222,
branches/paul_reporting/dabo/lib/reporting_tests/invoice_demo/bizcard01_medium.jpg)
===================================================================
(Binary files differ)
Deleted: trunk/dabo/lib/reporting_tests/invoice_demo/invoice.py
Copied: trunk/dabo/lib/reporting_tests/invoice_demo/invoice.py (from rev 5222,
branches/paul_reporting/dabo/lib/reporting_tests/invoice_demo/invoice.py)
===================================================================
--- trunk/dabo/lib/reporting_tests/invoice_demo/invoice.py
(rev 0)
+++ trunk/dabo/lib/reporting_tests/invoice_demo/invoice.py 2009-05-18
02:52:14 UTC (rev 5223)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+This demo uses a copy of my invoice report with bogus data to show how to
+interact with the ReportWriter to create a PDF. Run this script to create
+invoice.pdf. To modify the report form, use daboide/ReportDesigner to open
+the invoice.rfxml file.
+"""
+
+from dabo.lib.reportWriter import ReportWriter
+from dabo.lib import reportUtils
+
+outFile = "invoice.pdf"
+
+print "Instantiating the report writer..."
+rw = ReportWriter()
+
+# Set some required properties:
+rw.ReportFormFile = "invoice.rfxml"
+rw.OutputFile = "%s" % outFile
+# (Normally, you'd also set the Cursor property, to provide the data,
+# but this sample just uses the test cursor inside the report form.)
+
+# Set some optional properties:
+rw.UseTestCursor = True
+rw.ShowBandOutlines = True
+
+print "Writing %s..." % outFile
+rw.write()
+
+print "Trying to preview it..."
+reportUtils.previewPDF(outFile)
Deleted: trunk/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml
Copied: trunk/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml (from rev
5222,
branches/paul_reporting/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml)
===================================================================
--- trunk/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml
(rev 0)
+++ trunk/dabo/lib/reporting_tests/invoice_demo/invoice.rfxml 2009-05-18
02:52:14 UTC (rev 5223)
@@ -0,0 +1,695 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+
+<!--
+ This is a Dabo report form xml (rfxml) document, describing a
+ report form. It was generated by the Dabo Report Writer, and can
+ be edited by hand or by using the Dabo Report Designer.
+-->
+
+<Report>
+ <title>Invoice</title>
+
+ <page>
+ <MarginBottom>".4 in"</MarginBottom>
+ <marginLeft>".75 in"</marginLeft>
+ <marginRight>".75 in"</marginRight>
+ <marginTop>".75 in"</marginTop>
+ <orientation>"portrait"</orientation>
+ <size>"letter"</size>
+ </page>
+
+ <variables>
+ <Variable>
+ <expr>self.Variables['clientBalance'] +
self.Record['amount']</expr>
+ <initialValue>decimal.Decimal('0.00')</initialValue>
+ <name>clientBalance</name>
+ <resetAt>self.Record['clientid']</resetAt>
+ </Variable>
+ <Variable>
+ <expr>self.Variables['timeCodeBalance'] +
self.Record['amount']</expr>
+ <initialValue>decimal.Decimal('0.00')</initialValue>
+ <name>timeCodeBalance</name>
+ <resetAt>"%s-%s" % (self.Record['clientid'],
self.Record['timecodeid'])</resetAt>
+ </Variable>
+ <Variable>
+ <expr>self.Variables['recordsPrinted_timecode'] +
1</expr>
+ <initialValue>decimal.Decimal('0.00')</initialValue>
+ <name>recordsPrinted_timecode</name>
+ <resetAt>"%s-%s" % (self.Record['clientid'],
self.Record['timecodeid'])</resetAt>
+ </Variable>
+ </variables>
+
+ <groups>
+ <Group>
+ <expr>self.clientid</expr>
+ <ResetPageNumber>True</ResetPageNumber>
+ <startOnNewPage>True</startOnNewPage>
+ <groupHeader>
+ <Height>"1.75 in"</Height>
+ <objects>
+ <String>
+ <expr>self.clientname</expr>
+ <fontSize>14</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <Height>'14.0 pt'</Height>
+ <width>"6 in"</width>
+ <x>"0 in"</x>
+ <y>'1.5497 in'</y>
+ </String>
+ <String>
+ <expr>self.clientaddress1</expr>
+ <fontSize>14</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <Height>'14.0 pt'</Height>
+ <width>"6 in"</width>
+ <x>"0 in"</x>
+ <y>'1.3297 in'</y>
+ </String>
+ <String>
+ <expr>self.clientaddress2</expr>
+ <fontSize>14</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <Height>'14.0 pt'</Height>
+ <width>"6 in"</width>
+ <x>"0 in"</x>
+ <y>'1.1097 in'</y>
+ </String>
+ <String>
+ <expr>self.clientaddress3</expr>
+ <fontSize>14</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <Height>'14.0 pt'</Height>
+ <width>"6 in"</width>
+ <x>"0 in"</x>
+ <y>'0.9012 in'</y>
+ </String>
+ </objects>
+ </groupHeader>
+ <groupFooter>
+ <height>"0.25 in"</height>
+ <objects>
+ <String>
+ <align>"right"</align>
+ <expr>"Amount due: $
%s" % locale.format('%.2f', self.clientBalance, True)</expr>
+ <fontSize>14</fontSize>
+ <hAnchor>"Right"</hAnchor>
+ <height>20</height>
+ <width>"6 in"</width>
+ <x>"6.925 in"</x>
+ <y>"0 in"</y>
+ </String>
+ </objects>
+ </groupFooter>
+ </Group>
+ <Group>
+ <expr>self.subclientid</expr>
+ <ReprintHeaderOnNewPage>True</ReprintHeaderOnNewPage>
+ <GroupHeader>
+ <Height>28.8</Height>
+ <Objects>
+ <Rectangle>
+ <fillColor>(.9, .9,
.9)</fillColor>
+ <height>".25 in"</height>
+ <strokeWidth>".25
pt"</strokeWidth>
+ <width>"7 in"</width>
+ <x>"0 in"</x>
+ <y>"0 in"</y>
+ </Rectangle>
+ <String>
+ <expr>self.subclientname</expr>
+ <Height>'11.0 pt'</Height>
+ <width>'165.0 pt'</width>
+ <y>'20.0 pt'</y>
+ </String>
+ <Line>
+ <height>"0.25 in"</height>
+ <LineSlant>"|"</LineSlant>
+
<strokeColor>(.2,.2,.2)</strokeColor>
+ <strokeWidth>0.25</strokeWidth>
+ <width>2</width>
+ <x>".575 in"</x>
+ <y>0</y>
+ </Line>
+ <Line>
+ <height>"0.25 in"</height>
+ <LineSlant>"|"</LineSlant>
+
<strokeColor>(.2,.2,.2)</strokeColor>
+ <strokeWidth>0.25</strokeWidth>
+ <width>2</width>
+ <x>"5.25 in"</x>
+ <y>0</y>
+ </Line>
+ <Line>
+ <height>"0.25 in"</height>
+ <LineSlant>"|"</LineSlant>
+
<strokeColor>(.2,.2,.2)</strokeColor>
+ <strokeWidth>0.25</strokeWidth>
+ <width>2</width>
+ <x>"5.78 in"</x>
+ <y>0</y>
+ </Line>
+ <Line>
+ <height>"0.25 in"</height>
+ <LineSlant>"|"</LineSlant>
+
<strokeColor>(.2,.2,.2)</strokeColor>
+ <strokeWidth>0.25</strokeWidth>
+ <width>2</width>
+ <x>"6.3 in"</x>
+ <y>0</y>
+ </Line>
+ <String>
+ <expr>"Date"</expr>
+ <Height>'11.0 pt'</Height>
+ <width>'0.5305 in'</width>
+ <x>"0.025 in"</x>
+ <y>5</y>
+ </String>
+ <String>
+ <expr>"Description"</expr>
+ <Height>'11.0 pt'</Height>
+ <width>"4.6225 in"</width>
+ <x>"0.6075 in"</x>
+ <y>5</y>
+ </String>
+ <String>
+ <align>"right"</align>
+ <expr>"Hours"</expr>
+ <hAnchor>"right"</hAnchor>
+ <Height>'11.0 pt'</Height>
+ <width>'0.4166 in'</width>
+ <x>"5.6975 in"</x>
+ <y>5</y>
+ </String>
+ <String>
+ <align>"right"</align>
+ <expr>"Rate"</expr>
+ <hAnchor>"right"</hAnchor>
+ <Height>'11.0 pt'</Height>
+ <width>'0.4166 in'</width>
+ <x>"6.2225 in"</x>
+ <y>5</y>
+ </String>
+ <String>
+ <align>"right"</align>
+ <expr>"Amount"</expr>
+ <hAnchor>"right"</hAnchor>
+ <Height>'11.0 pt'</Height>
+ <width>'0.5832 in'</width>
+ <x>"6.925 in"</x>
+ <y>5</y>
+ </String>
+ </Objects>
+ </GroupHeader>
+ <GroupFooter>
+ <Objects></Objects>
+ </GroupFooter>
+ </Group>
+ <Group>
+ <expr>self.timecodeid</expr>
+ <reprintHeaderOnNewPage>True</reprintHeaderOnNewPage>
+ <startOnNewPage>False</startOnNewPage>
+ <groupHeader>
+ <height>"0.25 in"</height>
+ <objects>
+ <Rectangle>
+ <fillColor>(.3, .3,
.3)</fillColor>
+ <height>".25 in"</height>
+ <strokeWidth>".25
pt"</strokeWidth>
+ <width>"7 in"</width>
+ <x>"0 in"</x>
+ <y>0</y>
+ </Rectangle>
+ <String>
+ <align>"center"</align>
+ <expr>"%s: %s%s" %
(self.timecode, self.timecodename, self.recordsPrinted_timecode > 1 and "
(continued)" or "")</expr>
+ <fontColor>(1,1,1)</fontColor>
+ <fontItalic>True</fontItalic>
+ <height>15</height>
+ <width>"7 in"</width>
+ <x>"0.0 in"</x>
+ <y>5</y>
+ </String>
+ </objects>
+ </groupHeader>
+ <groupFooter>
+ <height>"0.25 in"</height>
+ <objects>
+ <Rectangle>
+ <height>".25 in"</height>
+ <strokeWidth>".25
pt"</strokeWidth>
+ <width>"7 in"</width>
+ <x>"0 in"</x>
+ <y>1</y>
+ </Rectangle>
+ <String>
+ <align>"right"</align>
+ <expr>"Total for code %s:
$ %s" % (self.Record["timecode"], locale.format('%.2f',
self.Variables["timeCodeBalance"], True))</expr>
+ <fontItalic>True</fontItalic>
+ <hAnchor>"right"</hAnchor>
+ <height>15</height>
+ <width>"6.5 in"</width>
+ <x>"6.925 in"</x>
+ <y>6</y>
+ </String>
+ </objects>
+ </groupFooter>
+ </Group>
+ </groups>
+
+ <pageBackground>
+ <objects>
+ <Rectangle>
+ <height>.25</height>
+ <strokeColor>(.7,.7,.7)</strokeColor>
+ <width>.25</width>
+ <x>".25 in"</x>
+ <y>"6.875 in"</y>
+ </Rectangle>
+ <Rectangle>
+ <height>.25</height>
+ <strokeColor>(.7,.7,.7)</strokeColor>
+ <width>.25</width>
+ <x>"6.75 in"</x>
+ <y>"6.875 in"</y>
+ </Rectangle>
+ </objects>
+ </pageBackground>
+
+ <pageHeader>
+ <height>"1.75 in"</height>
+ <Objects>
+ <Rectangle>
+ <fillColor>(1, 1, 1)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".5 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"3 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1</x>
+ <y>"0.65 in"</y>
+ </Rectangle>
+ <Rectangle>
+ <fillColor>(1, 1, 1)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".3 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1</x>
+ <y>"0.65 in"</y>
+ </Rectangle>
+ <Image>
+ <borderColor>(.7, .7, .7)</borderColor>
+ <borderWidth>0</borderWidth>
+ <expr>"logo_2cannisters_medium.jpg"</expr>
+ <height>"1.22 in"</height>
+ <mode>"scale"</mode>
+ <width>"0.95 in"</width>
+ <x>"0 in"</x>
+ <y>"0.6375 in"</y>
+ </Image>
+ <String>
+ <align>"left"</align>
+ <borderWidth>"0 pt"</borderWidth>
+ <expr>"Paul McNett"</expr>
+ <fontBold>True</fontBold>
+ <FontName>"Helvetica"</FontName>
+ <fontSize>14</fontSize>
+ <hAnchor>"left"</hAnchor>
+ <height>16</height>
+ <name>companyName</name>
+ <vAlign>"top"</vAlign>
+ <vAnchor>"top"</vAnchor>
+ <Width>"2.875 in"</Width>
+ <x>"1.1 in"</x>
+ <y>"1.7875 in"</y>
+ </String>
+ <String>
+ <align>"left"</align>
+ <borderWidth>"0 pt"</borderWidth>
+ <expr>"881 B Street"</expr>
+ <fontName>"Helvetica"</fontName>
+ <fontSize>14</fontSize>
+ <hAnchor>"left"</hAnchor>
+ <height>16</height>
+ <name>companyAddress1</name>
+ <vAlign>"top"</vAlign>
+ <vAnchor>"top"</vAnchor>
+ <Width>"2.875 in"</Width>
+ <x>"1.1 in"</x>
+ <y>"1.5575 in"</y>
+ </String>
+ <String>
+ <align>"left"</align>
+ <borderWidth>"0 pt"</borderWidth>
+ <expr>"Hollister, CA 95023"</expr>
+ <fontName>"Helvetica"</fontName>
+ <fontSize>14</fontSize>
+ <hAnchor>"left"</hAnchor>
+ <height>16</height>
+ <name>companyAddress1</name>
+ <vAlign>"top"</vAlign>
+ <vAnchor>"top"</vAnchor>
+ <Width>"2.875 in"</Width>
+ <x>"1.1 in"</x>
+ <y>"1.3275 in"</y>
+ </String>
+ <String>
+ <align>"left"</align>
+ <borderWidth>"0 pt"</borderWidth>
+ <expr>"(831) 636-9900"</expr>
+ <fontName>"Helvetica"</fontName>
+ <fontSize>14</fontSize>
+ <hAnchor>"left"</hAnchor>
+ <height>16</height>
+ <name>companyAddress1</name>
+ <vAlign>"top"</vAlign>
+ <vAnchor>"top"</vAnchor>
+ <Width>"2.875 in"</Width>
+ <x>"1.1 in"</x>
+ <y>"1.0975 in"</y>
+ </String>
+ <String>
+ <align>"right"</align>
+ <borderWidth>"0 pt"</borderWidth>
+ <expr>"Invoice"</expr>
+ <fontName>"Helvetica"</fontName>
+ <fontSize>36</fontSize>
+ <hAnchor>"right"</hAnchor>
+ <height>30</height>
+ <name>title</name>
+ <vAlign>"top"</vAlign>
+ <vAnchor>"top"</vAnchor>
+ <width>'2.9992 in'</width>
+ <x>self.Bands["pageHeader"]["width"]-1</x>
+ <y>self.Bands["pageHeader"]["height"]</y>
+ </String>
+ <Rectangle>
+ <fillColor>(.9, .9, .9)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".2 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1</x>
+ <y>"0.95 in"</y>
+ </Rectangle>
+ <String>
+ <align>"center"</align>
+ <expr>"Due"</expr>
+ <hAnchor>"right"</hAnchor>
+ <height>15</height>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1</x>
+ <y>"1 in"</y>
+ </String>
+ <String>
+ <align>"center"</align>
+ <expr>"%s %s %s" % (self.duedate.day,
self.duedate.strftime("%b"), self.duedate.year)</expr>
+ <fontSize>12</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <height>15</height>
+ <width>"1 in"</width>
+ <x>'430.0 pt'</x>
+ <y>'0.7500 in'</y>
+ </String>
+ <Rectangle>
+ <fillColor>(.9, .9, .9)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".2 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72</x>
+ <y>"0.95 in"</y>
+ </Rectangle>
+ <String>
+ <align>"center"</align>
+ <expr>"Date"</expr>
+ <hAnchor>"right"</hAnchor>
+ <height>15</height>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72</x>
+ <y>"1 in"</y>
+ </String>
+ <Rectangle>
+ <fillColor>(1, 1, 1)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".3 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72</x>
+ <y>"0.65 in"</y>
+ </Rectangle>
+ <String>
+ <align>"center"</align>
+ <expr>"%s %s %s" % (self.invdate.day,
self.invdate.strftime("%b"), self.invdate.year)</expr>
+ <fontSize>12</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <height>15</height>
+ <width>"1 in"</width>
+ <x>'359.0 pt'</x>
+ <y>'0.7500 in'</y>
+ </String>
+ <Rectangle>
+ <fillColor>(.9, .9, .9)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".2 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72 -
72</x>
+ <y>"0.95 in"</y>
+ </Rectangle>
+ <String>
+ <align>"center"</align>
+ <expr>"Invoice #"</expr>
+ <hAnchor>"right"</hAnchor>
+ <height>15</height>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72 -
72</x>
+ <y>"1 in"</y>
+ </String>
+ <Rectangle>
+ <fillColor>(1, 1, 1)</fillColor>
+ <hAnchor>"right"</hAnchor>
+ <height>".3 in"</height>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"1 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72 -
72</x>
+ <y>"0.65 in"</y>
+ </Rectangle>
+ <String>
+ <align>"center"</align>
+ <expr>self.invnum</expr>
+ <fontSize>12</fontSize>
+ <hAnchor>"Left"</hAnchor>
+ <height>15</height>
+ <width>"1 in"</width>
+ <x>'287.0 pt'</x>
+ <y>'0.7500 in'</y>
+ </String>
+ <String>
+ <align>"center"</align>
+ <expr>"COPY"</expr>
+ <fontColor>(.7, .1, .1)</fontColor>
+ <fontSize>24</fontSize>
+ <hAnchor>"center"</hAnchor>
+ <height>"1 in"</height>
+ <rotation>33</rotation>
+ <show>self.Record["copy"] == True</show>
+ <width>"2 in"</width>
+ <x>self.Bands["pageHeader"]["width"]-1 - 72 -
72 - 60</x>
+ <y>"0.64 in"</y>
+ </String>
+ </Objects>
+ </pageHeader>
+
+ <detail>
+ <height>None</height>
+ <Objects>
+ <Rectangle>
+
<height>self.Bands["detail"]["TotalHeight"]</height>
+ <name>test2</name>
+ <strokeWidth>".25 pt"</strokeWidth>
+ <width>"7 in"</width>
+ <x>"0 in"</x>
+ <y>self.Bands["detail"]["Height"]</y>
+ <vAnchor>"top"</vAnchor>
+ </Rectangle>
+ <String>
+ <expr>"%s %s" % (self.Record["date"].day,
self.Record["date"].strftime("%b"))</expr>
+ <height>12</height>
+ <vAnchor>"top"</vAnchor>
+ <width>"0.60 in"</width>
+ <x>"0.025 in"</x>
+ <y>self.Bands["detail"]["height"]</y>
+ </String>
+ <Frameset>
+ <borderWidth>0</borderWidth>
+ <height>None</height>
+ <padBottom>4</padBottom>
+ <padTop>2</padTop>
+ <vAnchor>"top"</vAnchor>
+ <width>"4.6225 in"</width>
+ <x>"0.6075 in"</x>
+ <y>self.Bands["detail"]["height"]</y>
+ <objects>
+ <Paragraph>
+
<expr>self.Record["invdesc"]</expr>
+ <fontName>"Helvetica"</fontName>
+ </Paragraph>
+ </objects>
+ </Frameset>
+ <String>
+ <align>"right"</align>
+ <expr>self.Record["hours"]</expr>
+ <hAnchor>"right"</hAnchor>
+ <height>12</height>
+ <show>self.Record["mode"] == 1</show>
+ <vAnchor>"top"</vAnchor>
+ <width>'0.4166 in'</width>
+ <x>"5.6975 in"</x>
+ <y>self.Bands["detail"]["height"]</y>
+ </String>
+ <String>
+ <align>"right"</align>
+ <expr>"$%s" % int(self.Record["rate"])</expr>
+ <hAnchor>"right"</hAnchor>
+ <height>12</height>
+ <show>self.Record["mode"] == 1</show>
+ <vAnchor>"top"</vAnchor>
+ <width>'0.4166 in'</width>
+ <x>"6.2225 in"</x>
+ <y>self.Bands["detail"]["height"]</y>
+ </String>
+ <String>
+ <align>"right"</align>
+ <expr>locale.format("%.2f",
self.Record["amount"], True)</expr>
+ <hAnchor>"right"</hAnchor>
+ <height>12</height>
+ <vAnchor>"top"</vAnchor>
+ <width>'0.5832 in'</width>
+ <x>"6.925 in"</x>
+ <y>self.Bands["detail"]["height"]</y>
+ </String>
+ <Line>
+ <height>self.Ban
(32312 bytes were truncated as it was too long for the email (max 40000 bytes.)
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message:
http://leafe.com/archives/byMID/[email protected]