BodyBalanceEvaluation/backend/venv/Lib/site-packages/reportlab/pdfbase/acroform.py
2025-07-31 17:23:05 +08:00

1151 lines
45 KiB
Python

__all__=('AcroForm',)
from reportlab.pdfbase.pdfdoc import (PDFObject, PDFArray, PDFDictionary, PDFString, pdfdocEnc,
PDFName, PDFStream, PDFStreamFilterZCompress, escapePDF)
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 = []
stream = _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')
if value:
stream('BT\n/%(iFontName)s %(fontSize)s Tf\n%(textFill)s')
stream('1 0 0 1 %%(_4bw)s %s Tm' % fp_str(height - fontSize - _2bw))
for line in value.split('\n'):
stream('(%s) Tj\n0 %s Td' % (escPDF(line),fp_str(-leading)))
#the last change is not needed
_stream[-1] = _stream[-1][:_stream[-1].rfind('\n')]
stream('ET')
leading = fp_str(leading)
stream('Q\nEMC\n')
stream = ('\n'.join(_stream) % 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()