[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
r6447 - in trunk/gnue-common/src/printing: . pdftable
From: |
jamest |
Subject: |
r6447 - in trunk/gnue-common/src/printing: . pdftable |
Date: |
Sun, 3 Oct 2004 20:04:16 -0500 (CDT) |
Author: jamest
Date: 2004-10-03 20:04:15 -0500 (Sun, 03 Oct 2004)
New Revision: 6447
Added:
trunk/gnue-common/src/printing/pdftable/
trunk/gnue-common/src/printing/pdftable/README
trunk/gnue-common/src/printing/pdftable/__init__.py
trunk/gnue-common/src/printing/pdftable/pdftable.py
trunk/gnue-common/src/printing/pdftable/sample.py
Log:
Adding pdftable tabular report generator to common
And sample code showing how to use pdftable
Added: trunk/gnue-common/src/printing/pdftable/README
===================================================================
--- trunk/gnue-common/src/printing/pdftable/README 2004-10-02 11:52:06 UTC
(rev 6446)
+++ trunk/gnue-common/src/printing/pdftable/README 2004-10-04 01:04:15 UTC
(rev 6447)
@@ -0,0 +1 @@
+See sample.py for an example on how to use pdftable
Added: trunk/gnue-common/src/printing/pdftable/__init__.py
===================================================================
--- trunk/gnue-common/src/printing/pdftable/__init__.py 2004-10-02 11:52:06 UTC
(rev 6446)
+++ trunk/gnue-common/src/printing/pdftable/__init__.py 2004-10-04 01:04:15 UTC
(rev 6447)
@@ -0,0 +1,28 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2004 Free Software Foundation
+#
+# FILE:
+# pdftable/__init__.py
+#
+# DESCRIPTION:
+"""
+"""
+#
+from pdftable import *
Added: trunk/gnue-common/src/printing/pdftable/pdftable.py
===================================================================
--- trunk/gnue-common/src/printing/pdftable/pdftable.py 2004-10-02 11:52:06 UTC
(rev 6446)
+++ trunk/gnue-common/src/printing/pdftable/pdftable.py 2004-10-04 01:04:15 UTC
(rev 6447)
@@ -0,0 +1,672 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2004 Free Software Foundation
+#
+# FILE:
+# pdftable/pdftable.py
+#
+# DESCRIPTION:
+"""
+A class that creates multisection tabular pdf reports.
+"""
+#
+
+# ============================================================================
+# Imports
+# ============================================================================
+# ----------------------------------------------------------------------------
+# Python standard modules
+# ----------------------------------------------------------------------------
+import time
+
+# ----------------------------------------------------------------------------
+# Reportlab modules
+# ----------------------------------------------------------------------------
+from reportlab.lib import colors
+from reportlab.lib.units import inch
+from reportlab.lib.pagesizes import letter, landscape, portrait
+from reportlab.pdfgen import canvas
+from reportlab.pdfbase.pdfmetrics import getFont
+
+# ============================================================================
+# Module constants
+# ============================================================================
+# ----------------------------------------------------------------------------
+# Text alignment constants
+# ----------------------------------------------------------------------------
+LEFT=0
+RIGHT=1
+CENTER=2
+
+# ----------------------------------------------------------------------------
+# Some sample font definitions
+#
+# TODO: This really needs handled in a more flexible mannor.
+# ----------------------------------------------------------------------------
+ # Font name Pt Size Vert Spacing
+dataFont = ('Times-Roman', 10, 11)
+subtotalFont = ('Times-Bold', 10, 12)
+tableHeaderFont = ('Times-Bold', 10, 12)
+
+titleFont = ('Helvetica-Bold', 14, 14)
+title2Font = ('Helvetica', 12, 14)
+title3Font = ('Helvetica-Oblique',12, 14)
+repeatTitleFont = ('Helvetica-Oblique', 9, 10)
+footerFont = ('Times-Roman', 9, 10)
+
+subtitleFont = ('Times-Bold', 12, 13)
+subtitleLabelFont = ('Times-Roman', 12, 13)
+subtitleContinueFont = ('Times-Italic', 10, 13)
+
+# ----------------------------------------------------------------------------
+# Sample Color settings
+#
+# TODO: This really needs handled in a more flexible mannor.
+# ----------------------------------------------------------------------------
+highlightColor = colors.HexColor("0xffffcc")
+headingColor = subtotalColor = colors.HexColor("0xe6e6ff")
+
+
+# ----------------------------------------------------------------------------
+# Size constants
+#
+# TODO: This really needs handled in a more flexible mannor.
+# ----------------------------------------------------------------------------
+
+lineWidth = .25
+
+# NOTE: I tried not to tie internal logic to the font/color/tracking
+# values that follow, so *theoretically* you can adjust them to
+# your liking.
+leftmargin = .75*inch
+topmargin = .75*inch
+
+# This is nothing but voodoo guesses...
+# Greatly depends on pointsize of dataFont
+# TODO: This should probably be computed based on width of a "0"
+# TODO: and the width of the report. But, eh, this works for now...
+maxColsForPortraitNonscaled = 100 # Number of columns before we start
scaling down
+maxColsForPortraitScaled = 120 # Number of columns before we switch to
landscape
+maxColsForLandscapeNonscaled = 140 # Number of columns before landscape +
scaling
+
+# ============================================================================
+# Class definition
+# ============================================================================
+class pdftable:
+
+ # ==========================================================================
+ # Creation/closure functions
+ # ==========================================================================
+
+ # --------------------------------------------------------------------------
+ # Initialize
+ # --------------------------------------------------------------------------
+ def __init__(self, file, parameterBox = ()):
+ """
+ A simple table based report generator.
+
+ The class provides the following features.
+ Border support for columns, rows.
+ Automatic adjustments of the data when it is unable to fit on one page.
+ Multiple sections of a report handled automatically.
+
+ @param file: A python file handle used for output
+ @param parameterBox: Unused?
+ """
+ self._titleList = ['Your Report','Your Report']
+
+ self.file = file
+# self.parameterBox = parameterBox
+
+ self.highlightIndex = 0
+ self.init = 1
+ self._scalingComplete = 0
+ self.page = 0
+ self.continuing = 0
+
+ # Time of report session
+ self.timestamp = time.strftime("%m/%d/%Y %I:%M%p", time.localtime())
+
+ # Will be set by _beginData()
+ self.height = 0
+ self.width = 0
+ self.scale = 1.0
+
+ self._currentSectionType = "default"
+ self._currentSection = {}
+ self.definedSectionTypes = {}
+
+ self.columnGap = 6 # in pts, Amount of gap to leave between columns...
doesn't need to be too much
+
+ # --------------------------------------------------------------------------
+ # Finish up the output
+ # --------------------------------------------------------------------------
+ def close(self):
+ """
+ Finalize the report.
+
+ Should be called after all data was sent to the report.
+ """
+ self.canvas.showPage()
+ self.canvas.save()
+
+
+ # ==========================================================================
+ # Functions that effect report wide settings
+ # ==========================================================================
+
+ # --------------------------------------------------------------------------
+ # Sets the title of the report
+ # --------------------------------------------------------------------------
+ def setFullTitle(self,titleList):
+ """
+ Sets the title of the report
+
+ @param titleList: A list containing tuples of the following format
+ (text, font defintion)
+ Font defintions are also tuples in the format
+ (Font name, Pt Size, Vert Spacing) such as...
+ ('Times-Roman', 10, 11)
+ """
+ self._titleList = titleList
+
+ # --------------------------------------------------------------------------
+ # Define a column on the report
+ # --------------------------------------------------------------------------
+ def addColumn(self, align, minSize, overflow="", highlight=None,
+ leftBorder=0, rightBorder=0, sectionType="default"):
+ """
+ Defines a column on the record for the specified section
+
+ @param minSize: The minimum size in points of the column.
+ @param overflow: The text that should be printed if the contents of a
column are too
+ large for the column width.
+ @param highlight: The color of background to use in this column.
+ @param leftBorder: The width of the left side border in points.
+ @param rightBorder: The width of the right side border in points.
+ @param sectionType: The name of the section to which this column should be
added.
+ """
+ try:
+ secType = self.definedSectionTypes[sectionType]
+ except KeyError:
+ self.definedSectionTypes[sectionType] = {'columnSizes' :[],
+ 'columnAligns' :[],
+ 'columnOverflow' :[],
+ 'columnHighlight' :[],
+ 'columnCoords' :[],
+ 'columnLeftBorder' :[],
+ 'columnRightBorder':[],
+ 'headerList': [[]],
+ }
+ secType = self.definedSectionTypes[sectionType]
+
+ secType['columnSizes'].append(minSize)
+ secType['columnAligns'].append(align)
+ secType['columnOverflow'].append(overflow)
+ secType['columnHighlight'].append(highlight)
+ secType['columnLeftBorder'].append(leftBorder)
+ secType['columnRightBorder'].append(rightBorder)
+
+ # ==========================================================================
+ # Section level functions
+ # ==========================================================================
+
+ # --------------------------------------------------------------------------
+ # Define a section header
+ # --------------------------------------------------------------------------
+ def addHeader(self, heading, align, startColumn, endColumn,
+ leftBorder=0, rightBorder=0, sectionType="default"):
+ """
+ Adds a column header to one or more columns
+
+ @param heading: The text to display
+ @param align: The alignment to apply to the header text
+ LEFT, RIGHT, CENTER are defined in this module
+ @param startColumn: The starting column number (starts at 0) for the header
+ @param endColumn: The ending column number for the header
+ @param leftBorder: The width of the left side border in points.
+ @param rightBorder: The width of the right side border in points.
+ @param sectionType: The name of the section to which this header should be
added.
+ """
+ secType = self.definedSectionTypes[sectionType]
+ if endColumn > len(secType['columnSizes'])-1:
+ print "endColumn longer than defined columns"
+ sys.exit()
+
+ heading = {'text':heading,
+ 'align':align,
+ 'start':startColumn,
+ 'end':endColumn,
+ 'leftBorder':leftBorder,
+ 'rightBorder':rightBorder,
+ }
+ secType['headerList'][-1].append(heading)
+
+ # --------------------------------------------------------------------------
+ # Add a row to a header
+ # --------------------------------------------------------------------------
+ def addHeaderRow(self, sectionType="default"):
+ """
+ Adds a new row to the header. Subsequent calls to addHeader will now
+ apply to this new row.
+
+ @param sectionType: The name of the section to which this header row will
be added.
+ """
+ secType = self.definedSectionTypes[sectionType]
+ secType['headerList'].append([])
+
+ # --------------------------------------------------------------------------
+ # Inform the writer to switch to a new section
+ # --------------------------------------------------------------------------
+ def startNewSection(self, subtitle, sectionType="default", newPage=1):
+ """
+ Begins a new report section.
+
+ @param subtitle: The subtitle to display above the section
+ @param sectionType: The name of the previous defined section to use.
+ @param newPage: If 0 then the new page will be supressed.
+ If 1 (the default) then a new page will be output prior
+ to starting the section.
+ """
+ if sectionType != self._currentSectionType:
+ self.init = 1
+ self._currentSection = self.definedSectionTypes[sectionType]
+ self._currentSectionType = sectionType
+ self.subtitle = subtitle
+ if newPage:
+ self.highlightIndex = 0
+ if self.init:
+ self.init = 0
+ self.beginData()
+ self.continued = 0
+ if newPage:
+ self.newPage()
+ else:
+ self.drawSectionHeading()
+ self.drawTableHeader()
+
+ # ==========================================================================
+ # Data functions
+ # ==========================================================================
+
+ # --------------------------------------------------------------------------
+ # Add data row
+ # --------------------------------------------------------------------------
+ def addRow(self, data, style="Data"):
+ """
+ Adds a row of data to the current section
+
+ @param data: A list of strings containing the data to add to the current
section
+ @param style: The format style to use to render the row.
+ These are currently hardcoded into this class and include
+ Data (default), Subtotal, Total
+ """
+ canvas = self.canvas
+
+ if style == "Total":
+ self.y -= 4 * self.scale
+
+ if style in ("Subtotal","Total"):
+ font, size, tracking = subtotalFont
+ fontWidth = self.subtotalFontWidth
+ else:
+ font, size, tracking = dataFont
+ fontWidth = self.dataFontWidth
+
+ size = size * self.scale
+ tracking = tracking * self.scale
+
+ if self.y - tracking < self.miny:
+ self.newPage()
+
+ highlighted = 0
+ if style == "Data":
+ highlighted = divmod((self.highlightIndex),4)[1]>1
+ self.highlightIndex += 1
+ else:
+ self.highlightIndex = 0 # Reset highlighting after subtotals
+
+ boxy = self.y - (tracking-size)*1.5
+ boxh = tracking
+
+ if style in ("Subtotal","Total"):
+ self.drawHorizBorderedBox(leftmargin, boxy, self.width-leftmargin*2,
+ boxh, subtotalColor, lines=lineWidth *
self.scale)
+ elif highlighted:
+ self.drawHorizBorderedBox(leftmargin, boxy, self.width-leftmargin*2,
+ boxh, highlightColor, lines=0)
+
+ canvas.setFont(font, size)
+
+ i = 0
+ while i < len(data):
+ col = data[i]
+ # Find the hlx values (used for column highlight and borders)
+ if i > 0:
+ hlx1 = self._currentSection['columnCoords'][i-1][1]+self.columnGap/2.0
+ else:
+ hlx1 = leftmargin
+
+ if i < len(self._currentSection['columnCoords'])-1:
+ hlx2 = self._currentSection['columnCoords'][i+1][0]-self.columnGap/2.0
+ else:
+ hlx2 = self.width-leftmargin*2
+
+ # Column highlight support
+ highlightColumn = self._currentSection['columnHighlight'][i]
+ if highlightColumn and not style in ("Subtotal","Total"):
+ if self.highlightIndex==1: # We're on the first column (not 0 due to
+= above)
+ adjust = lineWidth#*self.scale
+ else:
+ adjust = 0
+ if highlighted:
+ color = colors.Blacker(highlightColor,.98)
+ else:
+ color = highlightColumn
+
+ self.drawHorizBorderedBox(hlx1, boxy - adjust, hlx2-hlx1, boxh,
+ color, lines=0)
+
+ # Column border support
+ leftBorder = self._currentSection['columnLeftBorder'][i]
+
+ if leftBorder:
+ canvas.setLineWidth(leftBorder*self.scale)
+ canvas.line(hlx1, boxy, hlx1, boxy + boxh)
+
+ rightBorder =self._currentSection['columnRightBorder'][i]
+ if rightBorder:
+ canvas.setLineWidth(rightBorder*self.scale)
+ canvas.line(hlx2, boxy, hlx2, boxy + boxh)
+
+ if col:
+ align= self._currentSection['columnAligns'][i]
+ x1, x2 = self._currentSection['columnCoords'][i]
+
+ # Clip text, if needed
+ restore = 0
+ if fontWidth(col, size) > x2-x1:
+ if self._currentSection['columnOverflow'][i]:
+ col = self._currentSection['columnOverflow'][i]
+ else:
+ restore = 1
+ canvas.saveState()
+ path = canvas.beginPath()
+ # Vertical is overkill, but only interested in horizontal
+ path.rect(x1,self.y-tracking, x2-x1, tracking*3)
+ canvas.clipPath(path, stroke=0, fill=0)
+
+ if align == LEFT:
+ canvas.drawString(x1,self.y,col)
+ elif align == RIGHT:
+ canvas.drawRightString(x2,self.y,col)
+ elif align == CENTER:
+ canvas.drawCentredString(x1+(x2-x1)/2.0,self.y,col)
+
+ # Restore from clipping
+ if restore:
+ canvas.restoreState()
+ i += 1
+
+ self.y -= tracking
+
+ # ==========================================================================
+ # Private functions
+ # ==========================================================================
+
+ # --------------------------------------------------------------------------
+ # Begin a new page
+ # --------------------------------------------------------------------------
+ def newPage(self):
+ """
+ Private function that creates a new page.
+ """
+ if self.page:
+ self.canvas.showPage()
+
+ self.page += 1
+ self.y = self.height - topmargin
+
+ if self.page == 1:
+ self.drawLargeTitle()
+ else:
+ self.drawSmallTitle()
+
+ self.drawTableHeader()
+ self.footer()
+
+ self.continued = 1
+
+
+ # --------------------------------------------------------------------------
+ # Draw footer
+ # --------------------------------------------------------------------------
+ def footer(self):
+ """
+ Private function that creates the footer containing the time/page #
+ """
+ canvas = self.canvas
+ font, size, tracking = footerFont
+ canvas.setFont(font, size)
+ canvas.drawString(leftmargin, topmargin, self.timestamp)
+ canvas.drawRightString(self.width - leftmargin, topmargin, "Page %s" %
self.page)
+ self.miny = topmargin + tracking*2
+
+ # --------------------------------------------------------------------------
+ # Draw full (first-page) header (Title)
+ # --------------------------------------------------------------------------
+ def drawLargeTitle(self):
+ """
+ Private function that creates a full (first page) header on a new page.
+ """
+ canvas = self.canvas
+ self.y -= titleFont[2]
+ for text, fontspec in self._titleList:
+ if text:
+ font, size, tracking = fontspec
+ canvas.setFont(font, size)
+ canvas.drawCentredString(self.width/2.0, self.y, text)
+ self.y -= tracking
+ self.drawSectionHeading()
+
+ # --------------------------------------------------------------------------
+ # Draw short (non-first-page) header (Title)
+ # --------------------------------------------------------------------------
+ def drawSmallTitle(self):
+ """
+ Private function that creates a short ( non first page) header on a new
page.
+ """
+ canvas = self.canvas
+ font, size, tracking = repeatTitleFont
+ self. y -= size
+ canvas.setFont(font, size)
+ canvas.drawString(leftmargin, self.y, self._titleList[0][0])
+ canvas.drawRightString(self.width - leftmargin, self.y,
self._titleList[1][0])
+ self.y -= tracking
+ self.drawSectionHeading()
+
+ # --------------------------------------------------------------------------
+ # Draw the section header
+ # --------------------------------------------------------------------------
+ def drawSectionHeading(self):
+ """
+ Draws the text that preceeds the section's table.
+ """
+ canvas = self.canvas
+
+ if not self.subtitle:
+ return
+
+ self.y -= subtitleFont[2]
+
+ font, size, tracking = subtitleLabelFont
+
+ text = canvas.beginText(leftmargin, self.y)
+ for l in self.subtitle.split():
+ boldOff = 0
+ if l[0] == '*':
+ l = l[1:]
+ font, size, tracking = subtitleFont
+
+ if l[-1] == '*':
+ boldOff = 1
+ l = l[:-1]
+
+ text.setFont(font, size)
+ text.textOut(l+ ' ')
+ if boldOff:
+ font, size, tracking = subtitleLabelFont
+ text.textOut(' ')
+
+ if self.continued:
+ font2, size2, tracking2 = subtitleContinueFont
+ text.setFont(font2, size2)
+ text.textOut("(Continued)")
+
+ canvas.drawText(text)
+ self.y -= tracking*2
+
+
+ # --------------------------------------------------------------------------
+ # Draw table header
+ # --------------------------------------------------------------------------
+ def drawTableHeader(self):
+ """
+ Generates a section's table header.
+ """
+ canvas = self.canvas
+
+ numRows = len(self._currentSection['headerList'])
+
+ font, size, tracking = tableHeaderFont
+ size = size * self.scale
+ tracking = tracking * self.scale
+ canvas.setFont(font, size)
+
+ boxy = self.y + tracking - (tracking-size)/2.0
+ boxh = -tracking*numRows - (tracking-size)/2.0 - lineWidth*self.scale
+ self.drawHorizBorderedBox(leftmargin, boxy, self.width-leftmargin*2,
+ boxh, headingColor)
+
+ for list in self._currentSection['headerList']:
+ for header in list:
+ c1 = header['start']
+ c2 = header['end']
+ x1 = self._currentSection['columnCoords'][c1][0]
+ x2 = self._currentSection['columnCoords'][c2][1]
+ align = header['align']
+ text = header['text']
+
+ canvas.saveState()
+ path = canvas.beginPath()
+ # Vertical is overkill, but only interested in horizontal
+ path.rect(x1,self.y-tracking, x2-x1, tracking*3)
+ canvas.clipPath(path, stroke=0, fill=0)
+
+ if align == LEFT:
+ canvas.drawString(x1,self.y,text)
+ elif align == RIGHT:
+ canvas.drawRightString(x2,self.y,text)
+ elif align == CENTER:
+ canvas.drawCentredString(x1+(x2-x1)/2.0,self.y,text)
+ canvas.restoreState()
+
+ leftBorder = header['leftBorder']
+ if leftBorder:
+ canvas.setLineWidth(leftBorder*self.scale)
+ canvas.line(x1-self.columnGap/2.0, boxy, x1-self.columnGap/2.0, boxy
+ boxh)
+
+ rightBorder = header['rightBorder']
+ if rightBorder:
+ canvas.setLineWidth(rightBorder*self.scale)
+ canvas.line(x2+self.columnGap/2.0, boxy, x2+self.columnGap/2.0, boxy
+ boxh)
+ self.y -= tracking
+
+
+
+ # --------------------------------------------------------------------------
+ # Draws a box w/shading and a top/bottom border
+ # --------------------------------------------------------------------------
+ def drawHorizBorderedBox(self, x, y, w, h, color, lines=lineWidth):
+ canvas = self.canvas
+ canvas.saveState()
+ canvas.setFillColor(color)
+ canvas.rect(x,y,w,h, stroke=0,fill=1)
+ if lines:
+ canvas.setLineWidth(lines)
+ canvas.line(x,y,x+w,y)
+ canvas.line(x,y+h,x+w,y+h)
+ canvas.restoreState()
+
+ # --------------------------------------------------------------------------
+ # Initialize report section
+ # --------------------------------------------------------------------------
+ def beginData(self):
+ """
+ Prepares the class to begin drawing a section on the report. Figures out
+ the required orientation of the report as well as any scaling that is
required
+ """
+ # Calculate column sizes
+ totalCols = 0
+ for cs in self._currentSection['columnSizes']:
+ totalCols += cs
+
+ # Figure out the page orientation/scaling
+ if not self._scalingComplete:
+ self._scalingComplete = 1
+ self.pageSize = letter
+ self.scale = 1.0
+ if totalCols < maxColsForPortraitNonscaled: # Guestimate of max #
cols we can get on portrait
+ self.pageOrient = portrait
+ self.width, self.height = letter
+ elif totalCols < maxColsForPortraitScaled:
+ self.pageOrient = portrait
+ self.width, self.height = letter
+ self.scale = maxColsForPortraitNonscaled / float(totalCols)
+ elif totalCols < maxColsForLandscapeNonscaled:
+ self.pageOrient = landscape
+ self.height, self.width = letter
+ else:
+ self.pageOrient = landscape
+ self.height, self.width = letter
+ self.scale = maxColsForLandscapeNonscaled / float(totalCols)
+
+ if self.scale < 1:
+ print "Scaling to %.2f%%" % (self.scale*100)
+
+ # in pts, Amount of gap to leave between columns... doesn't need to be
too much
+ self.columnGap = self.columnGap * self.scale
+
+ self.canvas = canvas.Canvas(self.file,
pagesize=self.pageOrient(self.pageSize))
+
+ font, size, leading = dataFont
+ self.dataFontWidth = getFont(font).stringWidth
+
+ font, size, leading = subtotalFont
+ self.subtotalFontWidth = getFont(font).stringWidth
+
+ # This is not scaled down according to self.scale...
+ # we'll instead scale the point sizes down when we do setFont
+ usableHorizSpace = (self.width - 2*leftmargin -
self.columnGap*(len(self._currentSection['columnSizes'])))
+ x = leftmargin + self.columnGap/2.0
+ for i in range(len(self._currentSection['columnSizes'])):
+ colSize = (self._currentSection['columnSizes'][i] / float(totalCols) *
usableHorizSpace)
+ self._currentSection['columnCoords'].append ( ( x, x+colSize ) )
+ x += colSize + self.columnGap
+
Added: trunk/gnue-common/src/printing/pdftable/sample.py
===================================================================
--- trunk/gnue-common/src/printing/pdftable/sample.py 2004-10-02 11:52:06 UTC
(rev 6446)
+++ trunk/gnue-common/src/printing/pdftable/sample.py 2004-10-04 01:04:15 UTC
(rev 6447)
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2004 Free Software Foundation
+#
+# FILE:
+# pdftable/sample.py
+#
+# DESCRIPTION:
+"""
+A sample application showing how to use pdftable
+"""
+
+# GNUe Modules
+from gnue.common.apps.GClientApp import *
+from gnue.common.printing.pdftable import *
+
+#
+# Sample
+#
+# Primay class of this application
+#
+class Sample(GClientApp):
+ VERSION = "1.0.0"
+ COMMAND = "sample"
+ NAME = "sample"
+ USAGE = GClientApp.USAGE
+ SUMMARY = """
+Generates a sample report using gnue.common.printing.pdftable
+"""
+
+ def __init__(self):
+ GClientApp.__init__(self)
+
+ def run(self):
+ # ------------------------------------------------------------------------
+ # Create the output file
+ # ------------------------------------------------------------------------
+ f = open('output.pdf','w')
+ writer = pdftable(f)
+
+
+ writer.setFullTitle([("Quarterly Sales Report", titleFont),
+ ('2002 - 2004', title2Font),
+ ])
+
+ # ========================================================================
+ # Main section of the report
+ # ========================================================================
+
+ # ------------------------------------------------------------------------
+ # Define this sections columns
+ # ------------------------------------------------------------------------
+ writer.addColumn(CENTER,7)
+
+ # Loop through creating columns for each quarter
+ for q in range(4): # q isn't used
+ # 2002 count and %
+ writer.addColumn(RIGHT, 5, leftBorder=.25)
+ writer.addColumn(RIGHT, 7)
+ # 2003 count and %
+ writer.addColumn(RIGHT, 5, highlight=colors.Whiter(highlightColor,.3))
+ writer.addColumn(RIGHT, 7, highlight=colors.Whiter(highlightColor,.3))
+ # 2004 count and %
+ writer.addColumn(RIGHT, 5)
+ writer.addColumn(RIGHT, 7)
+
+ # ------------------------------------------------------------------------
+ # Populate this section's header
+ # ------------------------------------------------------------------------
+
+ # Quarter heading
+ writer.addHeader('Q1',CENTER,startColumn=1, endColumn=6)
+ writer.addHeader('Q2',CENTER,startColumn=7, endColumn=12, leftBorder=.25)
+ writer.addHeader('Q3',CENTER,startColumn=13,endColumn=18, leftBorder=.25,
rightBorder=.25)
+ writer.addHeader('Q4',CENTER,startColumn=19,endColumn=24)
+
+ # Repeating year columns
+ writer.addHeaderRow()
+
+ for q in range(4):
+ offset=q*6 # Adjust for 3 years, 2 columns per year for each quarter
+ writer.addHeader("2002" , CENTER , 1+offset, 2+offset)
+ writer.addHeader("2003" , CENTER , 3+offset, 4+offset)
+ writer.addHeader("2004" , CENTER , 5+offset, 6+offset)
+
+ # The count and % columns
+ writer.addHeaderRow()
+
+ writer.addHeader("Model",CENTER,0,0, rightBorder=.25)
+ for q in range(4):
+ offset=q*6 # Adjust for 3 years, 2 columns per year for each quarter
+ print offset
+ writer.addHeader('#',CENTER, 1+offset,1+offset)
+ writer.addHeader('%',CENTER, 2+offset,2+offset)
+ writer.addHeader('#',CENTER, 3+offset,3+offset)
+ writer.addHeader('%',CENTER, 4+offset,4+offset)
+ writer.addHeader('#',CENTER, 5+offset,5+offset)
+ writer.addHeader('%',CENTER, 6+offset,6+offset)
+
+ # ========================================================================
+ # Second section of the report
+ # ========================================================================
+
+ # ------------------------------------------------------------------------
+ # Define this sections columns
+ # ------------------------------------------------------------------------
+ for counter in range(4):
+ writer.addColumn(LEFT, 5, sectionType="topSales")
+ writer.addColumn(LEFT, 15, sectionType="topSales")
+
+ # ------------------------------------------------------------------------
+ # Populate this section's header
+ # ------------------------------------------------------------------------
+ writer.addHeader('Q1',CENTER, 0, 1, rightBorder=.25,
sectionType="topSales")
+ writer.addHeader('Q2',CENTER, 2, 3, rightBorder=.25,
sectionType="topSales")
+ writer.addHeader('Q3',CENTER, 4, 5, rightBorder=.25,
sectionType="topSales")
+ writer.addHeader('Q4',CENTER, 6, 7, sectionType="topSales")
+
+ writer.addHeaderRow(sectionType="topSales")
+ writer.addHeader('Year',LEFT,0,0,sectionType="topSales")
+ writer.addHeader('Salesperson',LEFT,1,1,sectionType="topSales")
+ writer.addHeader('Year',LEFT,2,2,sectionType="topSales")
+ writer.addHeader('Salesperson',LEFT,3,3,sectionType="topSales")
+ writer.addHeader('Year',LEFT,4,4,sectionType="topSales")
+ writer.addHeader('Salesperson',LEFT,5,5,sectionType="topSales")
+ writer.addHeader('Year',LEFT,6,6,sectionType="topSales")
+ writer.addHeader('Salesperson',LEFT,7,7,sectionType="topSales")
+
+
+ # ========================================================================
+ # Populate report with data
+ # ========================================================================
+
+ storeInfo = [{'location' :'Manhattan, KS',
+ 'modelLines' : 15, # Number of bogus models to
create
+ },
+ {'location' :'Memphis, TN',
+ 'modelLines' : 65, # Number of bogus models to
create
+ },
+ ]
+
+ for store in storeInfo:
+ # ----------------------------------------------------------------------
+ # Main section of the store's report
+ # ----------------------------------------------------------------------
+
+ # Start the section
+ writer.startNewSection("%s Store" % store['location'])
+
+ # Fill with data
+ for row in self._generateSalesData(store['modelLines']):
+ writer.addRow(row)
+
+ # We won't compute totals in this sample
+ writer.addRow(["Total 1"]+['0']*24, style='Subtotal')
+ writer.addRow(["Total 2"]+['0']*24, style='Total')
+
+ # ----------------------------------------------------------------------
+ # Secondard section
+ # ----------------------------------------------------------------------
+
+ # Start the new section, suppress the automatic new page at section start
+ writer.startNewSection("Top Sales Personel - Last 5
years",sectionType="topSales", newPage=0)
+
+ # Fill with data
+ for row in self._generateTopSalespersons(5):
+ writer.addRow(row)
+
+ writer.close()
+ f.close()
+
+
+ # ==========================================================================
+ # Functions to define bogus data for the sample
+ # ==========================================================================
+ def _generateSalesData(self,lines):
+ """
+ Simple function to generate random sales data for the sample report.
+
+ @param lines: The number of lines of model sales data to generate
+ """
+ import random
+ data = []
+ for count in range(lines):
+ row = []
+ # Build the model number
+ letterOffset, number = divmod(count,10)
+ model = "%s%s" % (chr(65+letterOffset),number)
+ row.append(model)
+
+ # Randomly create quarterly sales data for 3 years, set % to 0 for now
+ for c in range(12):
+ sales = random.randrange(1,100,1)
+ row.append("%s" % sales)
+ row.append("0") # not computing %s in the sample
+
+ # Add the row to the data being returned
+ data.append(row)
+
+ return data
+
+ def _generateTopSalespersons(self,years):
+ """
+ Simple function to generate random salesperson data for the sample report.
+
+ @param lines: The number of lines of salesperson data to generate
+ """
+ import random
+ names=['Tom Barker','Linda Carter','Willy Sellut','Peter Smith','Chilly
Willy','Betty Bip',
+ 'Mark Bently','Maynard Thomas','Sally Watson','Tammy
Kingston','Shelly White',
+ 'Rose Parkson','Blair Thomas',
+ ]
+
+ data = []
+ for year in range(2004-years, 2004):
+ row1 = []
+ row2 = []
+ row3 = []
+ for q in range(4):
+ people = random.sample(names,3)
+ row1.append("%s" % year)
+ row1.append(people[0])
+ row2.append("%s" % year)
+ row2.append(people[1])
+ row3.append("%s" % year)
+ row3.append(people[2])
+ data.append(row1)
+ data.append(row2)
+ data.append(row3)
+ return data
+
+# ============================================================================
+# Run the sample report
+# ============================================================================
+if __name__ == '__main__':
+ Sample().run()
+
+
+
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- r6447 - in trunk/gnue-common/src/printing: . pdftable,
jamest <=