commit-gnue
[Top][All Lists]
Advanced

[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()
+
+
+  





reply via email to

[Prev in Thread] Current Thread [Next in Thread]