commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue-contrib] r289 - / printing printing/barcodes printing/pdftable


From: reinhard
Subject: [gnue-contrib] r289 - / printing printing/barcodes printing/pdftable
Date: Tue, 6 Oct 2009 09:49:10 -0500 (CDT)

Author: reinhard
Date: 2009-10-06 09:49:09 -0500 (Tue, 06 Oct 2009)
New Revision: 289

Added:
   printing/
   printing/__init__.py
   printing/barcodes/
   printing/barcodes/Base.py
   printing/barcodes/README
   printing/barcodes/__init__.py
   printing/barcodes/codabar.py
   printing/barcodes/code39.py
   printing/barcodes/interleaved2of5.py
   printing/barcodes/postnet.py
   printing/barcodes/standard2of5.py
   printing/pdftable/
   printing/pdftable/README
   printing/pdftable/__init__.py
   printing/pdftable/pdftable.py
   printing/pdftable/sample.py
Log:
Moved printing library to gnue-contrib since it's not used in any GNUe Tool.


Added: printing/__init__.py
===================================================================

Added: printing/barcodes/Base.py
===================================================================
--- printing/barcodes/Base.py                           (rev 0)
+++ printing/barcodes/Base.py   2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,257 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# barcodes/Base.py
+#
+# DESCRIPTION:
+"""
+"""
+#
+import sys, os, string
+from reportlab.lib import colors
+from reportlab.graphics.shapes import *
+
+class InvalidBarcode(StandardError):
+  pass
+
+
+class Barcode:
+  mapping = {}
+  chars = []
+  validLengths = []
+  lineWidth = 1.44 # points (.02")
+  lineHeight = 18 # (.125")
+  spacing = ''
+  start = ''
+  stop = ''
+  defaultIncludeText = True
+
+  encodingMap = {
+         # Stroke?, X Multiplier, Y Multiplier
+    '0': (False, 1, 1),
+    '1': (True, 1, 1),
+  }
+
+
+  def checkdigit(self, value):
+    """
+    Returns the checkdigit encoding for the given value
+    """
+    return ''
+
+  def calculateLineHeight(self, width):
+    return self.lineHeight
+
+  def _buildBinary(self, value):
+    """
+    Returns a string of 0s (no line) and 1s (line).
+    Note that, depending on the barcode type,
+    a single bar could be multiple
+    """
+
+    if self.validLengths and len(value) not in self.validLengths:
+      raise InvalidBarcode, 'Barcode is not a valid length: Should be one of 
%s' % self.validLengths
+
+    value = str(value)
+    rv = self.start + self.spacing
+
+    first = True
+    for ch in value + self.checkdigit(value):
+      if first:
+        first = False
+      else:
+        rv += self.spacing
+      try:
+        rv += self.mapping[ch]
+      except KeyError:
+        raise InvalidBarcode, 'Barcode cannot contain "%s" character.' % ch
+
+    return rv + self.spacing + self.stop
+
+
+  ###
+  ###
+  ###
+  def generate(self, value, stream=None,
+               format='eps', includeText=None, textSize=7, dpi=300):
+    """
+    Generates the requested bar code either via a stream or as the
+    requested object type.
+
+    @param value:   The string to convert to a barcode
+    @param stream:  Optional argument of file name as a string, or any
+                    open file style object.
+    @param format: The format in which the output should be generated.
+                  Valid file formats include pdf, eps, svg and
+                  will require the stream argument be provided.
+                  Valid object formats include
+                  rldrawing (ReportLab Drawing object will be returned,
+                  No stream argument is required).
+    @param includeText: Boolean.  If true then human readable text will
+                        be printed centered under the barcode.
+    @param textSize: The point size of the human readable text.
+    @param dpi: The dots per inch at which the bitmap should be generated.
+
+    @return: None or a format dependent object.  Valid return values::
+            eps : None
+            pdf : None
+            svg : None
+            rl  : ReportLab Drawing
+    @rtype: misc
+    """
+
+    assert (format in ('rl','pil') or stream is not None)
+
+    d = self._generateDrawing(value, includeText, textSize, dpi)
+
+    #
+    # Process formats that return value instead of write to a file
+    #
+    if format == 'rl':
+      return d
+
+    #
+    # A stream is required for the remaining formats
+    #
+    if not hasattr(stream, 'write'):
+      closeFile = True
+      stream = open(stream,'w')
+    else:
+      closeFile = False
+
+    if format == 'pdf':
+      from reportlab.graphics import renderPDF
+      renderPDF.drawToFile(d, stream, 'GNUe')
+    elif format == 'eps':
+      from reportlab.graphics import renderPS
+      renderPS.drawToFile(d, stream)
+    elif format == 'svg':
+      from reportlab.graphics import renderSVG
+      renderSVG.drawToFile(d, stream)
+    elif format in ('png','tiff'):
+      from reportlab.graphics import renderPM
+      renderPM.drawToFile(d, stream,format.upper(), dpi=dpi)
+    elif format in ('pil',):
+      from reportlab.graphics import renderPM
+      return renderPM.drawToPIL(d, dpi=dpi)
+
+
+#
+# This code *should* be replaced with calls to renderPM
+# but that appears broken in the .debs
+#
+    ##
+    ## Raster-based output using PIL
+    ##
+#     elif format in ('png','tiff','ppm','xbm'):
+#       lineWidth = int(lineWidth * dpi/72+.5)   # 300dpi
+#       lineHeight = int(lineHeight * dpi/72+.5)  # 300dpi
+#       # Special case for PostNet
+#       lineHeight2 = int(lineHeight * .45+.5)
+#
+#       # Create a new monochrome image with a white backgint
+#       image = Image.new('1',(int(len(code)*lineWidth+.5),
+#          int(lineHeight+.5)), 1)
+#       draw = ImageDraw.Draw(image)
+#       offs = 0
+#       for ch in code:
+#         if ch == '1':
+#           draw.rectangle((offs,0,offs+lineWidth-1,lineHeight),
+#                           outline=0, fill=0)
+#         # Special case for PostNet
+#         elif ch == '2':
+#           draw.rectangle((offs,0,offs+lineWidth-1,lineHeight2),
+#                           outline=0, fill=0)
+#         offs += lineWidth
+#
+#       image.save(stream, format)
+
+    if closeFile:
+      stream.close()
+
+  def _generateDrawing(self, value, includeText=None, textSize=7, dpi=300):
+    """
+    Generates a ReportLab Drawing object used by the renderers in generate()
+
+    @param value:   The string to convert to a barcode
+    @param includeText: Boolean.  If true then human readable text will
+                        be printed centered under the barcode.
+
+    @param textSize: The point size of the human readable text.
+    @param dpi: The dots per inch at which the bitmap should be generated.
+
+    @return: ReportLab Drawing
+    @rtype: misc
+    """
+
+    if includeText is None:
+      includeText = self.defaultIncludeText
+    code = self._buildBinary(value)
+    lineWidth = self.lineWidth
+    try:
+      spaceWidth = self.spaceWidth
+    except:
+      spaceWidth = lineWidth
+
+    width = 0
+    for ch in code:
+      stroke, xmul, ymul = self.encodingMap[ch]
+      if stroke:
+        width += lineWidth*xmul
+      else:
+        width += spaceWidth*xmul
+
+    lineHeight = self.calculateLineHeight(width)
+
+    d = Drawing(width+1,lineHeight+(includeText and ( textSize+2 ) or 1 ))
+
+    if includeText:
+      y = textSize + 1
+    else:
+      y = 0
+
+    # Draw each bar
+    x = 0
+    for ch in code:
+      stroke, xmul, ymul = self.encodingMap[ch]
+      dx = lineWidth*xmul
+      dy = lineHeight*ymul
+      if stroke:
+        # So we won't cut off half the first bar...
+        if not x:
+          x = dx/2.0
+
+        d.add(Line(x+dx/2, y, x+dx/2, y+dy, 
strokeColor=colors.black,strokeWidth=dx))
+
+        x += dx
+      else:
+        x += spaceWidth*xmul
+
+    # Draw the text
+    if includeText:
+      d.add(String(x/2.0, textSize/2.0, value, fontSize=textSize, 
fontName="Courier",fillColor=colors.black,textAnchor="middle"))
+
+    return d
+
+  # Line height is .15 * barcode width, but at least .25"
+  # This is used by Code39, Interleaved 2 of 5, etc
+  def _calculate15(self, width):
+    return max(18, .15 * width)

Added: printing/barcodes/README
===================================================================
--- printing/barcodes/README                            (rev 0)
+++ printing/barcodes/README    2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,33 @@
+Usage:
+
+  from gnue.common.printing.barcodes import Code39
+
+  # You can pass either a file object, or a string containing a file name:
+
+  # File Object:
+  myfile = open('foo.eps','w')
+  Code39().generate('123443',myfile,'eps')
+  myfile.close()
+
+  # or string...
+  Code39().generate('123456','foo2.eps','eps')
+
+  # or get a report lab object  
+  object = Code39().generate('GNUE',format='rl')
+  from reportlab.graphics import renderPDF 
+  renderPDF.drawToFile(object, "output.pdf","GNUe Common Barcode Demo")      
+
+It has strong support for "eps". It somewhat supports
+"png", "tiff", and "xbm" via Python Imaging Library. 
+
+Test the outputs with your scanners before using in mass-production. 
+We try to stay within specs in our code, but printers and readers vary. 
+
+Specs:
+ * http://www.adams1.com/pub/russadam/info.html#Specs
+   General line width specs, min sizes
+   (also has link to an online barcode generator to compare against)
+
+ * http://barcodeisland.com/symbolgy.phtml
+   A lot of encodings for barcodes, but no spacing specs
+

Added: printing/barcodes/__init__.py
===================================================================
--- printing/barcodes/__init__.py                               (rev 0)
+++ printing/barcodes/__init__.py       2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,32 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# barcodes/__init__.py
+#
+# DESCRIPTION:
+"""
+"""
+#
+from codabar import *
+from code39 import *
+from interleaved2of5 import *
+from postnet import *
+from standard2of5 import *

Added: printing/barcodes/codabar.py
===================================================================
--- printing/barcodes/codabar.py                                (rev 0)
+++ printing/barcodes/codabar.py        2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,77 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# barcodes/codabar.py
+#
+# DESCRIPTION:
+"""
+Implements the Codabar barcode spec
+"""
+#
+from Base import Barcode
+
+class Codabar(Barcode):
+  chars = '0123456789-$:/.+'
+  mapping = {
+    '0': 'NnNnNwWn',
+    '1': 'NnNnWwNn',
+    '2': 'NnNwNnWn',
+    '3': 'WwNnNnNn',
+    '4': 'NnWnNwNn',
+    '5': 'WnNnNwNn',
+    '6': 'NwNnNnWn',
+    '7': 'NwNnWnNn',
+    '8': 'NwWnNnNn',
+    '9': 'WnNwNnNn',
+    '-': 'NnNwWnNn',
+    '$': 'NnWwNnNn',
+    ':': 'WnNnWnWn',
+    '/': 'WnWnNnWn',
+    '.': 'WnWnWnNn',
+    '+': 'NnWwWwWn'
+  }
+
+  start = 'NwNwNnWn'
+  stop  = 'nnNwWwN'
+
+  lineWidth = .72 # points (1.0mil)
+  lineHeight = 18 # 1/4"
+
+  encodingMap = {
+         # Stroke?, X Multiplier, Y Multiplier
+    'n': (False, 1, 1),   # Narrow Spaces
+    'w': (False, 2.5, 1),   # Wide Spaces
+    'N': (True, 1, 1),    # Narrow bars
+    'W': (True, 2.5, 1)   # Wide bars
+  }
+
+if __name__ == '__main__':
+
+  codabar = Codabar()
+
+  def test(value, format, file):
+    f = open(file,'wb')
+    codabar.generate(value,f, format)
+    f.close()
+
+#   test('0123456789-$:/.+','png','test1.png')
+#   test('+./:$-9876543210','tiff','test1.tif')
+  test('0123456789-$:/.+','eps','codabar-1.eps')

Added: printing/barcodes/code39.py
===================================================================
--- printing/barcodes/code39.py                         (rev 0)
+++ printing/barcodes/code39.py 2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,134 @@
+#
+# 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 02W1-1307, USA.
+#
+# Copyright 2w4 Free Software Foundation
+#
+# FILE:
+# barcodes/code39.py
+#
+# DESCRIPTION:
+"""
+Implements the Code 39 barcode spec
+"""
+#
+
+from Base import Barcode
+
+class Code39(Barcode):
+  """
+  Code 39 without a check digit
+  """
+  chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%'
+  mapping = {
+    '0': 'NnNwWnWnNn',
+    '1': 'WnNwNnNnWn',
+    '2': 'NnWwNnNnWn',
+    '3': 'WnWwNnNnNn',
+    '4': 'NnNwWnNnWn',
+    '5': 'WnNwWnNnNn',
+    '6': 'NnWwWnNnNn',
+    '7': 'NnNwNnWnWn',
+    '8': 'WnNwNnWnNn',
+    '9': 'NnWwNnWnNn',
+    'A': 'WnNnNwNnWn',
+    'B': 'NnWnNwNnWn',
+    'C': 'WnWnNwNnNn',
+    'D': 'NnNnWwNnWn',
+    'E': 'WnNnWwNnNn',
+    'F': 'NnWnWwNnNn',
+    'G': 'NnNnNwWnWn',
+    'H': 'WnNnNwWnNn',
+    'I': 'NnWnNwWnNn',
+    'J': 'NnNnWwWnNn',
+    'K': 'WnNnNnNwWn',
+    'L': 'NnWnNnNwWn',
+    'M': 'WnWnNnNwNn',
+    'N': 'NnNnWnNwWn',
+    'O': 'WnNnWnNwNn',
+    'P': 'NnWnWnNwNn',
+    'Q': 'NnNnNnWwWn',
+    'R': 'WnNnNnWwNn',
+    'S': 'NnWnNnWwNn',
+    'T': 'NnNnWnWwNn',
+    'U': 'WwNnNnNnWn',
+    'V': 'NwWnNnNnWn',
+    'W': 'WwWnNnNnNn',
+    'X': 'NwNnWnNnWn',
+    'Y': 'WwNnWnNnNn',
+    'Z': 'NwWnWnNnNn',
+    '-': 'NwNnNnWnWn',
+    '.': 'WwNnNnWnNn',
+    ' ': 'NwWnNnWnNn',
+    '$': 'NwNwNnNwNn',
+    '/': 'NwNwNnNwNn',
+    '+': 'NwNnNwNwNn',
+    '%': 'NnNwNwNwNn',
+  }
+
+  start= 'NwNnWnWnNn'
+  stop = 'NwNnWnWnN'
+
+  lineWidth = .6 # points (1.0mil)
+  lineHeight = 18 # Actually dependent on the width
+
+  encodingMap = {
+         # Stroke?, X Multiplier, Y Multiplier
+    'n': (False, 1, 1),   # Narrow Spaces
+    'w': (False, 2.1, 1),   # Wide Spaces
+    'N': (True, 1, 1),    # Narrow bars
+    'W': (True, 2.1, 1)   # Wide bars
+  }
+
+  calculateLineHeight = Barcode._calculate15
+
+
+######################################
+##
+##
+class Code39CheckDigit(Code39):
+  """
+  Code 39 with a Mod 43 check digit
+  """
+
+  # Calculate a Mod-43 check digit
+  def checkdigit(self, value):
+    v = 0
+    for ch in value:
+      v += self.chars.index(ch)
+    return self.chars[divmod(chv,43)[1]]
+
+
+if __name__ == '__main__':
+
+  code39 = Code39()
+
+  def test(value, format, file):
+    f = open(file,'wb')
+    code39.generate(value,f, format)
+    f.close()
+
+  import sys
+  if len(sys.argv[:1]): 
+    test (sys.argv[1],'eps','code39-test.eps')
+  else: 
+#   test('0123456789ABCDEF','png','test1.png')
+#   test('0123456789ABCDEF','tiff','test1.tif')
+    test('0123456789ABCDEF','eps','code39-1.eps')
+    test('GHIJKLMNOPQRSTUV','eps','code39-2.eps')
+    test('WXYZ-. $/+%','eps','code39-3.eps')
+    test('AR04F123','eps','code39-4.eps')

Added: printing/barcodes/interleaved2of5.py
===================================================================
--- printing/barcodes/interleaved2of5.py                                (rev 0)
+++ printing/barcodes/interleaved2of5.py        2009-10-06 14:49:09 UTC (rev 
289)
@@ -0,0 +1,108 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# barcodes/code39.py
+#
+# DESCRIPTION:
+"""
+Implements the Interleaved 2 of 5 barcode spec
+"""
+#
+
+from Base import Barcode
+
+class Interleaved2of5(Barcode):
+  chars = '0123456789'
+
+  # Interleaved works differently than the others
+  mapping = {
+    '0': 'NNWWN',
+    '1': 'WNNNW',
+    '2': 'NWNNW',
+    '3': 'WWNNN',
+    '4': 'NNWNW',
+    '5': 'WNWNN',
+    '6': 'NWWNN',
+    '7': 'NNNWW',
+    '8': 'WNNWN',
+    '9': 'NWNWN'
+  }
+
+  lineWidth = .6 # points (7.5mil)
+  lineHeight = 18 # Actually dependent on the width
+
+  start = 'NnNn'
+  stop  = 'WnN'
+
+  encodingMap = {
+         # Stroke?, X Multiplier, Y Multiplier
+    'n': (False, 1, 1),   # Narrow Spaces
+    'w': (False, 2.5, 1),   # Wide Spaces
+    'N': (True, 1, 1),    # Narrow bars
+    'W': (True, 2.5, 1)   # Wide bars
+  }
+
+  calculateLineHeight = Barcode._calculate15
+
+  # Since this is interleaved, we do
+  # our own custom buildBinary.
+  def _buildBinary(self, value):
+    """
+    Returns a string of 0s (no line) and 1s (line).
+    Note that, depending on the barcode type,
+    a single bar could be multiple
+    """
+
+    value = str(value)
+    if len(value)/2.0 != int(len(value)/2.0):
+      raise InvalidBarcode, \
+          'Interleaved 2of5 must have an even number of digits'
+
+    rv = self.start
+
+    even = False
+    for i in range(len(value)/2):
+      try:
+        bar = self.mapping[value[i]]
+        space = self.mapping[value[i+1]].lower()
+      except KeyError:
+        raise InvalidBarcode
+
+      for j in xrange(5):
+        rv += bar[j]
+        rv += space[j]
+
+    return rv + self.stop
+
+
+if __name__ == '__main__':
+
+  testbar = Interleaved2of5()
+
+  def test(value, format, file):
+    f = open(file,'wb')
+    testbar.generate(value,f, format)
+    f.close()
+
+#   test('0123456789','png','test1.png')
+#   test('9876543210','tiff','test1.tif')
+  test('0123456789','eps','i2of5-1.eps')
+  test('9876543210','eps','i2of5-1.eps')

Added: printing/barcodes/postnet.py
===================================================================
--- printing/barcodes/postnet.py                                (rev 0)
+++ printing/barcodes/postnet.py        2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,97 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# barcodes/code39.py
+#
+# DESCRIPTION:
+"""
+Implements the USPS PostNet barcode spec
+"""
+#
+
+from Base import Barcode, InvalidBarcode
+
+class PostNet(Barcode):
+  """
+  PostNet
+  """
+  validLengths = [5,9,11]
+  chars = '0123456789'
+  mapping = {
+    # 1 = tall bar, 2=short bar, 0=space
+    '0': '1010202020',
+    '1': '2020201010',
+    '2': '2020102010',
+    '3': '2020101020',
+    '4': '2010202010',
+    '5': '2010201020',
+    '6': '2010102020',
+    '7': '1020202010',
+    '8': '1020201020',
+    '9': '1020102020'
+  }
+
+  start = '10'
+  stop  = '1'
+
+  lineWidth = 1.44 # points
+  lineHeight = 8.5 # (.125")
+  spaceWidth = 1.9 #1.66
+
+  encodingMap = {
+    # Stroke?, X Multiplier, Y Multiplier
+    '0': (False, 1, 1),   # Spaces
+    '1': (True, 1, 1),    # Tall bars
+    '2': (True, 1, .45)   # Short bars
+  }
+
+
+
+  defaultIncludeText = False
+
+  # Calculate a Mod-10 check digit
+  def checkdigit(self, value):
+    v = 0
+    for ch in value:
+      try:
+        v += int(ch)
+      except ValueError:
+        raise InvalidBarcode
+    cd = abs(10-divmod(v,10)[1])
+    if cd == 10:
+      cd = 0
+    return self.chars[cd]
+
+
+if __name__ == '__main__':
+
+  postnet = PostNet()
+
+  def test(value, format, file):
+    f = open(file,'wb')
+    postnet.generate(value,f, format)
+    f.close()
+
+#   test('381072456','png','test1.png')
+#   test('381172459','tiff','test1.tif')
+  test('383759907','eps','postnet-1.eps')
+  test('12345','eps','postnet-2.eps')
+  test('12345678901','eps','postnet-3.eps')

Added: printing/barcodes/standard2of5.py
===================================================================
--- printing/barcodes/standard2of5.py                           (rev 0)
+++ printing/barcodes/standard2of5.py   2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,74 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# barcodes/code39.py
+#
+# DESCRIPTION:
+"""
+Implements the Standard 2 of 5 barcode spec
+"""
+#
+
+from Base import Barcode
+
+class Standard2of5(Barcode):
+  chars = '0123456789'
+  mapping = {
+    '0': 'N0N0W0W0N0',
+    '1': 'W0N0N0N0W0',
+    '2': 'N0W0N0N0W0',
+    '3': 'W0W0N0N0N0',
+    '4': 'N0N0W0N0W0',
+    '5': 'W0N0W0N0N0',
+    '6': 'N0W0W0N0N0',
+    '7': 'N0N0N0W0W0',
+    '8': 'W0N0N0W0N0',
+    '9': 'N0W0N0W0N0'
+  }
+
+  start = '0M0N0'
+  stop  = 'M0N0M'
+
+  encodingMap = {
+         # Stroke?, X Multiplier, Y Multiplier
+    '0': (False, 1, 1),   # Narrow Spaces
+    'N': (True, 1, 1),    # Narrow bars
+    'M': (True, 2, 1),    # Medium bars
+    'W': (True, 3, 1)   # Wide bars
+  }
+
+  # calculateLineHeight = Barcode._calculate15 # Unsure of this
+  lineWidth = 1 # points -- Unsure of this
+  lineHeight = 18 # Actually dependent on the width
+
+
+if __name__ == '__main__':
+
+  testbar = Standard2of5()
+
+  def test(value, format, file):
+    f = open(file,'wb')
+    testbar.generate(value,f, format)
+    f.close()
+
+#   test('0123456789','png','test1.png')
+#   test('9876543210','tiff','test1.tif')
+  test('0123456789','eps','s2of5-1.eps')

Added: printing/pdftable/README
===================================================================
--- printing/pdftable/README                            (rev 0)
+++ printing/pdftable/README    2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1 @@
+See sample.py for an example on how to use pdftable

Added: printing/pdftable/__init__.py
===================================================================
--- printing/pdftable/__init__.py                               (rev 0)
+++ printing/pdftable/__init__.py       2009-10-06 14:49:09 UTC (rev 289)
@@ -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-2009 Free Software Foundation
+#
+# FILE:
+# pdftable/__init__.py
+#
+# DESCRIPTION:
+"""
+"""
+#
+from pdftable import *

Added: printing/pdftable/pdftable.py
===================================================================
--- printing/pdftable/pdftable.py                               (rev 0)
+++ printing/pdftable/pdftable.py       2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,789 @@
+#
+# 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-2009 Free Software Foundation
+#
+# FILE:
+# pdftable/pdftable.py
+#
+# DESCRIPTION:
+"""
+A class that creates multisection tabular pdf reports.
+"""
+#
+
+# ============================================================================
+# Imports
+# ============================================================================
+# ----------------------------------------------------------------------------
+# Python standard modules
+# ----------------------------------------------------------------------------
+import time
+import sys
+
+# ----------------------------------------------------------------------------
+# 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 
+# ----------------------------------------------------------------------------
+fontDefs = {               # 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.
+# ----------------------------------------------------------------------------
+
+
+# ----------------------------------------------------------------------------
+# 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 self.fontDefs['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:
+  fontDefs = fontDefs
+  # ==========================================================================
+  # Creation/closure functions
+  # ==========================================================================
+  
+  # --------------------------------------------------------------------------
+  # Initialize
+  # --------------------------------------------------------------------------
+  def __init__(self, destination, 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 destination: A python file handle used for output
+    @param parameterBox: Unused?
+    """
+    self._titleList = ['Your Report','Your Report']
+    
+    self.destination = destination
+#     self.parameterBox = parameterBox
+
+    self.highlightColor = colors.HexColor("0xffffcc")
+    self.headingColor = self.subtotalColor = colors.HexColor("0xe6e6ff")
+
+    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, topBorder=0, bottomBorder=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 topBorder: The width of the top side border in points.
+    @param bottomBorder: The width of the bottom 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':[],
+                                       'columnTopBorder'  :[],
+                                       'columnBottomBorder':[],
+                                       '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)
+    secType['columnTopBorder'].append(topBorder)
+    secType['columnBottomBorder'].append(bottomBorder)
+
+  # ==========================================================================
+  # 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", displayBorders=True):
+    """
+    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
+    @param displayBorders: Boolean that controls if a borders will be drawn 
for this
+                           row.  Makes it easy to produce a "blank" row for 
spacing
+                           output
+    """
+    canvas = self.canvas
+
+    if style == "Total":
+      self.y -= 4 * self.scale
+
+    if style in ("Subtotal","Total"):
+      font, size, tracking = self.fontDefs['subtotalFont']
+      fontWidth = self.subtotalFontWidth
+    else:
+      font, size, tracking = self.fontDefs['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, self.subtotalColor, lines=lineWidth * 
self.scale)
+    elif highlighted:
+      # If there is a bottom border we need to not draw over the top
+      # of the previous rows border.  Currently minimum must be 1 point      
+      adjustment = max([self._currentSection['columnBottomBorder'][0],1])
+      self.drawHorizBorderedBox(leftmargin, boxy, self.width-leftmargin*2,
+                                boxh-adjustment, self.highlightColor, lines=0)
+
+    canvas.setFont(font, size)
+
+    i = 0
+    while i < len(data):
+      col = str(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
+      
+      # 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(self.highlightColor,.98)
+        else:
+          color = highlightColumn
+        
+        self.drawHorizBorderedBox(hlx1, boxy - adjust, 
+                                  hlx2-hlx1, boxh, 
+                                  color, lines=0)
+      
+      # Column border support
+      if displayBorders:
+        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)
+  
+        topBorder =self._currentSection['columnTopBorder'][i]
+        if topBorder:
+          canvas.setLineWidth(topBorder*self.scale)
+          canvas.line(hlx1, boxy + boxh, hlx2, boxy + boxh)
+  
+        bottomBorder = self._currentSection['columnBottomBorder'][i]
+        if bottomBorder:
+          canvas.setLineWidth(bottomBorder*self.scale)
+          canvas.line(hlx1, boxy, hlx2, boxy)
+                      
+      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
+
+  def addLine(self, string, borders=None):
+    """
+    Adds a single line of text instead of a row to a table.
+    
+    Usefull in reproducing a check register style printout.
+    
+    @param string: The string to print
+    @param borders: A list containing the border widths to use on the line.
+                    Sequence is in the order Top, Right, Bottom, Left (TRBL)
+                    which I believe is how CSS does it
+    """
+    canvas = self.canvas
+
+    font, size, tracking = self.fontDefs['dataFont']
+    fontWidth = self.dataFontWidth
+
+    size = size * self.scale
+    tracking = tracking * self.scale
+
+    if self.y - tracking < self.miny:
+       self.newPage()
+
+    boxy = self.y - (tracking-size)*1.5
+    boxh = tracking
+    
+    # Use the same highlighting as the pervious row printed
+    highlighted = divmod((self.highlightIndex-1),4)[1]>1 # Use the same 
highlighting 
+    if highlighted:
+      # If there is a bottom border we need to not draw over the top
+      # of the previous rows border.  Currently minimum must be 1 point      
+      adjustment = max([self._currentSection['columnBottomBorder'][0],1])
+      self.drawHorizBorderedBox(leftmargin, boxy, self.width-leftmargin*2,
+                                boxh-adjustment, self.highlightColor, lines=0)
+
+    # Place a border around the memo line
+    if borders:
+      topBorder, rightBorder, bottomBorder, leftBorder = borders
+
+      # Top
+      if topBorder:
+        canvas.setLineWidth(topBorder*self.scale)
+        canvas.line(leftmargin, boxy + boxh, self.width-leftmargin, boxy + 
boxh)
+        
+      #Right
+      if rightBorder:
+        canvas.setLineWidth(rightBorder*self.scale)
+        canvas.line(self.width-leftmargin, boxy, self.width-leftmargin, boxy + 
boxh)
+        
+      # Bottom
+      if bottomBorder:
+        canvas.setLineWidth(bottomBorder*self.scale)
+        canvas.line(leftmargin, boxy, self.width-leftmargin, boxy)
+      # Left
+      if leftBorder:
+        canvas.setLineWidth(leftBorder*self.scale)
+        canvas.line(leftmargin, boxy, leftmargin, boxy + boxh)
+
+    canvas.setFont(font, size)
+    hlx1 = leftmargin
+
+    if string:
+      # Clip text, if needed
+      restore = False
+      if fontWidth(string, size) > self.width-leftmargin*2:
+        restore = True
+        canvas.saveState()
+        path = canvas.beginPath()
+        # Vertical is overkill, but only interested in horizontal
+        path.rect(leftmargin,self.y-tracking, self.width-leftmargin*2, 
tracking*3)
+        canvas.clipPath(path, stroke=0, fill=0)
+
+      canvas.drawString(leftmargin,self.y,string)
+
+      # Restore from clipping
+      if restore:
+        canvas.restoreState()
+
+    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 = self.fontDefs['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 -= self.fontDefs['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 = self.fontDefs['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 -= self.fontDefs['subtitleFont'][2]
+
+    font, size, tracking = self.fontDefs['subtitleLabelFont']
+
+    text = canvas.beginText(leftmargin, self.y)
+    for l in self.subtitle.split():
+      boldOff = 0
+      if l[0] == '*':
+        l = l[1:]
+        font, size, tracking = self.fontDefs['subtitleFont']
+
+      if l[-1] == '*':
+        boldOff = 1
+        l = l[:-1]
+
+      text.setFont(font, size)
+      text.textOut(l+ ' ')
+      if boldOff:
+        font, size, tracking = self.fontDefs['subtitleLabelFont']
+        text.textOut(' ')
+
+    if self.continued:
+      font2, size2, tracking2 = self.fontDefs['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 = self.fontDefs['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, self.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
+
+      if isinstance(self.destination, canvas.Canvas): 
+          self.canvas = self.destination
+      else: 
+          self.canvas = canvas.Canvas(self.destination, 
+              pagesize=self.pageOrient(self.pageSize))
+  
+      font, size, leading = self.fontDefs['dataFont']
+      self.dataFontWidth = getFont(font).stringWidth
+  
+      font, size, leading = self.fontDefs['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
+
+  def setHighlightColorHex(self, hexColor):
+    self.highlightColor = colors.HexColor(hexColor)
+    
+  def setFont(self, fontStyle, settings):
+    assert fontStyle in self.fontDefs.keys(), 'Invalid font style: %s'
+    
+    self.fontDefs[fontStyle] = settings

Added: printing/pdftable/sample.py
===================================================================
--- printing/pdftable/sample.py                         (rev 0)
+++ printing/pdftable/sample.py 2009-10-06 14:49:09 UTC (rev 289)
@@ -0,0 +1,266 @@
+#!/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-2009 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", 
writer.fontDefs['titleFont']),
+                         ('2002 - 2004', writer.fontDefs['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(writer.highlightColor,.3)) 
+      writer.addColumn(RIGHT, 7, 
+             highlight=colors.Whiter(writer.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)
+#         writer.addLine("Test Line", borders=[0,.25,.25,.25])
+        
+      # 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 years: 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):
+        # Python 2.3 only :(
+        #people = random.sample(names,3)
+        
+        people = {}
+        while len(people) < 3:
+          people[names[random.randint(1,len(names)-1)]] = ''
+        people = people.keys()
+        
+        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()
+
+
+  


Property changes on: printing/pdftable/sample.py
___________________________________________________________________
Name: svn:executable
   + *





reply via email to

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