summaryrefslogtreecommitdiff
path: root/bin/reportlab/pdfbase/pdfform.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/reportlab/pdfbase/pdfform.py')
-rw-r--r--bin/reportlab/pdfbase/pdfform.py632
1 files changed, 632 insertions, 0 deletions
diff --git a/bin/reportlab/pdfbase/pdfform.py b/bin/reportlab/pdfbase/pdfform.py
new file mode 100644
index 00000000000..9fca917021e
--- /dev/null
+++ b/bin/reportlab/pdfbase/pdfform.py
@@ -0,0 +1,632 @@
+
+"""Support for Acrobat Forms in ReportLab documents
+
+This module is somewhat experimental at this time.
+
+Includes basic support for
+ textfields,
+ select fields (drop down lists), and
+ check buttons.
+
+The public interface consists of functions at the moment.
+At some later date these operations may be made into canvas
+methods. (comments?)
+
+The ...Absolute(...) functions position the fields with respect
+to the absolute canvas coordinate space -- that is, they do not
+respect any coordinate transforms in effect for the canvas.
+
+The ...Relative(...) functions position the ONLY THE LOWER LEFT
+CORNER of the field using the coordinate transform in effect for
+the canvas. THIS WILL ONLY WORK CORRECTLY FOR TRANSLATED COORDINATES
+-- THE SHAPE, SIZE, FONTSIZE, AND ORIENTATION OF THE FIELD WILL NOT BE EFFECTED
+BY SCALING, ROTATION, SKEWING OR OTHER NON-TRANSLATION COORDINATE
+TRANSFORMS.
+
+Please note that all field names (titles) in a given document must be unique.
+Textfields and select fields only support the "base 14" canvas fonts
+at this time.
+
+See individual function docstrings below for more information.
+
+The function test1(...) generates a simple test file.
+
+THIS CONTRIBUTION WAS COMMISSIONED BY REPORTLAB USERS
+WHO WISH TO REMAIN ANONYMOUS.
+"""
+
+### NOTE: MAKE THE STRING FORMATS DYNAMIC IN PATTERNS TO SUPPORT ENCRYPTION XXXX
+
+import string
+from reportlab.pdfbase.pdfdoc import LINEEND, PDFString, PDFStream, PDFDictionary, PDFName
+
+#==========================public interfaces
+
+def textFieldAbsolute(canvas, title, x, y, width, height, value="", maxlen=1000000, multiline=0):
+ """Place a text field on the current page
+ with name title at ABSOLUTE position (x,y) with
+ dimensions (width, height), using value as the default value and
+ maxlen as the maximum permissible length. If multiline is set make
+ it a multiline field.
+ """
+ theform = getForm(canvas)
+ return theform.textField(canvas, title, x, y, x+width, y+height, value, maxlen, multiline)
+
+def textFieldRelative(canvas, title, xR, yR, width, height, value="", maxlen=1000000, multiline=0):
+ "same as textFieldAbsolute except the x and y are relative to the canvas coordinate transform"
+ (xA, yA) = canvas.absolutePosition(xR,yR)
+ return textFieldAbsolute(canvas, title, xA, yA, width, height, value, maxlen, multiline)
+
+def buttonFieldAbsolute(canvas, title, value, x, y):
+ """Place a check button field on the current page
+ with name title and default value value (one of "Yes" or "Off")
+ at ABSOLUTE position (x,y).
+ """
+ theform = getForm(canvas)
+ return theform.buttonField(canvas, title, value, x, y)
+
+def buttonFieldRelative(canvas, title, value, xR, yR):
+ "same as buttonFieldAbsolute except the x and y are relative to the canvas coordinate transform"
+ (xA, yA) = canvas.absolutePosition(xR,yR)
+ return buttonFieldAbsolute(canvas, title, value, xA, yA)
+
+def selectFieldAbsolute(canvas, title, value, options, x, y, width, height):
+ """Place a select field (drop down list) on the current page
+ with name title and
+ with options listed in the sequence options
+ default value value (must be one of options)
+ at ABSOLUTE position (x,y) with dimensions (width, height)."""
+ theform = getForm(canvas)
+ theform.selectField(canvas, title, value, options, x, y, x+width, y+height)
+
+def selectFieldRelative(canvas, title, value, options, xR, yR, width, height):
+ "same as textFieldAbsolute except the x and y are relative to the canvas coordinate transform"
+ (xA, yA) = canvas.absolutePosition(xR,yR)
+ return selectFieldAbsolute(canvas, title, value, options, xA, yA, width, height)
+
+def test1():
+ from reportlab.pdfgen import canvas
+ fn = "formtest1.pdf"
+ c = canvas.Canvas(fn)
+ # first page
+ c.setFont("Courier", 10)
+ c.drawString(100, 500, "hello world")
+ textFieldAbsolute(c, "fieldA", 100, 600, 100, 20, "default value")
+ textFieldAbsolute(c, "fieldB", 100, 300, 100, 50, "another default value", multiline=1)
+ selectFieldAbsolute(c, "fieldC", "France", ["Canada", "France", "China"], 100, 200, 100, 20)
+ c.rect(100, 600, 100, 20)
+ buttonFieldAbsolute(c, "field2", "Yes", 100, 700)
+ c.rect(100, 700, 20, 20)
+ buttonFieldAbsolute(c, "field3", "Off", 100, 800)
+ c.rect(100, 800, 20, 20)
+ # second page
+ c.showPage()
+ c.setFont("Helvetica", 7)
+ c.translate(50, 20)
+ c.drawString(100, 500, "hello world")
+ textFieldRelative(c, "fieldA_1", 100, 600, 100, 20, "default value 2")
+ c.setStrokeColorRGB(1,0,0)
+ c.setFillColorRGB(0,1,0.5)
+ textFieldRelative(c, "fieldB_1", 100, 300, 100, 50, "another default value 2", multiline=1)
+ selectFieldRelative(c, "fieldC_1", "France 1", ["Canada 0", "France 1", "China 2"], 100, 200, 100, 20)
+ c.rect(100, 600, 100, 20)
+ buttonFieldRelative(c, "field2_1", "Yes", 100, 700)
+ c.rect(100, 700, 20, 20)
+ buttonFieldRelative(c, "field3_1", "Off", 100, 800)
+ c.rect(100, 800, 20, 20)
+ c.save()
+ print "wrote", fn
+
+#==========================end of public interfaces
+
+from pdfpattern import PDFPattern
+
+def getForm(canvas):
+ "get form from canvas, create the form if needed"
+ try:
+ return canvas.AcroForm
+ except AttributeError:
+ theform = canvas.AcroForm = AcroForm()
+ # install the form in the document
+ d = canvas._doc
+ cat = d._catalog
+ cat.AcroForm = theform
+ return theform
+
+class AcroForm:
+ def __init__(self):
+ self.fields = []
+ def textField(self, canvas, title, xmin, ymin, xmax, ymax, value="", maxlen=1000000, multiline=0):
+ # determine the page ref
+ doc = canvas._doc
+ page = doc.thisPageRef()
+ # determine text info
+ (R,G,B) = canvas._fillColorRGB
+ #print "rgb", (R,G,B)
+ font = canvas. _fontname
+ fontsize = canvas. _fontsize
+ field = TextField(title, value, xmin, ymin, xmax, ymax, page, maxlen,
+ font, fontsize, R, G, B, multiline)
+ self.fields.append(field)
+ canvas._addAnnotation(field)
+ def selectField(self, canvas, title, value, options, xmin, ymin, xmax, ymax):
+ # determine the page ref
+ doc = canvas._doc
+ page = doc.thisPageRef()
+ # determine text info
+ (R,G,B) = canvas._fillColorRGB
+ #print "rgb", (R,G,B)
+ font = canvas. _fontname
+ fontsize = canvas. _fontsize
+ field = SelectField(title, value, options, xmin, ymin, xmax, ymax, page,
+ font=font, fontsize=fontsize, R=R, G=G, B=B)
+ self.fields.append(field)
+ canvas._addAnnotation(field)
+ def buttonField(self, canvas, title, value, xmin, ymin):
+ # determine the page ref
+ doc = canvas._doc
+ page = doc.thisPageRef()
+ field = ButtonField(title, value, xmin, ymin, page)
+ self.fields.append(field)
+ canvas._addAnnotation(field)
+ def format(self, document):
+ from reportlab.pdfbase.pdfdoc import PDFArray
+ proxy = PDFPattern(FormPattern, Resources=GLOBALRESOURCES, fields=PDFArray(self.fields))
+ return proxy.format(document)
+
+FormPattern = [
+'<<', LINEEND,
+' /NeedAppearances true ', LINEEND,
+' /DA ', PDFString('/Helv 0 Tf 0 g '), LINEEND,
+' /DR ', LINEEND,
+["Resources"],
+' /Fields ', LINEEND,
+["fields"],
+'>>'
+]
+
+def FormFontsDictionary():
+ from reportlab.pdfbase.pdfdoc import PDFDictionary
+ fontsdictionary = PDFDictionary()
+ fontsdictionary.__RefOnly__ = 1
+ for (fullname, shortname) in FORMFONTNAMES.items():
+ fontsdictionary[shortname] = FormFont(fullname, shortname)
+ fontsdictionary["ZaDb"] = ZADB
+ return fontsdictionary
+
+def FormResources():
+ return PDFPattern(FormResourcesDictionaryPattern,
+ Encoding=ENCODING, Font=GLOBALFONTSDICTIONARY)
+
+ZaDbPattern = [
+' <<'
+' /BaseFont'
+' /ZapfDingbats'
+' /Name'
+' /ZaDb'
+' /Subtype'
+' /Type1'
+' /Type'
+' /Font'
+'>>']
+
+ZADB = PDFPattern(ZaDbPattern)
+
+FormResourcesDictionaryPattern = [
+'<<',
+' /Encoding ',
+["Encoding"], LINEEND,
+' /Font ',
+["Font"], LINEEND,
+'>>'
+]
+
+FORMFONTNAMES = {
+ "Helvetica": "Helv",
+ "Helvetica-Bold": "HeBo",
+ 'Courier': "Cour",
+ 'Courier-Bold': "CoBo",
+ 'Courier-Oblique': "CoOb",
+ 'Courier-BoldOblique': "CoBO",
+ 'Helvetica-Oblique': "HeOb",
+ 'Helvetica-BoldOblique': "HeBO",
+ 'Times-Roman': "Time",
+ 'Times-Bold': "TiBo",
+ 'Times-Italic': "TiIt",
+ 'Times-BoldItalic': "TiBI",
+ }
+
+EncodingPattern = [
+'<<',
+' /PDFDocEncoding ',
+["PDFDocEncoding"], LINEEND,
+'>>',
+]
+
+PDFDocEncodingPattern = [
+'<<'
+' /Differences'
+' ['
+' 24'
+' /breve'
+' /caron'
+' /circumflex'
+' /dotaccent'
+' /hungarumlaut'
+' /ogonek'
+' /ring'
+' /tilde'
+' 39'
+' /quotesingle'
+' 96'
+' /grave'
+' 128'
+' /bullet'
+' /dagger'
+' /daggerdbl'
+' /ellipsis'
+' /emdash'
+' /endash'
+' /florin'
+' /fraction'
+' /guilsinglleft'
+' /guilsinglright'
+' /minus'
+' /perthousand'
+' /quotedblbase'
+' /quotedblleft'
+' /quotedblright'
+' /quoteleft'
+' /quoteright'
+' /quotesinglbase'
+' /trademark'
+' /fi'
+' /fl'
+' /Lslash'
+' /OE'
+' /Scaron'
+' /Ydieresis'
+' /Zcaron'
+' /dotlessi'
+' /lslash'
+' /oe'
+' /scaron'
+' /zcaron'
+' 160'
+' /Euro'
+' 164'
+' /currency'
+' 166'
+' /brokenbar'
+' 168'
+' /dieresis'
+' /copyright'
+' /ordfeminine'
+' 172'
+' /logicalnot'
+' /.notdef'
+' /registered'
+' /macron'
+' /degree'
+' /plusminus'
+' /twosuperior'
+' /threesuperior'
+' /acute'
+' /mu'
+' 183'
+' /periodcentered'
+' /cedilla'
+' /onesuperior'
+' /ordmasculine'
+' 188'
+' /onequarter'
+' /onehalf'
+' /threequarters'
+' 192'
+' /Agrave'
+' /Aacute'
+' /Acircumflex'
+' /Atilde'
+' /Adieresis'
+' /Aring'
+' /AE'
+' /Ccedilla'
+' /Egrave'
+' /Eacute'
+' /Ecircumflex'
+' /Edieresis'
+' /Igrave'
+' /Iacute'
+' /Icircumflex'
+' /Idieresis'
+' /Eth'
+' /Ntilde'
+' /Ograve'
+' /Oacute'
+' /Ocircumflex'
+' /Otilde'
+' /Odieresis'
+' /multiply'
+' /Oslash'
+' /Ugrave'
+' /Uacute'
+' /Ucircumflex'
+' /Udieresis'
+' /Yacute'
+' /Thorn'
+' /germandbls'
+' /agrave'
+' /aacute'
+' /acircumflex'
+' /atilde'
+' /adieresis'
+' /aring'
+' /ae'
+' /ccedilla'
+' /egrave'
+' /eacute'
+' /ecircumflex'
+' /edieresis'
+' /igrave'
+' /iacute'
+' /icircumflex'
+' /idieresis'
+' /eth'
+' /ntilde'
+' /ograve'
+' /oacute'
+' /ocircumflex'
+' /otilde'
+' /odieresis'
+' /divide'
+' /oslash'
+' /ugrave'
+' /uacute'
+' /ucircumflex'
+' /udieresis'
+' /yacute'
+' /thorn'
+' /ydieresis'
+' ]'
+' /Type'
+' /Encoding'
+'>>']
+
+# global constant
+PDFDOCENC = PDFPattern(PDFDocEncodingPattern)
+# global constant
+ENCODING = PDFPattern(EncodingPattern, PDFDocEncoding=PDFDOCENC)
+
+
+def FormFont(BaseFont, Name):
+ from reportlab.pdfbase.pdfdoc import PDFName
+ return PDFPattern(FormFontPattern, BaseFont=PDFName(BaseFont), Name=PDFName(Name), Encoding=PDFDOCENC)
+
+FormFontPattern = [
+'<<',
+' /BaseFont ',
+["BaseFont"], LINEEND,
+' /Encoding ',
+["Encoding"], LINEEND,
+' /Name ',
+["Name"], LINEEND,
+' /Subtype '
+' /Type1 '
+' /Type '
+' /Font '
+'>>' ]
+
+# global constants
+GLOBALFONTSDICTIONARY = FormFontsDictionary()
+GLOBALRESOURCES = FormResources()
+
+
+def TextField(title, value, xmin, ymin, xmax, ymax, page,
+ maxlen=1000000, font="Helvetica-Bold", fontsize=9, R=0, G=0, B=0.627, multiline=0):
+ from reportlab.pdfbase.pdfdoc import PDFString, PDFName
+ Flags = 0
+ if multiline:
+ Flags = Flags | (1<<12) # bit 13 is at position 12 :)
+ fontname = FORMFONTNAMES[font]
+ return PDFPattern(TextFieldPattern,
+ value=PDFString(value), maxlen=maxlen, page=page,
+ title=PDFString(title),
+ xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax,
+ fontname=PDFName(fontname), fontsize=fontsize, R=R, G=G, B=B, Flags=Flags)
+
+
+TextFieldPattern = [
+'<<'
+' /DA'
+' (', ["fontname"],' ',["fontsize"],' Tf ',["R"],' ',["G"],' ',["B"],' rg)'
+' /DV ',
+["value"], LINEEND,
+' /F 4 /FT /Tx'
+'/MK << /BC [ 0 0 0 ] >>'
+' /MaxLen ',
+["maxlen"], LINEEND,
+' /P ',
+["page"], LINEEND,
+' /Rect '
+' [', ["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"], ' ]'
+'/Subtype /Widget'
+' /T ',
+["title"], LINEEND,
+' /Type'
+' /Annot'
+' /V ',
+["value"], LINEEND,
+' /Ff ',
+["Flags"],LINEEND,
+'>>']
+
+def SelectField(title, value, options, xmin, ymin, xmax, ymax, page,
+ font="Helvetica-Bold", fontsize=9, R=0, G=0, B=0.627):
+ #print "ARGS", (title, value, options, xmin, ymin, xmax, ymax, page, font, fontsize, R, G, B)
+ from reportlab.pdfbase.pdfdoc import PDFString, PDFName, PDFArray
+ if value not in options:
+ raise ValueError, "value %s must be one of options %s" % (repr(value), repr(options))
+ fontname = FORMFONTNAMES[font]
+ optionstrings = map(PDFString, options)
+ optionarray = PDFArray(optionstrings)
+ return PDFPattern(SelectFieldPattern,
+ Options=optionarray,
+ Selected=PDFString(value), Page=page,
+ Name=PDFString(title),
+ xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax,
+ fontname=PDFName(fontname), fontsize=fontsize, R=R, G=G, B=B)
+
+SelectFieldPattern = [
+'<< % a select list',LINEEND,
+' /DA ',
+' (', ["fontname"],' ',["fontsize"],' Tf ',["R"],' ',["G"],' ',["B"],' rg)',LINEEND,
+#' (/Helv 12 Tf 0 g)',LINEEND,
+' /DV ',
+["Selected"],LINEEND,
+' /F ',
+' 4',LINEEND,
+' /FT ',
+' /Ch',LINEEND,
+' /MK ',
+' <<',
+' /BC',
+' [',
+' 0',
+' 0',
+' 0',
+' ]',
+' /BG',
+' [',
+' 1',
+' 1',
+' 1',
+' ]',
+' >>',LINEEND,
+' /Opt ',
+["Options"],LINEEND,
+' /P ',
+["Page"],LINEEND,
+'/Rect',
+' [',["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"],
+' ] ',LINEEND,
+'/Subtype',
+' /Widget',LINEEND,
+' /T ',
+["Name"],LINEEND,
+' /Type ',
+' /Annot',
+' /V ',
+["Selected"],LINEEND,
+'>>']
+
+def ButtonField(title, value, xmin, ymin, page):
+ if value not in ("Yes", "Off"):
+ raise ValueError, "button value must be 'Yes' or 'Off': "+repr(value)
+ (dx, dy) = (16.77036, 14.90698)
+ return PDFPattern(ButtonFieldPattern,
+ Name=PDFString(title),
+ xmin=xmin, ymin=ymin, xmax=xmin+dx, ymax=ymin+dy,
+ Hide=HIDE,
+ APDOff=APDOFF,
+ APDYes=APDYES,
+ APNYes=APNYES,
+ Value=PDFName(value),
+ Page=page)
+
+ButtonFieldPattern = ['<< ',
+'/AA',
+' <<',
+' /D ',
+["Hide"], LINEEND,
+#' %(imported.18.0)s',
+' >> ',
+'/AP ',
+' <<',
+' /D',
+' <<',
+' /Off ',
+#' %(imported.40.0)s',
+["APDOff"], LINEEND,
+' /Yes ',
+#' %(imported.39.0)s',
+["APDYes"], LINEEND,
+' >>', LINEEND,
+' /N',
+' << ',
+' /Yes ',
+#' %(imported.38.0)s',
+["APNYes"], LINEEND,
+' >>',
+' >>', LINEEND,
+' /AS ',
+["Value"], LINEEND,
+' /DA ',
+PDFString('/ZaDb 0 Tf 0 g'), LINEEND,
+'/DV ',
+["Value"], LINEEND,
+'/F ',
+' 4 ',
+'/FT ',
+' /Btn ',
+'/H ',
+' /T ',
+'/MK ',
+' <<',
+' /AC (\\376\\377)',
+#PDFString('\376\377'),
+' /CA ',
+PDFString('4'),
+' /RC ',
+PDFString('\376\377'),
+' >> ',LINEEND,
+'/P ',
+["Page"], LINEEND,
+'/Rect',
+' [',["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"],
+' ] ',LINEEND,
+'/Subtype',
+' /Widget ',
+'/T ',
+["Name"], LINEEND,
+'/Type',
+' /Annot ',
+'/V ',
+["Value"], LINEEND,
+' >>']
+
+HIDE = PDFPattern([
+'<< '
+'/S '
+' /Hide '
+'>>'])
+
+def buttonStreamDictionary():
+ "everything except the length for the button appearance streams"
+ result = PDFDictionary()
+ result["SubType"] = "/Form"
+ result["BBox"] = "[0 0 16.77036 14.90698]"
+ font = PDFDictionary()
+ font["ZaDb"] = ZADB
+ resources = PDFDictionary()
+ resources["ProcSet"] = "[ /PDF /Text ]"
+ resources["Font"] = font
+ result["Resources"] = resources
+ return result
+
+def ButtonStream(content):
+ dict = buttonStreamDictionary()
+ result = PDFStream(dict, content)
+ result.filters = []
+ return result
+
+APDOFF = ButtonStream('0.749 g 0 0 16.7704 14.907 re f'+LINEEND)
+APDYES = ButtonStream(
+'0.749 g 0 0 16.7704 14.907 re f q 1 1 14.7704 12.907 re W '+
+'n BT /ZaDb 11.3086 Tf 0 g 1 0 0 1 3.6017 3.3881 Tm (4) Tj ET'+LINEEND)
+APNYES = ButtonStream(
+'q 1 1 14.7704 12.907 re W n BT /ZaDb 11.3086 Tf 0 g 1 0 0 1 3.6017 3.3881 Tm (4) Tj ET Q'+LINEEND)
+
+#==== script interpretation
+
+if __name__=="__main__":
+ test1() \ No newline at end of file