[Top][All Lists]
[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
+ *
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue-contrib] r289 - / printing printing/barcodes printing/pdftable,
reinhard <=