1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2006 Michael Schindler <m-schindler@users.sourceforge.net>
6 # This file is part of PyX (http://pyx.sourceforge.net/).
8 # PyX is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # PyX is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with PyX; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 import cStringIO
, math
23 import canvasitem
, bbox
, pdfwriter
, color
, unit
24 from font
.font
import PDFHelvetica
, PDFZapfDingbats
27 # - discuss behaviour under transformations with André and Jörg
28 # - what about fillstyles here?
29 # - where should e.g. a font be added to the registry:
30 # in processPDF or in __init__ of the PDF-item?
31 # - test for double occurrance of field names:
32 # this leads to wrong/no display
34 # TODO horizontal alignment in textfields
37 # flags for annotations:
38 PDFannotflags
= [("invisible", 0), ("hidden", 1), ("printable", 2),
39 ("nozoom", 3), ("norotate", 4), ("noview", 5), ("readonly", 6)]
41 # flags for form fields
42 PDFformflags
= [("readonly", 0), ("required", 1), ("noexport", 2),
43 # flags for the button field
44 ("notoggletooff", 14), ("radio", 15), ("pushbutton", 16),
45 # flags for the choice list field
46 ("combo", 17), ("edit", 18), ("sort", 19), ("multiselect", 21),
47 # flags for the text field
48 ("multiline", 12), ("password", 13), ("fileselect", 20), ("donotspellcheck", 22),
53 """A helper class for handling flags in pdf forms and annotations"""
55 def __init__(self
, value
=None):
58 def is_set(self
, bit
):
59 return self
.value
is not None and (self
.value
& 1<<bit
) == 1<<bit
62 if self
.value
is None:
65 self
.value
= self
.value |
1<<bit
68 if self
.value
is not None:
69 self
.value
= self
.value
& ~
(1<<bit
)
75 return self
.value
.__str
__()
77 def _pdfflags(flags
): # <<<
78 """Splits flags into annotation/form part
79 the flag for the annotation dictionary
80 the flag for the form (field) dictionary
82 All flags are handled equally here, independent of their use
83 for the specific form field.
86 # we initialize with 0 and set only those flags which are not 0
87 annotflag
= flag(value
=0)
88 formflag
= flag(value
=0)
90 for key
, value
in PDFannotflags
:
91 if flags
.has_key(key
) and flags
[key
]:
94 for key
, value
in PDFformflags
:
95 if flags
.has_key(key
) and flags
[key
]:
98 return int(annotflag
), int(formflag
)
100 def _pdfalignment(string
): # <<<
108 def _topt(value
, type="u", un
="cm"): # <<<
109 if isinstance(value
, unit
.length
):
110 return unit
.topt(value
)
112 return unit
.topt(unit
.length(value
, type, un
))
114 def _simplestring(text
): # <<<
121 def _sizetrafo(s
, tr
): # <<<
122 x1
, y1
= tr
.apply_pt(s
, s
)
123 x0
, y0
= tr
.apply_pt(0, 0)
124 return math
.hypot(x1
- x0
, y1
- y0
) * math
.sqrt(0.5)
128 class formfield(canvasitem
.canvasitem
): # <<<
129 """Base class for acroforms"""
131 defaultflags
= dict()
133 def selectflags(self
, flags
):
134 newflags
= dict(**self
.defaultflags
)
135 # overwrite the default flags with given values:
136 for key
, value
in flags
.items():
137 if newflags
.has_key(key
):
138 newflags
[key
] = value
140 raise RuntimeError("unknown argument \"%s\" to formfield" % key
)
144 return bbox
.bbox_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
146 def processPS(self
, file, writer
, context
, registry
, bbox
):
147 raise RuntimeError("postscript output of forms is not supported")
151 class textfield(formfield
): # <<<
152 """An interactive pdf form field for text input.
154 The "name" is used for the graphical user interface and for exporing the input data.
155 Note that the behaviour under rotations is undefined."""
157 defaultflags
= dict(invisible
=0, hidden
=0, printable
=1, nozoom
=0, norotate
=0, noview
=0,
158 readonly
=0, required
=0, noexport
=0, multiline
=0, password
=0, fileselect
=0,
159 donotspellcheck
=1, donotscroll
=0)
161 def __init__(self
, x
, y
, width
, height
, name
, defaultvalue
="", fontsize
=10, font
=PDFHelvetica
,
162 fontrelleading
=1.16, borderwidth
=0, align
="l", **flags
):
164 self
.llx_pt
, self
.lly_pt
= _topt(x
), _topt(y
)
165 self
.urx_pt
, self
.ury_pt
= _topt(x
+width
), _topt(y
+height
)
167 self
.defaultvalue
= defaultvalue
168 self
.fontsize_pt
= _topt(fontsize
, "x", "pt")
170 self
.fontrelleading
= fontrelleading
171 self
.borderwidth_pt
= _topt(borderwidth
, "x", "pt")
173 self
.flags
= self
.selectflags(flags
)
175 def processPDF(self
, file, writer
, context
, registry
, bbox
):
176 # the bounding box is transformed by the canvas
179 # the annotation rectangle must be transformed separately:
180 llx_pt
, lly_pt
= context
.trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)
181 urx_pt
, ury_pt
= context
.trafo
.apply_pt(self
.urx_pt
, self
.ury_pt
)
182 fontsize_pt
= _sizetrafo(self
.fontsize_pt
, context
.trafo
)
183 borderwidth_pt
= _sizetrafo(self
.borderwidth_pt
, context
.trafo
)
185 # we create numbers from the flags given
186 annotflag
, formflag
= _pdfflags(self
.flags
)
187 alignflag
= _pdfalignment(self
.align
)
189 registry
.add(PDFtextfield((llx_pt
, lly_pt
, urx_pt
, ury_pt
), self
.name
, self
.defaultvalue
,
190 fontsize_pt
, self
.font
, self
.fontrelleading
*fontsize_pt
,
191 borderwidth_pt
, (not self
.flags
["multiline"]),
192 alignflag
, annotflag
, formflag
, context
.fillstyles
, writer
, registry
))
194 class PDFtextfield(pdfwriter
.PDFobject
): # <<<
196 def __init__(self
, bb_pt
, name
, defaultvalue
, fontsize
, font
, fontleading
,
197 borderwidth
, vcenter
,
198 alignflag
, annotflag
, formflag
, fillstyles
, writer
, registry
):
200 pdfwriter
.PDFobject
.__init
__(self
, "formfield_text")
202 # append this formfield to the global document form
203 # and to the annotation list of the page:
205 for object in registry
.objects
:
206 if object.type == "form":
208 self
.PDFform
= object
209 elif object.type == "annotations":
214 self
.defaultvalue
= defaultvalue
215 self
.fontsize
= fontsize
217 if self
.font
is None:
218 self
.font
= PDFHelvetica
219 self
.fontleading
= fontleading
220 self
.borderwidth
= borderwidth
221 self
.alignflag
= alignflag
222 self
.formflag
= formflag
223 self
.annotflag
= annotflag
225 self
.registry
= pdfwriter
.PDFregistry()
226 self
.registry
.addresource("Font", self
.font
.name
, self
.font
, procset
="Text")
227 self
.registry
.add(self
.font
)
229 if self
.defaultvalue
:
230 text
= self
.defaultvalue
.split("\n")
231 self
.defaulttext
= PDFdefaulttext(writer
, registry
, self
.fontsize
, self
.font
,
232 self
.fontleading
, text
, self
.bb_pt
, self
.borderwidth
, vcenter
)
233 self
.registry
.add(self
.defaulttext
)
235 self
.defaulttext
= None
237 # process some fillstyles:
238 fillstring
= cStringIO
.StringIO()
239 for attr
in fillstyles
:
240 if 1:#isinstance(attr, color.color):
241 cont
= pdfwriter
.context()
244 attr
.processPDF(fillstring
, writer
, cont
, self
.registry
, bbox
)
245 self
.fillstyles
= fillstring
.getvalue()
248 registry
.mergeregistry(self
.registry
)
250 def write(self
, file, writer
, registry
):
251 ### the dictionary entries for the annotation
252 file.write("<</Type /Annot\n")
253 file.write("/P %d 0 R\n" % registry
.getrefno(self
.PDFform
)) # reference to the page objects
254 file.write("/Rect [%f %f %f %f]\n" % self
.bb_pt
) # the annotation rectangle
255 #ile.write("/BS <</W 0 /S /S>>\n") # border style dictionary
256 file.write("/Border [0 0 %f]\n" % self
.borderwidth
) # border style
257 file.write("/F %d\n" % self
.annotflag
)
258 ### the dictionary entries for the widget annotations
259 file.write("/Subtype /Widget\n")
260 file.write("/H /N\n") # highlight behaviour
262 file.write("/AP <</N %d 0 R >>\n" % registry
.getrefno(self
.defaulttext
)) # appearance dictionary
263 ### the dictionary entries for the form field
264 file.write("/FT /Tx\n") # type of the form field
265 file.write("/T (%s)\n" % self
.name
) # partial field name
266 file.write("/TU (%s)\n" % self
.name
) # field name for the user-interface
267 file.write("/TM (%s)\n" % self
.name
) # field name for exporting the data
268 file.write("/V (%s)\n" % self
.defaultvalue
) # starting value
269 file.write("/DV (%s)\n" % self
.defaultvalue
) # reset value
270 file.write("/Ff %d\n" % self
.formflag
) # flags for various purposes
271 ### the dictionary entries for the text field
273 self
.registry
.writeresources(file) # default resources for appearance
274 file.write("/DA (%s /%s %f Tf %f TL)\n" % (self
.fillstyles
, self
.font
.name
, self
.fontsize
, self
.fontleading
)) # default appearance string
275 file.write("/Q %d\n" % self
.alignflag
)
278 class PDFdefaulttext(pdfwriter
.PDFobject
): # <<<
280 def __init__(self
, writer
, registry
, fontsize
, font
, fontleading
, texts
, bb
, borderwidth
, vcenter
):
282 pdfwriter
.PDFobject
.__init
__(self
, "defaulttext")
284 self
.fontsize
= fontsize
285 self
.fontleading
= fontleading
287 self
.registry
= pdfwriter
.PDFregistry()
288 self
.registry
.addresource("Font", self
.font
.name
, self
.font
, procset
="Text")
289 self
.registry
.add(self
.font
)
291 self
.bb
= (0, 0, bb
[2] - bb
[0], bb
[3] - bb
[1])
292 self
.texts
= [t
for t
in texts
if t
]
294 self
.borderwidth
= borderwidth
295 # try to imitate the shifting of PDF:
296 # the font orientation point is on the baseline of the text
297 self
.hshift
= 2*self
.borderwidth
300 self
.vshift
= 0.5 * (bb
[3] - bb
[1]) + (len(self
.texts
) / 2.0 - 1)*self
.fontleading
+ baselinevrel
*self
.fontsize
301 elif (bb
[3] - bb
[1]) < self
.fontleading
+ 4*self
.borderwidth
:
303 self
.vshift
= 2*self
.borderwidth
+ baselinevrel
* self
.fontsize
304 #self.vshift = 0.5 * (bb[3] - bb[1]) - (0.5 - baselinevrel - 0.5*addrelshift)*self.fontsize
308 self
.vshift
= (bb
[3] - bb
[1]) - 2*self
.borderwidth
- self
.fontleading
+ (baselinevrel
- addrelshift
)*self
.fontsize
310 registry
.mergeregistry(self
.registry
)
313 def write(self
, file, writer
, registry
):
314 content
= "/Tx BMC q BT /%s %f Tf %f TL %f %f Td (%s) Tj" % (self
.font
.name
, self
.fontsize
, self
.fontleading
, self
.hshift
, self
.vshift
, self
.texts
[0])
315 for text
in self
.texts
[1:]:
316 content
+= " (%s)'" % (text
)
317 content
+= " ET Q EMC\n"
320 content
= zlib
.compress(content
)
323 file.write("/Type /XObject\n")
324 file.write("/Subtype /Form\n")
325 file.write("/BBox [%f %f %f %f]\n" % self
.bb
)
326 #ile.write("/Matrix [0.98 0.17 -0.17 0.98 0 0]\n")
327 file.write("/Resources ")
328 self
.registry
.writeresources(file) # default resources for appearance
329 file.write("/Length %i\n" % len(content
))
331 file.write("/Filter /FlateDecode\n")
335 file.write("endstream\n")
339 class radiobuttons(formfield
): # <<<
341 """A set of related buttons that can each be on or off.
343 Typically, at most one radio button in a set may be on at any
344 given time, and selecting any one of the buttons
345 automatically deselects all the others.
347 Note that the behaviour under rotations is undefined."""
349 defaultflags
= dict(invisible
=0, hidden
=0, printable
=1, nozoom
=0,
350 norotate
=0, noview
=0, readonly
=0, required
=0, noexport
=0, notoggletooff
=0)
352 def __init__(self
, positions
, name
, values
, defaultvalue
=None, size
=10, baselinerelpos
=0.2, **flags
):
355 self
.size_pt
= _topt(size
, "x", "pt")
356 self
.positions_pt
= [(_topt(x
), _topt(y
) - baselinerelpos
*self
.size_pt
) for x
, y
in positions
]
357 self
.flags
= self
.selectflags(flags
)
358 self
.flags
["radio"] = 1
360 self
.defaultvalue
= defaultvalue
363 llx
= min([x
[0] for x
in self
.positions_pt
])
364 lly
= min([x
[1] for x
in self
.positions_pt
])
365 urx
= max([x
[0] for x
in self
.positions_pt
]) + self
.size_pt
366 ury
= max([x
[1] for x
in self
.positions_pt
]) + self
.size_pt
367 return bbox
.bbox_pt(llx
, lly
, urx
, ury
)
369 def processPDF(self
, file, writer
, context
, registry
, bbox
):
370 # the bbox is transformed by the canvas
373 # the annotation rectangle must be transformed separately:
374 positions_pt
= [context
.trafo
.apply_pt(x
, y
) for x
, y
in self
.positions_pt
]
375 size_pt
= _sizetrafo(self
.size_pt
, context
.trafo
)
377 # we create numbers from the flags given
378 annotflag
, formflag
= _pdfflags(self
.flags
)
380 onstate
= PDFButtonState(writer
, registry
,
381 10, PDFZapfDingbats
, bgchar
="m", fgchar
="8",
382 bgscale
=1.1, bgrelshift
=(0, 0.18), fgrelshift
=(0.12, 0.26))
383 offstate
= PDFButtonState(writer
, registry
,
384 10, PDFZapfDingbats
, bgchar
="m", fgchar
=None,
385 bgscale
=1.1, bgrelshift
=(0, 0.18))
386 registry
.add(onstate
)
387 registry
.add(offstate
)
389 registry
.add(PDFbuttonlist(positions_pt
, self
.name
, size_pt
, self
.values
, self
.defaultvalue
,
390 annotflag
, formflag
, onstate
, offstate
, writer
, registry
))
392 class checkbox(formfield
): # <<<
394 """Toggles between two states, on and off
396 Note that the behaviour under rotations is undefined."""
398 defaultflags
= dict(invisible
=0, hidden
=0, printable
=1, nozoom
=0,
399 norotate
=0, noview
=0, readonly
=0, required
=0, noexport
=0)
401 def __init__(self
, x
, y
, name
, defaulton
=0, size
=10, baselinerelpos
=0.2, **flags
):
404 self
.size_pt
= _topt(size
, "x", "pt")
405 self
.llx_pt
, self
.lly_pt
= _topt(x
), _topt(y
) - baselinerelpos
*self
.size_pt
406 self
.urx_pt
, self
.ury_pt
= self
.llx_pt
+ self
.size_pt
, self
.lly_pt
+ self
.size_pt
407 self
.flags
= self
.selectflags(flags
)
408 self
.defaulton
= defaulton
410 def processPDF(self
, file, writer
, context
, registry
, bbox
):
411 # the bbox is transformed by the canvas
414 # the annotation rectangle must be transformed separately:
415 positions_pt
= [context
.trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)]
416 size_pt
= _sizetrafo(self
.size_pt
, context
.trafo
)
418 # we create numbers from the flags given
419 annotflag
, formflag
= _pdfflags(self
.flags
)
421 onstate
= PDFButtonState(writer
, registry
,
422 10, PDFZapfDingbats
, bgchar
="o", fgchar
="4",
423 bgscale
=1.2, bgrelshift
=(0, 0.08), fgscale
=0.9, fgrelshift
=(0.15, 0.25))
424 offstate
= PDFButtonState(writer
, registry
,
425 10, PDFZapfDingbats
, bgchar
="o", fgchar
=None,
426 bgscale
=1.2, bgrelshift
=(0, 0.08))
427 registry
.add(onstate
)
428 registry
.add(offstate
)
435 registry
.add(PDFbuttonlist(positions_pt
, self
.name
, size_pt
, ["Yes"], default
,
436 annotflag
, formflag
, onstate
, offstate
, writer
, registry
))
438 class PDFbuttonlist(pdfwriter
.PDFobject
): # <<<
440 def __init__(self
, positions_pt
, name
, size_pt
, values
, defaultvalue
, annotflag
, formflag
,
441 onstate
, offstate
, writer
, registry
):
443 pdfwriter
.PDFobject
.__init
__(self
, "formfield_buttonlist")
445 # append this formfield to the global document form
446 # but we do not treat this as a fully valid annotation field
447 for object in registry
.objects
:
448 if object.type == "form":
452 self
.formflag
= formflag
453 self
.annotflag
= annotflag
455 self
.size_pt
= size_pt
456 self
.defaultvalue
= defaultvalue
457 self
.onstate
= onstate
458 self
.offstate
= offstate
461 for i
, pos_pt
, value
in zip(range(len(values
)), positions_pt
, values
):
462 chbox
= PDFcheckboxfield(pos_pt
, value
, size_pt
, _simplestring(value
), (value
== defaultvalue
),
463 self
, self
.onstate
, self
.offstate
, self
.annotflag
, self
.formflag
, writer
, registry
)
464 self
.checkboxes
.append(chbox
)
467 def write(self
, file, writer
, registry
):
468 ### implementation note: There are some (undocumented) PDF flaws which
469 ### do not allow to inherit certain variables:
470 ### * The parent button may not have /Ff (otherwise, notoggletooff fails)
471 ### * The Kids of a radio button may not have a /T on their own (otherwise, they are not displayed)
472 ### * The /BS and /Border do not draw anything.
473 ### Nevertheless, the border width of /Border is used
475 ### the dictionary entries for the annotation
477 ### the dictionary entries for the form field
478 file.write("/FT /Btn\n") # type of the form field
479 file.write("/Kids [%s]\n" % " ".join(["%d 0 R" % registry
.getrefno(x
) for x
in self
.checkboxes
]))
480 file.write("/T (%s)\n" % self
.name
) # partial field name
481 file.write("/TU (%s)\n" % self
.name
) # field name for the user-interface
482 file.write("/TM (%s)\n" % self
.name
) # field name for exporting the data
483 ### the dictionary entries for the radiobuttons field
484 file.write("/V /%s\n" % self
.defaultvalue
)
487 class PDFcheckboxfield(pdfwriter
.PDFobject
): # <<<
489 def __init__(self
, pos_pt
, name
, size_pt
, valuename
, defaulton
, parent
, onstate
, offstate
, annotflag
, formflag
, writer
, registry
):
491 pdfwriter
.PDFobject
.__init
__(self
, "formfield_checkbox")
493 # we treat this as an annotation only, since the parent is
494 # already in the form field
496 for object in registry
.objects
:
497 if object.type == "form":
498 assert self
.PDFform
is None
499 self
.PDFform
= object
500 if object.type == "annotations":
503 self
.bb_pt
= (pos_pt
[0], pos_pt
[1], pos_pt
[0] + size_pt
, pos_pt
[1] + size_pt
)
505 self
.size_pt
= size_pt
506 self
.valuename
= valuename
508 self
.defaultvalue
= self
.valuename
510 self
.defaultvalue
= "Off"
512 self
.onstate
= onstate
513 self
.offstate
= offstate
514 self
.annotflag
= annotflag
515 self
.formflag
= formflag
517 def write(self
, file, writer
, registry
):
518 ### the dictionary entries for the annotation
520 file.write("/Type /Annot\n")
521 file.write("/Subtype /Widget\n")
522 file.write("/P %d 0 R\n" % registry
.getrefno(self
.PDFform
)) # reference to the page objects
523 file.write("/Rect [%f %f %f %f]\n" % self
.bb_pt
) # the annotation rectangle
524 file.write("/F %d\n" % self
.annotflag
) # flags
525 ### the dictionary entries for the widget annotations
526 file.write("/H /N\n") # hightlight behaviour
527 ### the dictionary entries for the form field
528 file.write("/FT /Btn\n") # type of the form field
529 file.write("/Parent %d 0 R\n" % registry
.getrefno(self
.parent
)) # only for hierarchy
530 file.write("/AP << /N << /%s %d 0 R /Off %d 0 R >> >>\n" % (self
.valuename
, registry
.getrefno(self
.onstate
), registry
.getrefno(self
.offstate
)))
531 file.write("/AS /%s\n" % self
.defaultvalue
)
532 file.write("/Ff %d\n" % self
.formflag
) # Ff may not come from parent!
535 class PDFButtonState(pdfwriter
.PDFobject
): # <<<
537 def __init__(self
, writer
, registry
, fontsize
, font
, bgchar
, fgchar
,
538 bgscale
=None, bgrelshift
=None, fgscale
=None, fgrelshift
=None):
540 pdfwriter
.PDFobject
.__init
__(self
, "buttonstate", "buttonstate" + "_".join(map(str, map(id, [fontsize
, font
, bgchar
, fgchar
, bgscale
, bgrelshift
, fgscale
, fgrelshift
]))))
542 self
.fontsize
= fontsize
543 registry
.addresource("Font", self
.font
.name
, self
.font
, procset
="Text")
544 registry
.add(self
.font
)
545 self
.bb
= 0, 0, fontsize
, fontsize
549 if bgscale
is None and bgrelshift
is not None:
551 if bgscale
is not None and bgrelshift
is None:
553 if bgscale
is not None:
554 self
.bgtrafo
= "%f 0 0 %f %f %f Tm" % (bgscale
, bgscale
, bgrelshift
[0]*self
.fontsize
, bgrelshift
[1]*self
.fontsize
)
558 if fgscale
is None and fgrelshift
is not None:
560 if fgscale
is not None and fgrelshift
is None:
562 if fgscale
is not None:
563 self
.fgtrafo
= "%f 0 0 %f %f %f Tm" % (fgscale
, fgscale
, fgrelshift
[0]*self
.fontsize
, fgrelshift
[1]*self
.fontsize
)
567 def write(self
, file, writer
, registry
):
570 content
+= "q BT /%s %f Tf %s (%s) Tj ET Q\n" % (self
.font
.name
, self
.fontsize
, self
.bgtrafo
, self
.bgchar
)
572 content
+= "q BT /%s %f Tf %s (%s) Tj ET Q\n" % (self
.font
.name
, self
.fontsize
, self
.fgtrafo
, self
.fgchar
)
575 content
= zlib
.compress(content
)
578 file.write("/Type /XObject\n")
579 file.write("/Subtype /Form\n")
580 file.write("/BBox [%f %f %f %f]\n" % self
.bb
)
581 #ile.write("/Matrix [0.98 0.17 -0.17 0.98 0 0]\n")
582 file.write("/Resources <</Font << /%s %d 0 R >> /ProcSet [/PDF /Text] >>\n" %
583 (self
.font
.name
, registry
.getrefno(self
.font
)))
584 file.write("/Length %i\n" % len(content
))
586 file.write("/Filter /FlateDecode\n")
590 file.write("endstream\n")
593 ## Zapf Dingbats symbols for further buttonstates:
594 # "3" = thin checkmark
595 # "4" = thick checkmark
596 # "5" = thin large cross
597 # "6" = thick large cross
598 # "7" = thin small cross
599 # "8" = thick small cross
600 # "l" = filled circle
602 # "n" = filled rectangle
603 # "o" = empty rectangle (shadow bottom right)
604 # "p" = empty rectangle (shadow top right)
605 # "q" = empty box (to bottom right)
606 # "r" = empty box (to top right)
610 class choicefield(formfield
): # <<<
611 """An interactive pdf form field for text input.
613 The name is used for the graphical user interface and for exporing the input data.
614 Note that the behaviour under rotations is undefined."""
616 defaultflags
= dict(invisible
=0, hidden
=0, printable
=1, nozoom
=0,
617 norotate
=0, noview
=0, readonly
=0, required
=0, noexport
=0, combo
=1,
618 edit
=0, sort
=0, multiselect
=0, donotspellcheck
=1)
620 def __init__(self
, x
, y
, width
, height
, name
, values
, defaultvalue
=None, fontsize
=10, font
=None,
621 borderwidth
=0, align
="l", **flags
):
623 self
.llx_pt
, self
.lly_pt
= _topt(x
), _topt(y
)
624 self
.urx_pt
, self
.ury_pt
= _topt(x
+width
), _topt(y
+height
)
627 self
.defaultvalue
= defaultvalue
628 self
.fontsize_pt
= _topt(fontsize
, "x", "pt")
629 self
.font
= font
# TODO: add the generic fonts
630 self
.borderwidth_pt
= _topt(borderwidth
, "x", "pt")
631 self
.flags
= self
.selectflags(flags
)
634 def processPDF(self
, file, writer
, context
, registry
, bbox
):
635 # the bounding box is transformed by the canvas
638 # the annotation rectangle must be transformed separately:
639 llx_pt
, lly_pt
= context
.trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)
640 urx_pt
, ury_pt
= context
.trafo
.apply_pt(self
.urx_pt
, self
.ury_pt
)
641 fontsize_pt
= _sizetrafo(self
.fontsize_pt
, context
.trafo
)
642 borderwidth_pt
= _sizetrafo(self
.borderwidth_pt
, context
.trafo
)
644 # we create numbers from the flags given
645 annotflag
, formflag
= _pdfflags(self
.flags
)
646 alignflag
= _pdfalignment(self
.align
)
648 registry
.add(PDFchoicefield((llx_pt
, lly_pt
, urx_pt
, ury_pt
),
649 self
.name
, self
.values
, self
.defaultvalue
, fontsize_pt
, self
.font
,
650 borderwidth_pt
, alignflag
, annotflag
, formflag
, writer
, registry
))
652 class PDFchoicefield(pdfwriter
.PDFobject
): # <<<
654 def __init__(self
, bb_pt
, name
, values
, defaultvalue
, fontsize
, font
,
655 borderwidth_pt
, alignflag
, annotflag
, formflag
, writer
, registry
):
657 pdfwriter
.PDFobject
.__init
__(self
, "formfield_choice")
659 # append this formfield to the global document form
660 # and to the annotation list of the page:
662 for object in registry
.objects
:
663 if object.type == "form":
665 self
.PDFform
= object
666 elif object.type == "annotations":
672 self
.defaultvalue
= defaultvalue
673 self
.fontsize
= fontsize
675 if self
.font
is None:
676 self
.font
= PDFHelvetica
677 registry
.addresource("Font", self
.font
.name
, self
.font
, procset
="Text")
678 registry
.add(self
.font
)
679 self
.borderwidth_pt
= borderwidth_pt
680 self
.alignflag
= alignflag
681 self
.formflag
= formflag
682 self
.annotflag
= annotflag
684 def write(self
, file, writer
, registry
):
685 ### the dictionary entries for the annotation
686 file.write("<</Type /Annot\n")
687 file.write("/P %d 0 R\n" % registry
.getrefno(self
.PDFform
)) # reference to the page objects
688 file.write("/Rect [%f %f %f %f]\n" % self
.bb_pt
) # the annotation rectangle
689 #ile.write("/BS << ... >>\n" # border style dictionary
690 file.write("/Border [0 0 %f]\n" % self
.borderwidth_pt
) # border style
691 file.write("/F %d\n" % self
.annotflag
)
692 ### the dictionary entries for the widget annotations
693 file.write("/Subtype /Widget\n")
694 file.write("/H /N\n") # highlight behaviour
695 #ile.write("/AP <</N >>\n") # appearance dictionary TODO
696 ### the dictionary entries for the form field
697 file.write("/FT /Ch\n") # type of the form field
698 file.write("/T (%s)\n" % self
.name
) # partial field name
699 file.write("/TU (%s)\n" % self
.name
) # field name for the user-interface
700 file.write("/TM (%s)\n" % self
.name
) # field name for exporting the data
701 if self
.defaultvalue
in self
.values
:
702 file.write("/V (%s)\n" % self
.defaultvalue
) # starting value
703 file.write("/Ff %d\n" % self
.formflag
) # flags for various purposes
704 ### the dictionary entries for the text field
705 file.write("/DR <</Font <</%s %d 0 R >> >>\n" % (self
.font
.name
, registry
.getrefno(self
.font
))) # default resources for appearance
706 file.write("/DA (/%s %f Tf)\n" % (self
.font
.name
, self
.fontsize
)) # default appearance string
707 file.write("/Q %d\n" % self
.alignflag
)
709 for value
in self
.values
:
710 file.write(" (%s)" % value
)
716 # vim:foldmethod=marker:foldmarker=<<<,>>>