12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148 |
- __all__=('AcroForm',)
- from reportlab.pdfbase.pdfdoc import (PDFObject, PDFArray, PDFDictionary, PDFString, pdfdocEnc,
- PDFName, PDFStream, PDFStreamFilterZCompress, escapePDF)
- from reportlab.pdfgen.canvas import Canvas
- from reportlab.pdfbase.pdfmetrics import stringWidth
- from reportlab.lib.colors import Color, CMYKColor, Whiter, Blacker, opaqueColor
- from reportlab.lib.rl_accel import fp_str
- from reportlab.lib.utils import isStr, asNative
- import weakref
- visibilities = dict(
- visible=0,
- hidden=0,
- visibleNonPrinting=0,
- hiddenPrintable=0,
- )
- orientations = {
- 0: [],
- 90: [],
- 180: [],
- 270: [],
- }
- #adobe counts bits 1 - 32
- fieldFlagValues = dict(
- readOnly = 1<<0,
- required = 1<<1,
- noExport = 1<<2,
- noToggleToOff = 1<<14,
- radio = 1<<15,
- pushButton = 1<<16,
- radiosInUnison = 1<<25,
- #text fields
- multiline = 1<<12,
- password = 1<<13,
- fileSelect = 1<<20, #1.4
- doNotSpellCheck = 1<<22, #1.4
- doNotScroll = 1<<23, #1.4
- comb = 1<<24, #1.5
- richText = 1<<25, #1.5
- #choice fields
- combo = 1<<17,
- edit = 1<<18,
- sort = 1<<19,
- multiSelect = 1<<21, #1.4
- commitOnSelChange = 1<<26, #1.5
- )
- annotationFlagValues = dict(
- invisible=1<<0,
- hidden=1<<1,
- nozoom=1<<3,
- norotate=1<<4,
- noview=1<<5,
- readonly=1<<6,
- locked=1<<7, #1.4
- togglenoview=1<<8, #1.9
- lockedcontents=1<<9, #1.7
- )
- annotationFlagValues['print']=1<<2
- _bsStyles = dict(
- solid='S',
- dashed='D',
- bevelled='B',
- inset='I',
- underlined='U',
- )
- def bsPDF(borderWidth,borderStyle,dashLen):
- d = dict(W=borderWidth,S=PDFName(_bsStyles[borderStyle]))
- if borderStyle=='dashed':
- if not dashLen:
- dashLen = [3]
- elif not isinstance(dashLen,(list,tuple)):
- dashLen = [dashLen]
- d['D'] = PDFArray(dashLen)
- return PDFDictionary(d)
- def escPDF(s):
- return escapePDF(s).replace('%','\\045')
- def makeFlags(s,d=annotationFlagValues):
- if not isinstance(s,int):
- v = s
- s = 0
- for x in v.split():
- s |= d[x]
- return s
- class PDFFromString(PDFObject):
- def __init__(self,s):
- if not isStr(s):
- raise ValueError('need a unicode/bytes argument not %r' % s)
- self._s = s
- def format(self,document):
- return pdfdocEnc(self._s)
- class RadioGroup(PDFObject):
- def __init__(self,name,tooltip='',fieldFlags='noToggleToOff required radio'):
- if not name:
- raise ValueError('RadioGroup created with no name')
- self.TU = tooltip
- self.Ff = makeFlags(fieldFlags,fieldFlagValues)
- self.kids = []
- self.T = name
- self.V = None
- def format(self,doc):
- kids = self.kids
- d = len(kids)
- if d<2: raise ValueError('RadioGroup:%s has %d < 2 RadioBoxes' % (self.T,d))
- d = dict(
- Ff=self.Ff,
- Kids = PDFArray([k for k in self.kids]),
- FT = PDFName('Btn'),
- T = PDFString(self.T),
- #DA = PDFString('0 g'),
- )
- if self.V: d['V'] = PDFName(self.V)
- if self.TU: d['TU'] =PDFString(self.TU)
- r = PDFDictionary(d).format(doc)
- return r
- def _pdfObjToStr(obj):
- if isinstance(obj,PDFArray):
- return '[%s]' % ''.join((_pdfObjToStr(e) for e in obj.sequence))
- if isinstance(obj,PDFFromString):
- return obj._s
- return str(obj)
- class AcroForm(PDFObject):
- 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",
- }
- def __init__(self,canv,**kwds):
- self.referenceMap = {}
- self._canv = weakref.ref(canv)
- self.fonts = {}
- self.fields = []
- self._radios = {}
- self._refMap = {}
- self._pdfdocenc = {}
- self.sigFlags = None
- self.extras = {}
- @property
- def canv(self):
- _canv = self._canv()
- if _canv is None:
- raise ValueError('%s.canv is no longer available' % self.__class__.__name__)
- return _canv
- def fontRef(self,f):
- return '/Font << /%s %s >>' % (f,self.fonts[f])
- def format(self,doc):
- d = dict(
- Fields = PDFArray([self.getRef(f) for f in self.fields]),
- )
- if self.sigFlags: d['SigFlags'] = self.sigFlags
- if self.fonts:
- FK = list(sorted(self.fonts.keys()))
- F = [self.fontRef(f) for f in FK]
- d['DA'] = PDFString('/%s 0 Tf 0 g' % FK[0])
- d['DR'] = PDFFromString('<< /Encoding\n<<\n/RLAFencoding\n%s\n>>\n%s\n>>' % (self.encRefStr,'\n'.join(F)))
- d.update(self.extras)
- r = PDFDictionary(d).format(doc)
- return r
- def colorTuple(self,c):
- # ISO-32000-1, Table 189: An array of numbers that shall be in ther
- # range 0.0 to 1.0 specifying the colour [..]. The number of array
- # elements determines the colour space in which the colour shall
- # be defined:
- # 0 No colour; transparent 1 DeviceGray 3 DeviceRGB 4 DeviceCMYK
- if c is None or c.alpha == 0:
- return ()
- return c.cmyk() if isinstance(c,CMYKColor) else c.rgb()
- def streamFillColor(self,c):
- t = self.colorTuple(c)
- return fp_str(*t)+(' k' if len(t)==4 else ' rg')
-
- def streamStrokeColor(self,c):
- t = self.colorTuple(c)
- return fp_str(*t)+(' K' if len(t)==4 else ' RG')
- def checkboxAP(self,
- key, #N/D/R
- value, #Yes/Off
- buttonStyle='circle',
- shape='square',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- size=20,
- dashLen=3,
- ):
- stream = [].append
- ds = size
- if shape=='square':
- stream('q')
- streamFill = self.streamFillColor(fillColor)
- stream('1 g 1 G %(streamFill)s 0 0 %(size)s %(size)s re f')
- if borderWidth!=None:
- streamStroke = self.streamStrokeColor(borderColor)
- hbw = borderWidth*0.5
- smbw = size - borderWidth
- ds = smbw
- if borderStyle=='underlined':
- stream('%(streamStroke)s %(borderWidth)s w 0 %(hbw)s m %(size)s %(hbw)s l s')
- elif borderStyle in ('dashed','inset','bevelled','solid'):
- if borderStyle=='dashed':
- dash = ' [%s ] 0 d' % fp_str(dashLen)
- else:
- dash = ''
- stream('%(streamStroke)s%(dash)s %(borderWidth)s w %(hbw)s %(hbw)s %(smbw)s %(smbw)s re s')
- if borderStyle in ('bevelled','inset'):
- _2bw = 2*borderWidth
- sm2bw = size - _2bw
- ds = sm2bw
- bbs0 = Blacker(fillColor,0.5)
- bbs1 = fillColor
- if key!='D':
- bbs0, bbs1 = bbs1, bbs0
- bbs0 = self.streamFillColor(bbs0)
- bbs1 = self.streamFillColor(bbs1)
- stream('%(bbs0)s %(borderWidth)s %(borderWidth)s m %(borderWidth)s %(smbw)s l %(smbw)s %(smbw)s l %(sm2bw)s %(sm2bw)s l %(_2bw)s %(sm2bw)s l %(_2bw)s %(_2bw)s l f %(bbs1)s %(smbw)s %(smbw)s m %(smbw)s %(borderWidth)s l %(borderWidth)s %(borderWidth)s l %(_2bw)s %(_2bw)s l %(sm2bw)s %(_2bw)s l %(sm2bw)s %(sm2bw)s l f')
- stream('Q')
- elif shape=='circle':
- cas = lambda _r,**_casKwds: self.circleArcStream(size,_r,**_casKwds)
- r = size*0.5
- streamFill = self.streamFillColor(fillColor)
- stream('q 1 g 1 G %(streamFill)s')
- stream(cas(r))
- stream('f')
- stream('Q')
- if borderWidth!=None:
- stream('q')
- streamStroke = self.streamStrokeColor(borderColor)
- hbw = borderWidth*0.5
- ds = size - borderWidth
- if borderStyle=='underlined':
- stream('q %(streamStroke)s %(borderWidth)s w 0 %(hbw)s m %(size)s %(hbw)s l s Q')
- elif borderStyle in ('dashed','inset','bevelled','solid'):
- if borderStyle=='dashed':
- dash = ' [3 ] 0 d'
- else:
- dash = ''
- stream('%(streamStroke)s%(dash)s %(borderWidth)s w')
- stream(cas(r-hbw))
- stream('s')
- stream('Q')
- if borderStyle in ('bevelled','inset'):
- _3bwh = 3*hbw
- ds = size - _3bwh
- bbs0 = Blacker(fillColor,0.5)
- bbs1 = Whiter(fillColor,0.5)
- a0 = (0,1)
- a1 = (2,3)
- if borderStyle=='inset':
- bbs0, bbs1 = bbs1, bbs0
- if key!='D':
- bbs0, bbs1 = bbs1, bbs0
- bbs0 = self.streamStrokeColor(bbs0)
- bbs1 = self.streamStrokeColor(bbs1)
- stream('q %(bbs0)s %(borderWidth)s w')
- stream(cas(r-_3bwh,rotated=True,arcs=a0))
- stream('S Q %(bbs1)s q')
- stream(cas(r-_3bwh,rotated=True,arcs=a1))
- stream('S Q')
- if value=='Yes':
- textFillColor = self.streamFillColor(textColor)
- textStrokeColor = self.streamStrokeColor(textColor)
- stream('q %(textFillColor)s %(textStrokeColor)s')
- cbm = cbmarks[buttonStyle]
- if shape=='circle' and buttonStyle=='circle':
- stream(cas((max(r-(size-ds),1))*0.5))
- stream('f')
- else:
- stream(cbm.scaledRender(size,size-ds))
- stream('Q')
- stream = ('\n'.join(stream.__self__) % vars()).replace(' ',' ').replace('\n\n','\n')
- return self.makeStream(
- size, size, stream,
- Resources = PDFFromString('<< /ProcSet [/PDF] >>'),
- )
- @staticmethod
- def circleArcStream(size, r, arcs=(0,1,2,3), rotated=False):
- R = [].append
- rlen = R.__self__.__len__
- hsize = size * 0.5
- f = size / 20.0
- size *= f
- hsize *= f
- r *= f
- cp = fp_str(0.55231 * r)
- r = fp_str(r)
- hsize = fp_str(hsize)
- mx = '0.7071 0.7071 -0.7071 0.7071' if rotated else '1 0 0 1'
- R('%(mx)s %(hsize)s %(hsize)s cm')
- if 0 in arcs:
- if rlen()==1: R('%(r)s 0 m')
- R('%(r)s %(cp)s %(cp)s %(r)s 0 %(r)s c')
- if 1 in arcs:
- if rlen()==1: R('0 %(r)s m')
- R('-%(cp)s %(r)s -%(r)s %(cp)s -%(r)s 0 c')
- if 2 in arcs:
- if rlen()==1: R('-%(r)s 0 m')
- R('-%(r)s -%(cp)s -%(cp)s -%(r)s 0 -%(r)s c')
- if 3 in arcs:
- if rlen()==1: R('0 -%(r)s m')
- R('%(cp)s -%(r)s %(r)s -%(cp)s %(r)s 0 c')
- return '\n'.join(R.__self__) % vars()
- def zdMark(self,c,size,ds,iFontName):
- c = ZDSyms[c]
- W = H = size-ds
- fs = H/1.2
- w = float(stringWidth(c,'ZapfDingbats',fs))
- if w>W:
- fs *= W/w
- dx = ds + 0.5*(W-w)
- dy = 0
- return 'BT %(iFontName)s %(fs)s Tf %(dx)s %(dy)s Td %(fs)s TL (%(c)s) Tj ET' % vars()
- def getRef(self,obj):
- return self.canv._doc.Reference(obj)
- def getRefStr(self,obj):
- return asNative(self.getRef(obj).format(self.canv._doc))
- @staticmethod
- def stdColors(t,b,f):
- if isinstance(f,CMYKColor) or isinstance(t,CMYKColor) or isinstance(b,CMYKColor):
- return (t or CMYKColor(0,0,0,0.9), b or CMYKColor(0,0,0,0.9), f or CMYKColor(0.12,0.157,0,0))
- else:
- return (t or Color(0.1,0.1,0.1), b or Color(0.1,0.1,0.1), f or Color(0.8,0.843,1))
-
- @staticmethod
- def varyColors(key,t,b,f):
- if key!='N':
- func = Whiter if key=='R' else Blacker
- t,b,f = [func(c,0.9) for c in (t,b,f)]
- return t,b,f
- def checkForceBorder(self,x,y,width,height,forceBorder,shape,borderStyle,borderWidth,borderColor,fillColor):
- if forceBorder:
- canv = self.canv
- canv.saveState()
- canv.resetTransforms()
- if borderWidth!=None:
- hbw = 0.5*borderWidth
- canv.setLineWidth(borderWidth)
- canv.setStrokeColor(borderColor)
- s = 1
- else:
- s = hbw = 0
- width -= 2*hbw
- height -= 2*hbw
- x += hbw
- y += hbw
- canv.setFillColor(fillColor)
- if shape=='square':
- canv.rect(x,y,width,height,stroke=s,fill=1)
- else:
- r = min(width,height) * 0.5
- canv.circle(x+r,y+r,r,stroke=s,fill=1)
- canv.restoreState()
- def checkbox(self,
- checked=False,
- buttonStyle='check',
- shape='square',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- size=20,
- x=0,
- y=0,
- tooltip=None,
- name=None,
- annotationFlags='print',
- fieldFlags='required',
- forceBorder=False,
- relative=False,
- dashLen = 3,
- ):
- initialValue = 'Yes' if checked else 'Off'
- textColor,borderColor,fillColor=self.stdColors(textColor,borderColor,fillColor)
- canv = self.canv
- if relative:
- x, y = self.canv.absolutePosition(x,y)
- doc = canv._doc
- AP = {}
- for key in 'NDR':
- APV = {}
- tC,bC,fC = self.varyColors(key,textColor,borderColor,fillColor)
- for value in ('Yes','Off'):
- ap = self.checkboxAP(
- key,
- value,
- buttonStyle=buttonStyle,
- shape=shape,
- fillColor=fC,
- borderColor=bC,
- textColor=tC,
- borderWidth=borderWidth,
- borderStyle=borderStyle,
- size=size,
- dashLen=dashLen,
- )
- if ap._af_refstr in self._refMap:
- ref = self._refMap[ap._af_refstr]
- else:
- ref = self.getRef(ap)
- self._refMap[ap._af_refstr] = ref
- APV[value]=ref
- AP[key] = PDFDictionary(APV)
- del APV
- CB = dict(
- FT = PDFName('Btn'),
- P = doc.thisPageRef(),
- V = PDFName(initialValue),
- AS = PDFName(initialValue),
- #DV = PDFName(initialValue),
- Rect = PDFArray((x,y,x+size,y+size)),
- AP = PDFDictionary(AP),
- Subtype = PDFName('Widget'),
- Type = PDFName('Annot'),
- F = makeFlags(annotationFlags,annotationFlagValues),
- Ff = makeFlags(fieldFlags,fieldFlagValues),
- H=PDFName('N'),
- )
- if tooltip:
- CB['TU'] = PDFString(tooltip)
- if not name:
- name = 'AFF%03d' % len(self.fields)
- if borderWidth: CB['BS'] = bsPDF(borderWidth,borderStyle,dashLen)
- CB['T'] = PDFString(name)
- MK = dict(
- CA='(%s)' % ZDSyms[buttonStyle],
- BC=PDFArray(self.colorTuple(borderColor)),
- BG=PDFArray(self.colorTuple(fillColor)),
- )
- CB['MK'] = PDFDictionary(MK)
- CB = PDFDictionary(CB)
- self.canv._addAnnotation(CB)
- self.fields.append(self.getRef(CB))
- self.checkForceBorder(x,y,size,size,forceBorder,shape,borderStyle,borderWidth,borderColor,fillColor)
- def radio(self,
- value=None,
- selected=False,
- buttonStyle='circle',
- shape='circle',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- size=20,
- x=0,
- y=0,
- tooltip=None,
- name=None,
- annotationFlags='print',
- fieldFlags='noToggleToOff required radio',
- forceBorder=False,
- relative=False,
- dashLen=3,
- ):
- if name not in self._radios:
- group = RadioGroup(name,tooltip=tooltip,fieldFlags=fieldFlags)
- group._ref = self.getRef(group)
- self._radios[name] = group
- self.fields.append(group._ref)
- else:
- group = self._radios[name]
- fieldFlags = makeFlags(fieldFlags,fieldFlagValues)
- if fieldFlags!=group.Ff:
- raise ValueError('radio.%s.%s created with different flags' % (name,value))
- if not value:
- raise ValueError('bad value %r for radio.%s' % (value,name))
- initialValue = value if selected else 'Off'
- textColor,borderColor,fillColor=self.stdColors(textColor,borderColor,fillColor)
- if initialValue==value:
- if group.V is not None:
- if group.V!=value:
- raise ValueError('radio.%s.%s sets initial value conflicting with %s'%(name,value,group.V))
- else:
- group.V = value
- canv = self.canv
- if relative:
- x, y = self.canv.absolutePosition(x,y)
- doc = canv._doc
- AP = {}
- for key in 'NDR':
- APV = {}
- tC,bC,fC = self.varyColors(key,textColor,borderColor,fillColor)
- for v in (value,'Off'):
- ap = self.checkboxAP(
- key,
- 'Yes' if v==value else 'Off',
- buttonStyle=buttonStyle,
- shape=shape,
- fillColor=fC,
- borderColor=bC,
- textColor=tC,
- borderWidth=borderWidth,
- borderStyle=borderStyle,
- size=size,
- dashLen=dashLen,
- )
- if ap._af_refstr in self._refMap:
- ref = self._refMap[ap._af_refstr]
- else:
- ref = self.getRef(ap)
- self._refMap[ap._af_refstr] = ref
- APV[v]=ref
- AP[key] = PDFDictionary(APV)
- del APV
- RB = dict(
- FT = PDFName('Btn'),
- P = doc.thisPageRef(),
- AS = PDFName(initialValue),
- #DV = PDFName(initialValue),
- Rect = PDFArray((x,y,x+size,y+size)),
- AP = PDFDictionary(AP),
- Subtype = PDFName('Widget'),
- Type = PDFName('Annot'),
- F = makeFlags(annotationFlags,annotationFlagValues),
- Parent = group._ref,
- #DA = PDFString('1 g '+(self.streamFillColor(fillColor) if fillColor else '-0.25 0.75 -0.25 rg'))
- H=PDFName('N'),
- )
- #RB['T'] = PDFString(name)
- MK = dict(
- CA='(%s)' % ZDSyms[buttonStyle],
- BC=PDFArray(self.colorTuple(borderColor)),
- BG=PDFArray(self.colorTuple(fillColor)),
- )
- if borderWidth: RB['BS'] = bsPDF(borderWidth,borderStyle,dashLen)
- RB['MK'] = PDFDictionary(MK)
- RB = PDFDictionary(RB)
- self.canv._addAnnotation(RB)
- group.kids.append(self.getRef(RB))
- self.checkForceBorder(x,y,size,size,forceBorder,shape,borderStyle,borderWidth,borderColor,fillColor)
- def makeStream(self,
- width,
- height,
- stream,
- **D
- ):
- D['Matrix'] = PDFArray([1.0,0.0,0.0,1.0,0.0,0.0])
- D['BBox'] = PDFArray([0,0,width,height])
- D['Subtype'] = PDFName('Form')
- D['Type'] = PDFName('XObject')
- D['FormType'] = 1
- s = PDFStream(
- PDFDictionary(D),
- stream,
- filters = [PDFStreamFilterZCompress()] if self.canv._doc.compression else None,
- )
- #compute a lookup string
- s._af_refstr = stream+'\n'.join(('%s=%r' % (k,_pdfObjToStr(v)) for k,v in sorted(D.items())))
- return s
- def txAP(self,
- key, #N/D/R
- value,
- iFontName,
- rFontName,
- fontSize,
- shape='square',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- width=120,
- height=36,
- dashLen=3,
- wkind='textfield',
- labels=[],
- I=[],
- sel_bg='0.600006 0.756866 0.854904 rg',
- sel_fg='0 g',
- ):
- stream = [].append
- if opaqueColor(fillColor):
- streamFill = self.streamFillColor(fillColor)
- stream('%(streamFill)s\n0 0 %(width)s %(height)s re\nf')
- if borderWidth!=None and borderWidth>0 and opaqueColor(borderColor):
- hbw = borderWidth*0.5
- bww = width - borderWidth
- bwh = height - borderWidth
- _2bw = 2*borderWidth
- if borderStyle in ('bevelled','inset'):
- bw2w = width - _2bw
- bw2h = height - _2bw
- if borderStyle == 'bevelled':
- bbs0 = '1 g'
- if fillColor or borderColor:
- bbs1 = '-0.250977 0.749023 -0.250977 rg'
- else:
- bbs1 = '.75293 g'
- else:
- bbs0 = '.501953 g'
- bbs1 = '.75293 g'
- stream('%(bbs0)s\n%(borderWidth)s %(borderWidth)s m\n%(borderWidth)s %(bwh)s l\n%(bww)s %(bwh)s l\n%(bw2w)s %(bw2h)s l\n%(_2bw)s %(bw2h)s l\n%(_2bw)s %(_2bw)s l\nf\n%(bbs1)s\n%(bww)s %(bwh)s m\n%(bww)s %(borderWidth)s l\n%(borderWidth)s %(borderWidth)s l\n%(_2bw)s %(_2bw)s l\n%(bw2w)s %(_2bw)s l\n%(bw2w)s %(bw2h)s l\nf')
- else:
- hbw = _2bw = borderWidth = 0
- bww = width
- bwh = height
- undash = ''
- if opaqueColor(borderColor) and borderWidth:
- streamStroke = self.streamStrokeColor(borderColor)
- if borderStyle=='underlined':
- stream('%(streamStroke)s %(borderWidth)s w 0 %(hbw)s m %(width)s %(hbw)s l s')
- elif borderStyle in ('dashed','inset','bevelled','solid'):
- if borderStyle=='dashed':
- dash = '\n[%s ] 0 d\n' % fp_str(dashLen)
- undash = '[] 0 d'
- else:
- dash = '\n%s w' % borderWidth
- stream('%(streamStroke)s\n%(dash)s\n%(hbw)s %(hbw)s %(bww)s %(bwh)s re\ns')
- _4bw = 4*borderWidth
- w4bw = width - _4bw
- h4bw = height - _4bw
- textFill = self.streamFillColor(textColor)
- stream('/Tx BMC \nq\n%(_2bw)s %(_2bw)s %(w4bw)s %(h4bw)s re\nW\nn')
- leading = 1.2 * fontSize
- if wkind=='listbox':
- nopts = int(h4bw/leading)
- leading = h4bw/float(nopts)
- if nopts>len(labels):
- i0 = 0
- nopts = len(labels)
- elif len(I)<=1:
- i0 = I[0] if I else 0
- if i0:
- if i0<nopts:
- i0 = 0
- else:
- i = len(labels) - nopts
- if i0>=i:
- i0 = i
- else: #|I|>1
- if I[1]<nopts:
- i0 = 0
- else:
- i0 = I[0]
- y = len(labels)
- i = i0 + nopts
- if i>y: i0 = i - y
- ilim = min(y,i0+nopts)
- if I:
- i = i0
- y = height - _2bw - leading
- stream(sel_bg)
- while i<ilim:
- if i in I:
- #draw selected bg
- stream('%%(_2bw)s %s %%(w4bw)s %%(leading)s re\nf' % fp_str(y))
- y -= leading
- i += 1
- i = i0
- y = height - _2bw - fontSize
- stream('0 g\n0 G\n%(undash)s')
- while i<ilim:
- stream('BT')
- if i==i0:
- stream('/%(iFontName)s %(fontSize)s Tf')
- stream(sel_fg if i in I else '%(textFill)s')
- stream('%%(_4bw)s %s Td\n(%s) Tj' % (fp_str(y),escPDF(labels[i])))
- y -= leading
- i += 1
- stream('ET')
- else:
- stream('0 g\n0 G\n%(undash)s')
- y = height - fontSize - _2bw
- stream('BT\n/%(iFontName)s %(fontSize)s Tf\n%(textFill)s')
- for line in value.split('\n'):
- stream('%%(_4bw)s %s Td\n(%s) Tj' % (y,escPDF(line)))
- y -= leading
- stream('ET')
- leading = fp_str(leading)
- stream('Q\nEMC\n')
- stream = ('\n'.join(stream.__self__) % vars()).replace(' ',' ').replace('\n\n','\n')
- return self.makeStream(
- width, height, stream,
- Resources = PDFFromString('<< /ProcSet [/PDF /Text] /Font %(rFontName)s >>' % vars()),
- )
- def makeFont(self,fontName):
- if fontName is None:
- fontName = 'Helvetica'
- if fontName not in self.formFontNames:
- raise ValueError('form font name, %r, is not one of the standard 14 fonts' % fontName)
- fn = self.formFontNames[fontName]
- ref = self.getRefStr(PDFFromString('<< /BaseFont /%s /Subtype /Type1 /Name /%s /Type /Font /Encoding %s >>' % (
- fontName,fn,self.encRefStr)))
- if fn not in self.fonts:
- self.fonts[fn] = ref
- return ref, fn
- def _textfield(self,
- value='',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- width=120,
- height=36,
- x=0,
- y=0,
- tooltip=None,
- name=None,
- annotationFlags='print',
- fieldFlags='',
- forceBorder=False,
- relative=False,
- maxlen=100,
- fontName=None,
- fontSize=None,
- wkind=None,
- options=None,
- dashLen=3,
- ):
- rFontName, iFontName = self.makeFont(fontName)
- if fontSize is None:
- fontSize = 12
- textColor,borderColor,fillColor=self.stdColors(textColor,borderColor,fillColor)
- canv = self.canv
- if relative:
- x, y = self.canv.absolutePosition(x,y)
- doc = canv._doc
- rFontName = '<</%s %s>>' % (iFontName,rFontName)
- Ff = makeFlags(fieldFlags,fieldFlagValues)
- if wkind!='textfield':
- #options must be a list of pairs (label value)
- #value must be a list of the values
- FT='Ch'
- if wkind=='choice':
- Ff |= fieldFlagValues['combo'] #just in case
- V = []
- Opt = []
- AP = []
- I = []
- TF = []
- if not isinstance(options,(list,tuple)):
- raise TypeError('%s options=%r is wrong type' % (wkind,options))
- for v in options:
- if isStr(v):
- Opt.append(PDFString(v))
- l = v
- elif isinstance(v,(list,tuple)):
- if len(v)==1:
- v=l=v[0]
- else:
- l,v = v
- Opt.append(PDFArray([PDFString(v),PDFString(l)]))
- else:
- raise TypeError('%s option %r is wrong type' % (wkind,v))
- AP.append(v)
- TF.append(l)
- Opt = PDFArray(Opt)
- if value:
- if not isinstance(value,(list,tuple)):
- value = [value]
- for v in value:
- if v not in AP:
- if v not in TF:
- raise ValueError('%s value %r is not in option\nvalues %r\nor labels %r' % (wkind,v,AP,TF))
- else:
- v = AP[TF.index(v)]
- I.append(AP.index(v))
- V.append(PDFString(v))
- I.sort()
- if not (Ff & fieldFlagValues['multiSelect']) or len(value)==1:
- if wkind=='choice':
- value = TF[I[0]]
- else:
- value = value[:1]
- V = V[:1]
- V = V[0] if len(V)==1 else PDFArray(V)
- lbextras = dict(labels=TF,I=I,wkind=wkind)
- else:
- V = PDFString(value)
- else:
- I = Opt = []
- lbextras = {}
- FT='Tx'
- if not isStr(value):
- raise TypeError('textfield value=%r is wrong type' % value)
- V = PDFString(value)
- AP = {}
- for key in 'N':
- tC,bC,fC = self.varyColors(key,textColor,borderColor,fillColor)
- ap = self.txAP(
- key,
- value,
- iFontName,
- rFontName,
- fontSize,
- fillColor=fC,
- borderColor=bC,
- textColor=tC,
- borderWidth=borderWidth,
- borderStyle=borderStyle,
- width=width,
- height=height,
- dashLen = dashLen,
- **lbextras
- )
- if ap._af_refstr in self._refMap:
- ref = self._refMap[ap._af_refstr]
- else:
- ref = self.getRef(ap)
- self._refMap[ap._af_refstr] = ref
- AP[key] = ref
- TF = dict(
- FT = PDFName(FT),
- P = doc.thisPageRef(),
- V = V,
- #AS = PDFName(value),
- DV = V,
- Rect = PDFArray((x,y,x+width,y+height)),
- AP = PDFDictionary(AP),
- Subtype = PDFName('Widget'),
- Type = PDFName('Annot'),
- F = makeFlags(annotationFlags,annotationFlagValues),
- Ff = Ff,
- #H=PDFName('N'),
- DA=PDFString('/%s %d Tf %s' % (iFontName,fontSize, self.streamFillColor(textColor))),
- )
- if Opt: TF['Opt'] = Opt
- if I: TF['I'] = PDFArray(I)
- if maxlen:
- TF['MaxLen'] = maxlen
- if tooltip:
- TF['TU'] = PDFString(tooltip)
- if not name:
- name = 'AFF%03d' % len(self.fields)
- TF['T'] = PDFString(name)
- MK = dict(
- BG=PDFArray(self.colorTuple(fillColor)),
- )
- # Acrobat seems to draw a thin border when BS is defined, so only
- # include this if there actually is a border to draw
- if borderWidth:
- TF['BS'] = bsPDF(borderWidth,borderStyle,dashLen)
- MK['BC'] = PDFArray(self.colorTuple(borderColor))
- TF['MK'] = PDFDictionary(MK)
- TF = PDFDictionary(TF)
- self.canv._addAnnotation(TF)
- self.fields.append(self.getRef(TF))
- self.checkForceBorder(x,y,width,height,forceBorder,'square',borderStyle,borderWidth,borderColor,fillColor)
- def textfield(self,
- value='',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- width=120,
- height=36,
- x=0,
- y=0,
- tooltip=None,
- name=None,
- annotationFlags='print',
- fieldFlags='',
- forceBorder=False,
- relative=False,
- maxlen=100,
- fontName=None,
- fontSize=None,
- dashLen=3,
- ):
- return self._textfield(
- value=value,
- fillColor=fillColor,
- borderColor=borderColor,
- textColor=textColor,
- borderWidth=borderWidth,
- borderStyle=borderStyle,
- width=width,
- height=height,
- x=x,
- y=y,
- tooltip=tooltip,
- name=name,
- annotationFlags=annotationFlags,
- fieldFlags=fieldFlags,
- forceBorder=forceBorder,
- relative=relative,
- maxlen=maxlen,
- fontName=fontName,
- fontSize=fontSize,
- dashLen=dashLen,
- wkind='textfield',
- )
- def listbox(self,
- value='',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- width=120,
- height=36,
- x=0,
- y=0,
- tooltip=None,
- name=None,
- annotationFlags='print',
- fieldFlags='',
- forceBorder=False,
- relative=False,
- fontName=None,
- fontSize=None,
- dashLen=3,
- maxlen=None,
- options=[],
- ):
- return self._textfield(
- value=value,
- fillColor=fillColor,
- borderColor=borderColor,
- textColor=textColor,
- borderWidth=borderWidth,
- borderStyle=borderStyle,
- width=width,
- height=height,
- x=x,
- y=y,
- tooltip=tooltip,
- name=name,
- annotationFlags=annotationFlags,
- fieldFlags=fieldFlags,
- forceBorder=forceBorder,
- relative=relative,
- maxlen=maxlen,
- fontName=fontName,
- fontSize=fontSize,
- dashLen=dashLen,
- wkind='listbox',
- options = options,
- )
- def choice(self,
- value='',
- fillColor=None,
- borderColor=None,
- textColor=None,
- borderWidth=1,
- borderStyle='solid',
- width=120,
- height=36,
- x=0,
- y=0,
- tooltip=None,
- name=None,
- annotationFlags='print',
- fieldFlags='combo',
- forceBorder=False,
- relative=False,
- fontName=None,
- fontSize=None,
- dashLen=3,
- maxlen=None,
- options=[],
- ):
- return self._textfield(
- value=value,
- fillColor=fillColor,
- borderColor=borderColor,
- textColor=textColor,
- borderWidth=borderWidth,
- borderStyle=borderStyle,
- width=width,
- height=height,
- x=x,
- y=y,
- tooltip=tooltip,
- name=name,
- annotationFlags=annotationFlags,
- fieldFlags=fieldFlags,
- forceBorder=forceBorder,
- relative=relative,
- maxlen=maxlen,
- fontName=fontName,
- fontSize=fontSize,
- dashLen=dashLen,
- wkind='choice',
- options = options,
- )
- def checkboxRelative(self, **kwds):
- "same as checkbox except the x and y are relative to the canvas coordinate transform"
- kwds['relative']=True
- self.checkbox(**kwds)
- def radioRelative(self, **kwds):
- "same as radio except the x and y are relative to the canvas coordinate transform"
- kwds['relative']=True
- self.radio(**kwds)
- def textfieldRelative(self, **kwds):
- "same as textfield except the x and y are relative to the canvas coordinate transform"
- kwds['relative']=True
- self.textfield(**kwds)
- def listboxRelative(self, **kwds):
- "same as textfield except the x and y are relative to the canvas coordinate transform"
- kwds['relative']=True
- self.textfield(**kwds)
- def choiceRelative(self, **kwds):
- "same as textfield except the x and y are relative to the canvas coordinate transform"
- kwds['relative']=True
- self.textfield(**kwds)
- @property
- def encRefStr(self):
- if not self._pdfdocenc:
- self._pdfdocenc = PDFFromString('''<</Type /Encoding /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]>>''')
- return self.getRefStr(self._pdfdocenc)
- class CBMark:
- opNames = 'm l c h'.split()
- opCount = 1,1,3,0
- def __init__(self,ops,points,bounds,slack=0.05):
- self.ops = ops
- self.xmin,self.ymin,self.xmax,self.ymax = bounds
- self.points = points
- self.slack = slack
- def scaledRender(self,size,ds=0):
- '''
- >>> print(cbmarks['check'].scaledRender(20))
- 12.97075 14.68802 m 15.00139 17.16992 l 15.9039 18.1727 17.93454 18.67409 19.2883 18.67409 c 19.46379 18.27298 l 17.13231 15.51532 l 11.91783 8.62117 l 8.307799 3.030641 l 7.430362 1.526462 l 7.305014 1.275766 7.154596 .97493 6.9039 .824513 c 6.577994 .674095 5.825905 .674095 5.47493 .674095 c 4.672702 .674095 4.497214 .674095 4.321727 .799443 c 4.071031 .97493 3.945682 1.325905 3.770195 1.67688 c 3.218663 2.830084 2.240947 5.337047 2.240947 6.590529 c 2.240947 7.016713 2.491643 7.21727 2.817549 7.442897 c 3.344011 7.818942 4.0961 8.245125 4.747911 8.245125 c 5.249304 8.245125 5.299443 7.818942 5.449861 7.417827 c 5.951253 6.239554 l 6.026462 6.038997 6.252089 5.337047 6.527855 5.337047 c 6.778552 5.337047 7.079387 5.913649 7.179666 6.089136 c 12.97075 14.68802 l h f
- >>> print(cbmarks['cross'].scaledRender(20))
- 19.9104 17.43931 m 12.41908 10 l 19.9104 2.534682 l 18.37572 1 l 10.9104 8.491329 l 3.445087 1 l 1.910405 2.534682 l 9.427746 10 l 1.910405 17.46532 l 3.445087 19 l 10.9104 11.50867 l 18.37572 19 l 19.9104 17.43931 l h f
- >>> print(cbmarks['circle'].scaledRender(20))
- 1.872576 9.663435 m 1.872576 14.64958 5.936288 18.61357 10.89751 18.61357 c 15.8338 18.61357 19.87258 14.59972 19.87258 9.663435 c 19.87258 4.727147 15.8338 .688366 10.89751 .688366 c 5.936288 .688366 1.872576 4.677285 1.872576 9.663435 c h f
- >>> print(cbmarks['star'].scaledRender(20))
- 10.85542 18.3253 m 12.90361 11.84337 l 19.84337 11.84337 l 14.25301 7.650602 l 16.42169 1 l 10.85542 5.096386 l 5.289157 1 l 7.481928 7.650602 l 1.843373 11.84337 l 8.759036 11.84337 l 10.85542 18.3253 l h f
- >>> print(cbmarks['diamond'].scaledRender(20))
- 17.43533 9.662031 m 15.63282 7.484006 l 10.85118 .649513 l 8.422809 4.329624 l 5.919332 7.659249 l 4.267038 9.662031 l 6.16968 12.0153 l 10.85118 18.64951 l 12.75382 15.4701 15.00695 12.49096 17.43533 9.662031 c h f
- '''
- #work out the scale and translation
- W = H = size - 2*ds
- xmin = self.xmin
- ymin = self.ymin
- w = self.xmax-xmin
- h = self.ymax-ymin
- slack = self.slack*min(W,H)
- sx = (W - 2*slack)/float(w)
- sy = (H - 2*slack)/float(h)
- sx = sy = min(sx,sy)
- w *= sx
- h *= sy
- dx = ds+(W - w)*0.5
- dy = ds+(H - h)*0.5
- xsc = lambda v: fp_str((v-xmin)*sx+dx)
- ysc = lambda v: fp_str((v-ymin)*sy+dy)
- opNames = self.opNames
- opCount = self.opCount
- C = [].append
- i = 0
- points = self.points
- for op in self.ops:
- c = opCount[op]
- for _ in range(c):
- C(xsc(points[i]))
- C(ysc(points[i+1]))
- i += 2
- C(opNames[op])
- C('f')
- return ' '.join(C.__self__)
- cbmarks = dict(
- check=CBMark(
- [0, 1, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 3],
- [462, 546, 543, 645, 579, 685, 660, 705, 714, 705, 721, 689, 628, 579, 420, 304, 276, 81, 241, 21, 236, 11, 230, -1, 220, -7, 207, -13, 177, -13, 163, -13, 131, -13, 124, -13, 117, -8, 107, -1, 102, 13, 95, 27, 73, 73, 34, 173, 34, 223, 34, 240, 44, 248, 57, 257, 78, 272, 108, 289, 134, 289, 154, 289, 156, 272, 162, 256, 182, 209, 185, 201, 194, 173, 205, 173, 215, 173, 227, 196, 231, 203, 462, 546],
- (34,-12,721,706),
- ),
- cross = CBMark(
- [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
- [727, 632, 439, 346, 727, 59, 668, 0, 381, 288, 94, 0, 35, 59, 324, 346, 35, 633, 94, 692, 381, 404, 668, 692, 727, 632],
- (35,0,727,692),
- ),
- circle = CBMark(
- [0, 2, 2, 2, 2, 3],
- [35, 346, 35, 546, 198, 705, 397, 705, 595, 705, 757, 544, 757, 346, 757, 148, 595, -14, 397, -14, 198, -14, 35, 146, 35, 346],
- (35,-14,757,705),
- ),
- star = CBMark(
- [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3],
- [409, 705, 494, 436, 782, 436, 550, 262, 640, -14, 409, 156, 178, -14, 269, 262, 35, 436, 322, 436, 409, 705],
- (35,-14,782,705),
- ),
- diamond = CBMark(
- [0, 1, 1, 1, 1, 1, 1, 1, 2, 3],
- [560, 346, 488, 259, 297, -14, 200, 133, 100, 266, 34, 346, 110, 440, 297, 705, 373, 578, 463, 459, 560, 346],
- (34,-14,560,705),
- ),
- )
- ZDSyms=dict(check='4',cross='5',circle='l',star='N',diamond='u')
- if __name__ == "__main__":
- import doctest
- doctest.testmod()
|