diff options
author | Hans Hagen <pragma@wxs.nl> | 2013-05-20 02:00:00 +0200 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 2013-05-20 02:00:00 +0200 |
commit | bd95a21d2b31a5fab1b4cc7c2b0334823fb3a3e9 (patch) | |
tree | 831128c411476f077eb7910d8c08f524d3ee43ec /tex/context/base/lpdf-fld.lua | |
parent | 9a10021cd4cb23995ad3ffa915fc5b7f6890aaf8 (diff) | |
download | context-bd95a21d2b31a5fab1b4cc7c2b0334823fb3a3e9.tar.gz |
beta 2013.05.20 02:00
Diffstat (limited to 'tex/context/base/lpdf-fld.lua')
-rw-r--r-- | tex/context/base/lpdf-fld.lua | 2610 |
1 files changed, 1305 insertions, 1305 deletions
diff --git a/tex/context/base/lpdf-fld.lua b/tex/context/base/lpdf-fld.lua index 0a15bb850..a9b9fd72d 100644 --- a/tex/context/base/lpdf-fld.lua +++ b/tex/context/base/lpdf-fld.lua @@ -1,1305 +1,1305 @@ -if not modules then modules = { } end modules ['lpdf-fld'] = {
- version = 1.001,
- comment = "companion to lpdf-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- The problem with widgets is that so far each version of acrobat
--- has some rendering problem. I tried to keep up with this but
--- it makes no sense to do so as one cannot rely on the viewer
--- not changing. Especially Btn fields are tricky as their appearences
--- need to be synchronized in the case of children but e.g. acrobat
--- 10 does not retain the state and forces a check symbol. If you
--- make a file in acrobat then it has MK entries that seem to overload
--- the already present appearance streams (they're probably only meant for
--- printing) as it looks like the viewer has some fallback on (auto
--- generated) MK behaviour built in. So ... hard to test. Unfortunately
--- not even the default appearance is generated. This will probably be
--- solved at some point.
---
--- Also, for some reason the viewer does not always show custom appearances
--- when fields are being rolled over or clicked upon, and circles or checks
--- pop up when you don't expect them. I fear that this kind of instability
--- eventually will kill pdf forms. After all, the manual says: "individual
--- annotation handlers may ignore this entry and provide their own appearances"
--- and one might wonder what 'individual' means here, but effectively this
--- renders the whole concept of appearances useless.
---
--- Okay, here is one observation. A pdf file contains objects and one might
--- consider each one to be a static entity when read in. However, acrobat
--- starts rendering and seems to manipulate (appearance streams) of objects
--- in place (this is visible when the file is saved again). And, combined
--- with some other caching and hashing, this might give side effects for
--- shared objects. So, it seems that for some cases one can best be not too
--- clever and not share but duplicate information. Of course this defeats the
--- whole purpose of these objects. Of course I can be wrong.
---
--- A rarther weird side effect of the viewer is that the highlighting of fields
--- obscures values, unless you uses one of the BS variants, and this makes
--- custum appearances rather useless as there is no way to control this apart
--- from changing the viewer preferences. It could of course be a bug but it would
--- be nice if the highlighting was at least transparent. I have no clue why the
--- built in shapes work ok (some xform based appearances are generated) while
--- equally valid other xforms fail. It looks like acrobat appearances come on
--- top (being refered to in the MK) while custom ones are behind the highlight
--- rectangle. One can disable the "Show border hover color for fields" option
--- in the preferences. If you load java-imp-rhh this side effect gets disabled
--- and you get what you expect (it took me a while to figure out this hack).
---
--- When highlighting is enabled, those default symbols flash up, so it looks
--- like we have some inteference between this setting and custom appearances.
---
--- Anyhow, the NeedAppearances is really needed in order to get a rendering
--- for printing especially when highlighting (those colorfull foregrounds) is
--- on.
-
-local gmatch, lower, format = string.gmatch, string.lower, string.format
-local lpegmatch = lpeg.match
-local utfchar = utf.char
-local bpfactor, todimen = number.dimenfactors.bp, string.todimen
-
-local trace_fields = false trackers.register("backends.fields", function(v) trace_fields = v end)
-
-local report_fields = logs.reporter("backend","fields")
-
-local backends, lpdf = backends, lpdf
-
-local variables = interfaces.variables
-local context = context
-
-local references = structures.references
-local settings_to_array = utilities.parsers.settings_to_array
-
-local pdfbackend = backends.pdf
-
-local nodeinjections = pdfbackend.nodeinjections
-local codeinjections = pdfbackend.codeinjections
-local registrations = pdfbackend.registrations
-
-local registeredsymbol = codeinjections.registeredsymbol
-
-local pdfstream = lpdf.stream
-local pdfdictionary = lpdf.dictionary
-local pdfarray = lpdf.array
-local pdfreference = lpdf.reference
-local pdfunicode = lpdf.unicode
-local pdfstring = lpdf.string
-local pdfconstant = lpdf.constant
-local pdftoeight = lpdf.toeight
-local pdfflushobject = lpdf.flushobject
-local pdfshareobjectreference = lpdf.shareobjectreference
-local pdfshareobject = lpdf.shareobject
-local pdfreserveobject = lpdf.reserveobject
-local pdfreserveannotation = lpdf.reserveannotation
-local pdfaction = lpdf.action
-
-local hpack_node = node.hpack
-
-local nodepool = nodes.pool
-
-local pdfannotation_node = nodepool.pdfannotation
-
-local submitoutputformat = 0 -- 0=unknown 1=HTML 2=FDF 3=XML => not yet used, needs to be checked
-
-local pdf_widget = pdfconstant("Widget")
-local pdf_tx = pdfconstant("Tx")
-local pdf_ch = pdfconstant("Ch")
-local pdf_btn = pdfconstant("Btn")
------ pdf_yes = pdfconstant("Yes")
-local pdf_off = pdfconstant("Off")
-local pdf_p = pdfconstant("P") -- None Invert Outline Push
-local pdf_n = pdfconstant("N") -- None Invert Outline Push
---
-local pdf_no_rect = pdfarray { 0, 0, 0, 0 }
-
-local splitter = lpeg.splitat("=>")
-
-local formats = {
- html = 1, fdf = 2, xml = 3,
-}
-
-function codeinjections.setformsmethod(name)
- submitoutputformat = formats[lower(name)] or formats.xml
-end
-
-local flag = { -- /Ff
- ReadOnly = 1, -- 1
- Required = 2, -- 2
- NoExport = 4, -- 3
- MultiLine = 4096, -- 13
- Password = 8192, -- 14
- NoToggleToOff = 16384, -- 15
- Radio = 32768, -- 16
- PushButton = 65536, -- 17
- PopUp = 131072, -- 18
- Edit = 262144, -- 19
- Sort = 524288, -- 20
- FileSelect = 1048576, -- 21
- DoNotSpellCheck = 4194304, -- 23
- DoNotScroll = 8388608, -- 24
- Comb = 16777216, -- 25
- RichText = 33554432, -- 26
- RadiosInUnison = 33554432, -- 26
- CommitOnSelChange = 67108864, -- 27
-}
-
-local plus = { -- /F
- Invisible = 1, -- 1
- Hidden = 2, -- 2
- Printable = 4, -- 3
- Print = 4, -- 3
- NoZoom = 8, -- 4
- NoRotate = 16, -- 5
- NoView = 32, -- 6
- ReadOnly = 64, -- 7
- Locked = 128, -- 8
- ToggleNoView = 256, -- 9
- LockedContents = 512, -- 10,
- AutoView = 256, -- 288 (6+9)
-}
-
--- todo: check what is interfaced
-
-flag.readonly = flag.ReadOnly
-flag.required = flag.Required
-flag.protected = flag.Password
-flag.sorted = flag.Sort
-flag.unavailable = flag.NoExport
-flag.nocheck = flag.DoNotSpellCheck
-flag.fixed = flag.DoNotScroll
-flag.file = flag.FileSelect
-
-plus.hidden = plus.Hidden
-plus.printable = plus.Printable
-plus.auto = plus.AutoView
-
--- some day .. lpeg with function or table
-
-local function fieldflag(specification) -- /Ff
- local o, n = specification.option, 0
- if o and o ~= "" then
- for f in gmatch(o,"[^, ]+") do
- n = n + (flag[f] or 0)
- end
- end
- return n
-end
-
-local function fieldplus(specification) -- /F
- local o, n = specification.option, 0
- if o and o ~= "" then
- for p in gmatch(o,"[^, ]+") do
- n = n + (plus[p] or 0)
- end
- end
--- n = n + 4
- return n
-end
-
-local function checked(what)
- local set, bug = references.identify("",what)
- if not bug and #set > 0 then
- local r, n = pdfaction(set)
- return pdfshareobjectreference(r)
- end
-end
-
-local function fieldactions(specification) -- share actions
- local d, a = { }, nil
- a = specification.mousedown
- or specification.clickin if a and a ~= "" then d.D = checked(a) end
- a = specification.mouseup
- or specification.clickout if a and a ~= "" then d.U = checked(a) end
- a = specification.regionin if a and a ~= "" then d.E = checked(a) end -- Enter
- a = specification.regionout if a and a ~= "" then d.X = checked(a) end -- eXit
- a = specification.afterkey if a and a ~= "" then d.K = checked(a) end
- a = specification.format if a and a ~= "" then d.F = checked(a) end
- a = specification.validate if a and a ~= "" then d.V = checked(a) end
- a = specification.calculate if a and a ~= "" then d.C = checked(a) end
- a = specification.focusin if a and a ~= "" then d.Fo = checked(a) end
- a = specification.focusout if a and a ~= "" then d.Bl = checked(a) end
- a = specification.openpage if a and a ~= "" then d.PO = checked(a) end
- a = specification.closepage if a and a ~= "" then d.PC = checked(a) end
- -- a = specification.visiblepage if a and a ~= "" then d.PV = checked(a) end
- -- a = specification.invisiblepage if a and a ~= "" then d.PI = checked(a) end
- return next(d) and pdfdictionary(d)
-end
-
--- fonts and color
-
-local pdfdocencodingvector, pdfdocencodingcapsule
-
--- The pdf doc encoding vector is needed in order to
--- trigger propper unicode. Interesting is that when
--- a glyph is not in the vector, it is still visible
--- as it is taken from some other font. Messy.
-
--- To be checked: only when text/line fields.
-
-local function checkpdfdocencoding()
- report_fields("adding pdfdoc encoding vector")
- local encoding = dofile(resolvers.findfile("lpdf-enc.lua")) -- no checking, fatal if not present
- pdfdocencodingvector = pdfreference(pdfflushobject(encoding))
- local capsule = pdfdictionary {
- PDFDocEncoding = pdfdocencodingvector
- }
- pdfdocencodingcapsule = pdfreference(pdfflushobject(capsule))
- checkpdfdocencoding = function() end
-end
-
-local fontnames = {
- rm = {
- tf = "Times-Roman",
- bf = "Times-Bold",
- it = "Times-Italic",
- sl = "Times-Italic",
- bi = "Times-BoldItalic",
- bs = "Times-BoldItalic",
- },
- ss = {
- tf = "Helvetica",
- bf = "Helvetica-Bold",
- it = "Helvetica-Oblique",
- sl = "Helvetica-Oblique",
- bi = "Helvetica-BoldOblique",
- bs = "Helvetica-BoldOblique",
- },
- tt = {
- tf = "Courier",
- bf = "Courier-Bold",
- it = "Courier-Oblique",
- sl = "Courier-Oblique",
- bi = "Courier-BoldOblique",
- bs = "Courier-BoldOblique",
- },
- symbol = {
- dingbats = "ZapfDingbats",
- }
-}
-
-local usedfonts = { }
-
-local function fieldsurrounding(specification)
- local fontsize = specification.fontsize or "12pt"
- local fontstyle = specification.fontstyle or "rm"
- local fontalternative = specification.fontalternative or "tf"
- local colorvalue = specification.colorvalue
- local s = fontnames[fontstyle]
- if not s then
- fontstyle, s = "rm", fontnames.rm
- end
- local a = s[fontalternative]
- if not a then
- alternative, a = "tf", s.tf
- end
- local tag = fontstyle .. fontalternative
- fontsize = todimen(fontsize)
- fontsize = fontsize and (bpfactor * fontsize) or 12
- fontraise = 0.1 * fontsize -- todo: figure out what the natural one is and compensate for strutdp
- local fontcode = format("%0.4f Tf %0.4f Ts",fontsize,fontraise)
- -- we could test for colorvalue being 1 (black) and omit it then
- local colorcode = lpdf.color(3,colorvalue) -- we force an rgb color space
- if trace_fields then
- report_fields("using font, style %a, alternative %a, size %p, tag %a, code %a",fontstyle,fontalternative,fontsize,tag,fontcode)
- report_fields("using color, value %a, code %a",colorvalue,colorcode)
- end
- local stream = pdfstream {
- pdfconstant(tag),
- format("%s %s",fontcode,colorcode)
- }
- usedfonts[tag] = a -- the name
- -- move up with "x.y Ts"
- return tostring(stream)
-end
-
-local function registerfonts()
- if next(usedfonts) then
- checkpdfdocencoding() -- already done
- local d = pdfdictionary()
- local pdffonttype, pdffontsubtype = pdfconstant("Font"), pdfconstant("Type1")
- for tag, name in next, usedfonts do
- local f = pdfdictionary {
- Type = pdffonttype,
- Subtype = pdffontsubtype,
- Name = pdfconstant(tag),
- BaseFont = pdfconstant(name),
- Encoding = pdfdocencodingvector,
- }
- d[tag] = pdfreference(pdfflushobject(f))
- end
- return d
- end
-end
-
--- symbols
-
-local function fieldappearances(specification)
- -- todo: caching
- local values = specification.values
- local default = specification.default -- todo
- if not values then
- -- error
- return
- end
- local v = settings_to_array(values)
- local n, r, d
- if #v == 1 then
- n, r, d = v[1], v[1], v[1]
- elseif #v == 2 then
- n, r, d = v[1], v[1], v[2]
- else
- n, r, d = v[1], v[2], v[3]
- end
- local appearance = pdfdictionary {
- N = registeredsymbol(n), R = registeredsymbol(r), D = registeredsymbol(d),
- }
- return pdfshareobjectreference(appearance)
--- return pdfreference(pdfflushobject(appearance))
-end
-
-local YesorOn = "Yes" -- somehow On is not always working out well any longer (why o why this change)
-
--- beware ... maybe we should have unique /Yes1 ... we will probably
--- change this one too.
---
--- TODO: the same as radio .. play safe and use different names.
-
-local function fieldstates_check(specification,forceyes,values,default,yesdefault)
- -- we don't use Opt here (too messy for radio buttons)
- local values, default = values or specification.values, default or specification.default
- if not values or values == "" then
- -- error
- return
- end
- local v = settings_to_array(values)
- local yes, off, yesn, yesr, yesd, offn, offr, offd
- if #v == 1 then
- yes, off = v[1], v[1]
- else
- yes, off = v[1], v[2]
- end
- local yesshown, yesvalue = lpegmatch(splitter,yes)
- if not (yesshown and yesvalue) then
- yesshown = yes, yes
- end
- yes = settings_to_array(yesshown)
- local offshown, offvalue = lpegmatch(splitter,off)
- if not (offshown and offvalue) then
- offshown = off, off
- end
- off = settings_to_array(offshown)
- if #yes == 1 then
- yesn, yesr, yesd = yes[1], yes[1], yes[1]
- elseif #yes == 2 then
- yesn, yesr, yesd = yes[1], yes[1], yes[2]
- else
- yesn, yesr, yesd = yes[1], yes[2], yes[3]
- end
- if #off == 1 then
- offn, offr, offd = off[1], off[1], off[1]
- elseif #off == 2 then
- offn, offr, offd = off[1], off[1], off[2]
- else
- offn, offr, offd = off[1], off[2], off[3]
- end
- if not yesvalue then
- yesvalue = yesdefault or yesn
- end
- if not offvalue then
- offvalue = offn
- end
- if forceyes == true then
- forceyes = YesorOn -- spec likes Yes more but we've used On for ages now
- else
- -- false or string
- end
- if default == yesn then
- default = pdfconstant(forceyes or yesn)
- else
- default = pdf_off
- end
- local appearance
- if false then -- needs testing
- appearance = pdfdictionary { -- maybe also cache components
- N = pdfshareobjectreference(pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) }),
- R = pdfshareobjectreference(pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) }),
- D = pdfshareobjectreference(pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) }),
- }
- else
- appearance = pdfdictionary { -- maybe also cache components
- N = pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) },
- R = pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) },
- D = pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) }
- }
- end
- local appearanceref = pdfshareobjectreference(appearance)
- -- local appearanceref = pdfreference(pdfflushobject(appearance))
- return appearanceref, default, yesvalue
-end
-
--- It looks like there is always a (MK related) symbol used and that
--- the appearances are only used as ornaments behind a symbol. So,
--- contrary to what we did when widgets showed up, we now limit
--- ourself to more dumb definitions. Especially when highlighting is
--- enabled weird interferences happen. So, we play safe (some nice code
--- has been removed that worked well till recently).
-
-local function fieldstates_radio(specification,name,parent)
- local values = values or specification.values
- local default = default or parent.default -- specification.default
- if not values or values == "" then
- -- error
- return
- end
- local v = settings_to_array(values)
- local yes, off, yesn, yesr, yesd, offn, offr, offd
- if #v == 1 then
- yes, off = v[1], v[1]
- else
- yes, off = v[1], v[2]
- end
- -- yes keys might be the same in the three appearances within a field
- -- but can best be different among fields ... don't ask why
- local yessymbols, yesvalue = lpegmatch(splitter,yes) -- n,r,d=>x
- if not (yessymbols and yesvalue) then
- yessymbols = yes
- end
- if not yesvalue then
- yesvalue = name
- end
- yessymbols = settings_to_array(yessymbols)
- if #yessymbols == 1 then
- yesn = yessymbols[1]
- yesr = yesn
- yesd = yesr
- elseif #yessymbols == 2 then
- yesn = yessymbols[1]
- yesr = yessymbols[2]
- yesd = yesr
- else
- yesn = yessymbols[1]
- yesr = yessymbols[2]
- yesd = yessymbols[3]
- end
- -- we don't care about names, as all will be /Off
- local offsymbols = lpegmatch(splitter,off) or off
- offsymbols = settings_to_array(offsymbols)
- if #offsymbols == 1 then
- offn = offsymbols[1]
- offr = offn
- offd = offr
- elseif #offsymbols == 2 then
- offn = offsymbols[1]
- offr = offsymbols[2]
- offd = offr
- else
- offn = offsymbols[1]
- offr = offsymbols[2]
- offd = offsymbols[3]
- end
- if default == name then
- default = pdfconstant(name)
- else
- default = pdf_off
- end
- --
- local appearance
- if false then -- needs testing
- appearance = pdfdictionary { -- maybe also cache components
- N = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesn), Off = registeredsymbol(offn) }),
- R = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesr), Off = registeredsymbol(offr) }),
- D = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesd), Off = registeredsymbol(offd) }),
- }
- else
- appearance = pdfdictionary { -- maybe also cache components
- N = pdfdictionary { [name] = registeredsymbol(yesn), Off = registeredsymbol(offn) },
- R = pdfdictionary { [name] = registeredsymbol(yesr), Off = registeredsymbol(offr) },
- D = pdfdictionary { [name] = registeredsymbol(yesd), Off = registeredsymbol(offd) }
- }
- end
- local appearanceref = pdfshareobjectreference(appearance) -- pdfreference(pdfflushobject(appearance))
- return appearanceref, default, yesvalue
-end
-
-local function fielddefault(field)
- local default = field.default
- if not default or default == "" then
- local values = settings_to_array(field.values)
- default = values[1]
- end
- if not default or default == "" then
- return pdf_off
- else
- return pdfconstant(default)
- end
-end
-
-local function fieldoptions(specification)
- local values = specification.values
- local default = specification.default
- if values then
- local v = settings_to_array(values)
- for i=1,#v do
- local vi = v[i]
- local shown, value = lpegmatch(splitter,vi)
- if shown and value then
- v[i] = pdfarray { pdfunicode(value), shown }
- else
- v[i] = pdfunicode(v[i])
- end
- end
- return pdfarray(v)
- end
-end
-
-local mapping = {
- -- acrobat compliant (messy, probably some pdfdoc encoding interference here)
- check = "4", -- 0x34
- circle = "l", -- 0x6C
- cross = "8", -- 0x38
- diamond = "u", -- 0x75
- square = "n", -- 0x6E
- star = "H", -- 0x48
-}
-
-local function todingbat(n)
- if n and n ~= "" then
- return mapping[n] or ""
- end
-end
-
--- local zero_bc = pdfarray { 0, 0, 0 }
--- local zero_bg = pdfarray { 1, 1, 1 }
-
-local function fieldrendering(specification)
- local bvalue = tonumber(specification.backgroundcolorvalue)
- local fvalue = tonumber(specification.framecolorvalue)
- local svalue = specification.fontsymbol
- if bvalue or fvalue or (svalue and svalue ~= "") then
- return pdfdictionary {
- BG = bvalue and pdfarray { lpdf.colorvalues(3,bvalue) } or nil, -- or zero_bg,
- BC = fvalue and pdfarray { lpdf.colorvalues(3,fvalue) } or nil, -- or zero_bc,
- CA = svalue and pdfstring (svalue) or nil,
- }
- end
-end
-
--- layers
-
-local function fieldlayer(specification) -- we can move this in line
- local layer = specification.layer
- return (layer and lpdf.layerreference(layer)) or nil
-end
-
--- defining
-
-local fields, radios, clones, fieldsets, calculationset = { }, { }, { }, { }, nil
-
-local xfdftemplate = [[
-<?xml version='1.0' encoding='UTF-8'?>
-
-<xfdf xmlns='http://ns.adobe.com/xfdf/'>
- <f href='%s.pdf'/>
- <fields>
-%s
- </fields>
-</xfdf>
-]]
-
-function codeinjections.exportformdata(name)
- local result = { }
- for k, v in table.sortedhash(fields) do
- result[#result+1] = format(" <field name='%s'><value>%s</value></field>",v.name or k,v.default or "")
- end
- local base = file.basename(tex.jobname)
- local xfdf = format(xfdftemplate,base,table.concat(result,"\n"))
- if not name or name == "" then
- name = base
- end
- io.savedata(file.addsuffix(name,"xfdf"),xfdf)
-end
-
-function codeinjections.definefieldset(tag,list)
- fieldsets[tag] = list
-end
-
-function codeinjections.getfieldset(tag)
- return fieldsets[tag]
-end
-
-local function fieldsetlist(tag)
- if tag then
- local ft = fieldsets[tag]
- if ft then
- local a = pdfarray()
- for name in gmatch(list,"[^, ]+") do
- local f = field[name]
- if f and f.pobj then
- a[#a+1] = pdfreference(f.pobj)
- end
- end
- return a
- end
- end
-end
-
-function codeinjections.setfieldcalculationset(tag)
- calculationset = tag
-end
-
-local function predefinesymbols(specification)
- local values = specification.values
- if values then
- local symbols = settings_to_array(values)
- for i=1,#symbols do
- local symbol = symbols[i]
- local a, b = lpegmatch(splitter,symbol)
- codeinjections.presetsymbol(a or symbol)
- end
- end
-end
-
-function codeinjections.getdefaultfieldvalue(name)
- local f = fields[name]
- if f then
- local values = f.values
- local default = f.default
- if not default or default == "" then
- local symbols = settings_to_array(values)
- local symbol = symbols[1]
- if symbol then
- local a, b = lpegmatch(splitter,symbol) -- splits at =>
- default = a or symbol
- end
- end
- return default
- end
-end
-
-function codeinjections.definefield(specification)
- local n = specification.name
- local f = fields[n]
- if not f then
- local fieldtype = specification.type
- if not fieldtype then
- if trace_fields then
- report_fields("invalid definition for %a, unknown type",n)
- end
- elseif fieldtype == "radio" then
- local values = specification.values
- if values and values ~= "" then
- values = settings_to_array(values)
- for v=1,#values do
- radios[values[v]] = { parent = n }
- end
- fields[n] = specification
- if trace_fields then
- report_fields("defining %a as type %a",n,"radio")
- end
- elseif trace_fields then
- report_fields("invalid definition of radio %a, missing values",n)
- end
- elseif fieldtype == "sub" then
- -- not in main field list !
- local radio = radios[n]
- if radio then
- -- merge specification
- for key, value in next, specification do
- radio[key] = value
- end
- if trace_fields then
- local p = radios[n] and radios[n].parent
- report_fields("defining %a as type sub of radio %a",n,p)
- end
- elseif trace_fields then
- report_fields("invalid definition of radio sub %a, no parent given",n)
- end
- predefinesymbols(specification)
- elseif fieldtype == "text" or fieldtype == "line" then
- fields[n] = specification
- if trace_fields then
- report_fields("defining %a as type %a",n,fieldtype)
- end
- if specification.values ~= "" and specification.default == "" then
- specification.default, specification.values = specification.values, nil
- end
- else
- fields[n] = specification
- if trace_fields then
- report_fields("defining %a as type %a",n,fieldtype)
- end
- predefinesymbols(specification)
- end
- elseif trace_fields then
- report_fields("invalid definition for %a, already defined",n)
- end
-end
-
-function codeinjections.clonefield(specification) -- obsolete
- local p, c, v = specification.parent, specification.children, specification.alternative
- if not p or not c then
- if trace_fields then
- report_fields("invalid clone, children %a, parent %a, alternative %a",c,p,v)
- end
- return
- end
- local x = fields[p] or radios[p]
- if not x then
- if trace_fields then
- report_fields("invalid clone, unknown parent %a",p)
- end
- return
- end
- for n in gmatch(c,"[^, ]+") do
- local f, r, c = fields[n], radios[n], clones[n]
- if f or r or c then
- if trace_fields then
- report_fields("already cloned, child %a, parent %a, alternative %a",n,p,v)
- end
- else
- if trace_fields then
- report_fields("cloning, child %a, parent %a, alternative %a",n,p,v)
- end
- clones[n] = specification
- predefinesymbols(specification)
- end
- end
-end
-
-function codeinjections.getfieldcategory(name)
- local f = fields[name] or radios[name] or clones[name]
- if f then
- local g = f.category
- if not g or g == "" then
- local v, p, t = f.alternative, f.parent, f.type
- if v == "clone" or v == "copy" then
- f = fields[p] or radios[p]
- g = f and f.category
- elseif t == "sub" then
- f = fields[p]
- g = f and f.category
- end
- end
- return g
- end
-end
-
---
-
-function codeinjections.validfieldcategory(name)
- return fields[name] or radios[name] or clones[name]
-end
-
-function codeinjections.validfieldset(name)
- return fieldsets[tag]
-end
-
-function codeinjections.validfield(name)
- return fields[name]
-end
-
---
-
-local alignments = {
- flushleft = 0, right = 0,
- center = 1, middle = 1,
- flushright = 2, left = 2,
-}
-
-local function fieldalignment(specification)
- return alignments[specification.align] or 0
-end
-
-local function enhance(specification,option)
- local so = specification.option
- if so and so ~= "" then
- specification.option = so .. "," .. option
- else
- specification.option = option
- end
- return specification
-end
-
--- finish
-
-local collected = pdfarray()
-local forceencoding = false
-
-local function finishfields()
- local sometext = forceencoding
- for name, field in next, fields do
- local kids = field.kids
- if kids then
- pdfflushobject(field.kidsnum,kids)
- end
- local opt = field.opt
- if opt then
- pdfflushobject(field.optnum,opt)
- end
- local type = field.type
- if not sometext and (type == "text" or type == "line") then
- sometext = true
- end
- end
- for name, field in next, radios do
- local kids = field.kids
- if kids then
- pdfflushobject(field.kidsnum,kids)
- end
- local opt = field.opt
- if opt then
- pdfflushobject(field.optnum,opt)
- end
- end
- if #collected > 0 then
- local acroform = pdfdictionary {
- NeedAppearances = true,
- Fields = pdfreference(pdfflushobject(collected)),
- CO = fieldsetlist(calculationset),
- }
- if sometext then
- checkpdfdocencoding()
- usedfonts.tttf = fontnames.tt.tf
- acroform.DA = "/tttf 12 Tf 0 g"
- acroform.DR = pdfdictionary {
- Font = registerfonts(),
- Encoding = pdfdocencodingcapsule,
- }
- end
- lpdf.addtocatalog("AcroForm",pdfreference(pdfflushobject(acroform)))
- end
-end
-
-lpdf.registerdocumentfinalizer(finishfields,"form fields")
-
-local methods = { }
-
-function nodeinjections.typesetfield(name,specification)
- local field = fields[name] or radios[name] or clones[name]
- if not field then
- report_fields( "unknown child %a",name)
- -- unknown field
- return
- end
- local alternative, parent = field.alternative, field.parent
- if alternative == "copy" or alternative == "clone" then -- only in clones
- field = fields[parent] or radios[parent]
- end
- local method = methods[field.type]
- if method then
- return method(name,specification,alternative)
- else
- report_fields( "unknown method %a for child %a",field.type,name)
- end
-end
-
-local function save_parent(field,specification,d,hasopt)
- local kidsnum = pdfreserveobject()
- d.Kids = pdfreference(kidsnum)
- field.kidsnum = kidsnum
- field.kids = pdfarray()
- if hasopt then
- local optnum = pdfreserveobject()
- d.Opt = pdfreference(optnum)
- field.optnum = optnum
- field.opt = pdfarray()
- end
- local pnum = pdfflushobject(d)
- field.pobj = pnum
- collected[#collected+1] = pdfreference(pnum)
-end
-
-local function save_kid(field,specification,d,optname)
- local kn = pdfreserveannotation()
- field.kids[#field.kids+1] = pdfreference(kn)
- if optname then
- local opt = field.opt
- if opt then
- opt[#opt+1] = optname
- end
- end
- local width, height, depth = specification.width or 0, specification.height or 0, specification.depth
- local box = hpack_node(pdfannotation_node(width,height,depth,d(),kn))
- box.width, box.height, box.depth = width, height, depth -- redundant
- return box
-end
-
-local function makelineparent(field,specification)
- local text = pdfunicode(field.default)
- local length = tonumber(specification.length or 0) or 0
- local d = pdfdictionary {
- Subtype = pdf_widget,
- T = pdfunicode(specification.title),
- F = fieldplus(specification),
- Ff = fieldflag(specification),
- OC = fieldlayer(specification),
- DA = fieldsurrounding(specification),
- AA = fieldactions(specification),
- FT = pdf_tx,
- Q = fieldalignment(specification),
- MaxLen = length == 0 and 1000 or length,
- DV = text,
- V = text,
- }
- save_parent(field,specification,d)
-end
-
-local function makelinechild(name,specification)
- local field, parent = clones[name], nil
- if field then
- parent = fields[field.parent]
- if not parent.pobj then
- if trace_fields then
- report_fields("forcing parent text %a",parent.name)
- end
- makelineparent(parent,specification)
- end
- else
- parent = fields[name]
- field = parent
- if not parent.pobj then
- if trace_fields then
- report_fields("using parent text %a",name)
- end
- makelineparent(parent,specification)
- end
- end
- if trace_fields then
- report_fields("using child text %a",name)
- end
- local d = pdfdictionary {
- Subtype = pdf_widget,
- Parent = pdfreference(parent.pobj),
- F = fieldplus(specification),
- OC = fieldlayer(specification),
- DA = fieldsurrounding(specification),
- AA = fieldactions(specification),
- MK = fieldrendering(specification),
- Q = fieldalignment(specification),
- }
- return save_kid(parent,specification,d)
-end
-
-function methods.line(name,specification)
- return makelinechild(name,specification)
-end
-
-function methods.text(name,specification)
- return makelinechild(name,enhance(specification,"MultiLine"))
-end
-
-local function makechoiceparent(field,specification)
- local d = pdfdictionary {
- Subtype = pdf_widget,
- T = pdfunicode(specification.title),
- F = fieldplus(specification),
- Ff = fieldflag(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- FT = pdf_ch,
- Opt = fieldoptions(field), -- todo
- }
- save_parent(field,specification,d)
-end
-
-local function makechoicechild(name,specification)
- local field, parent = clones[name], nil
- if field then
- parent = fields[field.parent]
- if not parent.pobj then
- if trace_fields then
- report_fields("forcing parent choice %a",parent.name)
- end
- makechoiceparent(parent,specification,extras)
- end
- else
- parent = fields[name]
- field = parent
- if not parent.pobj then
- if trace_fields then
- report_fields("using parent choice %a",name)
- end
- makechoiceparent(parent,specification,extras)
- end
- end
- if trace_fields then
- report_fields("using child choice %a",name)
- end
- local d = pdfdictionary {
- Subtype = pdf_widget,
- Parent = pdfreference(parent.pobj),
- F = fieldplus(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- }
- return save_kid(parent,specification,d) -- do opt here
-end
-
-function methods.choice(name,specification)
- return makechoicechild(name,specification)
-end
-
-function methods.popup(name,specification)
- return makechoicechild(name,enhance(specification,"PopUp"))
-end
-
-function methods.combo(name,specification)
- return makechoicechild(name,enhance(specification,"PopUp,Edit"))
-end
-
-local function makecheckparent(field,specification)
- local d = pdfdictionary {
- T = pdfunicode(specification.title), -- todo: when tracing use a string
- F = fieldplus(specification),
- Ff = fieldflag(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- FT = pdf_btn,
- V = fielddefault(field),
- }
- save_parent(field,specification,d,true)
-end
-
-local function makecheckchild(name,specification)
- local field, parent = clones[name], nil
- if field then
- parent = fields[field.parent]
- if not parent.pobj then
- if trace_fields then
- report_fields("forcing parent check %a",parent.name)
- end
- makecheckparent(parent,specification,extras)
- end
- else
- parent = fields[name]
- field = parent
- if not parent.pobj then
- if trace_fields then
- report_fields("using parent check %a",name)
- end
- makecheckparent(parent,specification,extras)
- end
- end
- if trace_fields then
- report_fields("using child check %a",name)
- end
- local d = pdfdictionary {
- Subtype = pdf_widget,
- Parent = pdfreference(parent.pobj),
- F = fieldplus(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- H = pdf_n,
- }
- local fontsymbol = specification.fontsymbol
- if fontsymbol and fontsymbol ~= "" then
- specification.fontsymbol = todingbat(fontsymbol)
- specification.fontstyle = "symbol"
- specification.fontalternative = "dingbats"
- d.DA = fieldsurrounding(specification)
- d.MK = fieldrendering(specification)
- return save_kid(parent,specification,d)
- else
- local appearance, default, value = fieldstates_check(field,true)
- d.AS = default
- d.AP = appearance
- return save_kid(parent,specification,d,value)
- end
-end
-
-function methods.check(name,specification)
- return makecheckchild(name,specification)
-end
-
-local function makepushparent(field,specification) -- check if we can share with the previous
- local d = pdfdictionary {
- Subtype = pdf_widget,
- T = pdfunicode(specification.title),
- F = fieldplus(specification),
- Ff = fieldflag(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- FT = pdf_btn,
- AP = fieldappearances(field),
- H = pdf_p,
- }
- save_parent(field,specification,d)
-end
-
-local function makepushchild(name,specification)
- local field, parent = clones[name], nil
- if field then
- parent = fields[field.parent]
- if not parent.pobj then
- if trace_fields then
- report_fields("forcing parent push %a",parent.name)
- end
- makepushparent(parent,specification)
- end
- else
- parent = fields[name]
- field = parent
- if not parent.pobj then
- if trace_fields then
- report_fields("using parent push %a",name)
- end
- makepushparent(parent,specification)
- end
- end
- if trace_fields then
- report_fields("using child push %a",name)
- end
- local fontsymbol = specification.fontsymbol
- local d = pdfdictionary {
- Subtype = pdf_widget,
- Parent = pdfreference(field.pobj),
- F = fieldplus(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- H = pdf_p,
- }
- if fontsymbol and fontsymbol ~= "" then
- specification.fontsymbol = todingbat(fontsymbol)
- specification.fontstyle = "symbol"
- specification.fontalternative = "dingbats"
- d.DA = fieldsurrounding(specification)
- d.MK = fieldrendering(specification)
- else
- d.AP = fieldappearances(field)
- end
- return save_kid(parent,specification,d)
-end
-
-function methods.push(name,specification)
- return makepushchild(name,enhance(specification,"PushButton"))
-end
-
-local function makeradioparent(field,specification)
--- specification = enhance(specification,"Radio,RadiosInUnison")
- specification = enhance(specification,"Radio,RadiosInUnison,Print,NoToggleToOff")
--- specification = enhance(specification,"Radio,Print,NoToggleToOff")
- local d = pdfdictionary {
- T = field.name,
- FT = pdf_btn,
--- F = fieldplus(specification),
- Ff = fieldflag(specification),
--- H = pdf_n,
- V = fielddefault(field),
- }
- save_parent(field,specification,d,true)
-end
-
--- local function makeradiochild(name,specification)
--- local field, parent = clones[name], nil
--- if field then
--- field = radios[field.parent]
--- parent = fields[field.parent]
--- if not parent.pobj then
--- if trace_fields then
--- report_fields("forcing parent radio %a",parent.name)
--- end
--- makeradioparent(parent,parent)
--- end
--- else
--- field = radios[name]
--- if not field then
--- report_fields("there is some problem with field %a",name)
--- return nil
--- end
--- parent = fields[field.parent]
--- if not parent.pobj then
--- if trace_fields then
--- report_fields("using parent radio %a",name)
--- end
--- makeradioparent(parent,parent)
--- end
--- end
--- if trace_fields then
--- report_fields("using child radio %a with values %a and default %a",name,field.values,field.default)
--- end
--- local fontsymbol = specification.fontsymbol
--- fontsymbol="star"
--- local d = pdfdictionary {
--- Subtype = pdf_widget,
--- Parent = pdfreference(parent.pobj),
--- F = fieldplus(specification),
--- OC = fieldlayer(specification),
--- AA = fieldactions(specification),
--- H = pdf_n,
--- }
--- if fontsymbol and fontsymbol ~= "" then
--- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok
--- specification.fontsymbol = todingbat(fontsymbol)
--- specification.fontstyle = "symbol"
--- specification.fontalternative = "dingbats"
--- d.DA = fieldsurrounding(specification)
--- d.MK = fieldrendering(specification)
--- d.AS = pdfconstant(value) -- default -- mandate when AP but confuses viewers
--- d.AP = appearance
--- return save_kid(parent,specification,d,value)
--- -- return save_kid(parent,specification,d,name)
--- else
--- -- local appearance, default, value = fieldstates_radio(field,true) -- false is also ok
--- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok
--- d.AS = default -- mandate when AP but confuses viewers
--- d.AP = appearance
--- return save_kid(parent,specification,d,value)
--- end
--- end
-
-local function makeradiochild(name,specification)
- local field, parent = clones[name], nil
- if field then
- field = radios[field.parent]
- parent = fields[field.parent]
- if not parent.pobj then
- if trace_fields then
- report_fields("forcing parent radio %a",parent.name)
- end
- makeradioparent(parent,parent)
- end
- else
- field = radios[name]
- if not field then
- report_fields("there is some problem with field %a",name)
- return nil
- end
- parent = fields[field.parent]
- if not parent.pobj then
- if trace_fields then
- report_fields("using parent radio %a",name)
- end
- makeradioparent(parent,parent)
- end
- end
- if trace_fields then
- report_fields("using child radio %a with values %a and default %a",name,field.values,field.default)
- end
- local fontsymbol = specification.fontsymbol
- -- fontsymbol = "circle"
- local d = pdfdictionary {
- Subtype = pdf_widget,
- Parent = pdfreference(parent.pobj),
- F = fieldplus(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- H = pdf_n,
- }
- if fontsymbol and fontsymbol ~= "" then
- specification.fontsymbol = todingbat(fontsymbol)
- specification.fontstyle = "symbol"
- specification.fontalternative = "dingbats"
- d.DA = fieldsurrounding(specification)
- d.MK = fieldrendering(specification)
- end
- local appearance, default, value = fieldstates_radio(field,name,fields[field.parent])
- d.AP = appearance
- d.AS = default -- /Whatever
- return save_kid(parent,specification,d,value)
-end
-
-function methods.sub(name,specification)
- return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison"))
-end
+if not modules then modules = { } end modules ['lpdf-fld'] = { + version = 1.001, + comment = "companion to lpdf-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- The problem with widgets is that so far each version of acrobat +-- has some rendering problem. I tried to keep up with this but +-- it makes no sense to do so as one cannot rely on the viewer +-- not changing. Especially Btn fields are tricky as their appearences +-- need to be synchronized in the case of children but e.g. acrobat +-- 10 does not retain the state and forces a check symbol. If you +-- make a file in acrobat then it has MK entries that seem to overload +-- the already present appearance streams (they're probably only meant for +-- printing) as it looks like the viewer has some fallback on (auto +-- generated) MK behaviour built in. So ... hard to test. Unfortunately +-- not even the default appearance is generated. This will probably be +-- solved at some point. +-- +-- Also, for some reason the viewer does not always show custom appearances +-- when fields are being rolled over or clicked upon, and circles or checks +-- pop up when you don't expect them. I fear that this kind of instability +-- eventually will kill pdf forms. After all, the manual says: "individual +-- annotation handlers may ignore this entry and provide their own appearances" +-- and one might wonder what 'individual' means here, but effectively this +-- renders the whole concept of appearances useless. +-- +-- Okay, here is one observation. A pdf file contains objects and one might +-- consider each one to be a static entity when read in. However, acrobat +-- starts rendering and seems to manipulate (appearance streams) of objects +-- in place (this is visible when the file is saved again). And, combined +-- with some other caching and hashing, this might give side effects for +-- shared objects. So, it seems that for some cases one can best be not too +-- clever and not share but duplicate information. Of course this defeats the +-- whole purpose of these objects. Of course I can be wrong. +-- +-- A rarther weird side effect of the viewer is that the highlighting of fields +-- obscures values, unless you uses one of the BS variants, and this makes +-- custum appearances rather useless as there is no way to control this apart +-- from changing the viewer preferences. It could of course be a bug but it would +-- be nice if the highlighting was at least transparent. I have no clue why the +-- built in shapes work ok (some xform based appearances are generated) while +-- equally valid other xforms fail. It looks like acrobat appearances come on +-- top (being refered to in the MK) while custom ones are behind the highlight +-- rectangle. One can disable the "Show border hover color for fields" option +-- in the preferences. If you load java-imp-rhh this side effect gets disabled +-- and you get what you expect (it took me a while to figure out this hack). +-- +-- When highlighting is enabled, those default symbols flash up, so it looks +-- like we have some inteference between this setting and custom appearances. +-- +-- Anyhow, the NeedAppearances is really needed in order to get a rendering +-- for printing especially when highlighting (those colorfull foregrounds) is +-- on. + +local gmatch, lower, format = string.gmatch, string.lower, string.format +local lpegmatch = lpeg.match +local utfchar = utf.char +local bpfactor, todimen = number.dimenfactors.bp, string.todimen + +local trace_fields = false trackers.register("backends.fields", function(v) trace_fields = v end) + +local report_fields = logs.reporter("backend","fields") + +local backends, lpdf = backends, lpdf + +local variables = interfaces.variables +local context = context + +local references = structures.references +local settings_to_array = utilities.parsers.settings_to_array + +local pdfbackend = backends.pdf + +local nodeinjections = pdfbackend.nodeinjections +local codeinjections = pdfbackend.codeinjections +local registrations = pdfbackend.registrations + +local registeredsymbol = codeinjections.registeredsymbol + +local pdfstream = lpdf.stream +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfreference = lpdf.reference +local pdfunicode = lpdf.unicode +local pdfstring = lpdf.string +local pdfconstant = lpdf.constant +local pdftoeight = lpdf.toeight +local pdfflushobject = lpdf.flushobject +local pdfshareobjectreference = lpdf.shareobjectreference +local pdfshareobject = lpdf.shareobject +local pdfreserveobject = lpdf.reserveobject +local pdfreserveannotation = lpdf.reserveannotation +local pdfaction = lpdf.action + +local hpack_node = node.hpack + +local nodepool = nodes.pool + +local pdfannotation_node = nodepool.pdfannotation + +local submitoutputformat = 0 -- 0=unknown 1=HTML 2=FDF 3=XML => not yet used, needs to be checked + +local pdf_widget = pdfconstant("Widget") +local pdf_tx = pdfconstant("Tx") +local pdf_ch = pdfconstant("Ch") +local pdf_btn = pdfconstant("Btn") +----- pdf_yes = pdfconstant("Yes") +local pdf_off = pdfconstant("Off") +local pdf_p = pdfconstant("P") -- None Invert Outline Push +local pdf_n = pdfconstant("N") -- None Invert Outline Push +-- +local pdf_no_rect = pdfarray { 0, 0, 0, 0 } + +local splitter = lpeg.splitat("=>") + +local formats = { + html = 1, fdf = 2, xml = 3, +} + +function codeinjections.setformsmethod(name) + submitoutputformat = formats[lower(name)] or formats.xml +end + +local flag = { -- /Ff + ReadOnly = 1, -- 1 + Required = 2, -- 2 + NoExport = 4, -- 3 + MultiLine = 4096, -- 13 + Password = 8192, -- 14 + NoToggleToOff = 16384, -- 15 + Radio = 32768, -- 16 + PushButton = 65536, -- 17 + PopUp = 131072, -- 18 + Edit = 262144, -- 19 + Sort = 524288, -- 20 + FileSelect = 1048576, -- 21 + DoNotSpellCheck = 4194304, -- 23 + DoNotScroll = 8388608, -- 24 + Comb = 16777216, -- 25 + RichText = 33554432, -- 26 + RadiosInUnison = 33554432, -- 26 + CommitOnSelChange = 67108864, -- 27 +} + +local plus = { -- /F + Invisible = 1, -- 1 + Hidden = 2, -- 2 + Printable = 4, -- 3 + Print = 4, -- 3 + NoZoom = 8, -- 4 + NoRotate = 16, -- 5 + NoView = 32, -- 6 + ReadOnly = 64, -- 7 + Locked = 128, -- 8 + ToggleNoView = 256, -- 9 + LockedContents = 512, -- 10, + AutoView = 256, -- 288 (6+9) +} + +-- todo: check what is interfaced + +flag.readonly = flag.ReadOnly +flag.required = flag.Required +flag.protected = flag.Password +flag.sorted = flag.Sort +flag.unavailable = flag.NoExport +flag.nocheck = flag.DoNotSpellCheck +flag.fixed = flag.DoNotScroll +flag.file = flag.FileSelect + +plus.hidden = plus.Hidden +plus.printable = plus.Printable +plus.auto = plus.AutoView + +-- some day .. lpeg with function or table + +local function fieldflag(specification) -- /Ff + local o, n = specification.option, 0 + if o and o ~= "" then + for f in gmatch(o,"[^, ]+") do + n = n + (flag[f] or 0) + end + end + return n +end + +local function fieldplus(specification) -- /F + local o, n = specification.option, 0 + if o and o ~= "" then + for p in gmatch(o,"[^, ]+") do + n = n + (plus[p] or 0) + end + end +-- n = n + 4 + return n +end + +local function checked(what) + local set, bug = references.identify("",what) + if not bug and #set > 0 then + local r, n = pdfaction(set) + return pdfshareobjectreference(r) + end +end + +local function fieldactions(specification) -- share actions + local d, a = { }, nil + a = specification.mousedown + or specification.clickin if a and a ~= "" then d.D = checked(a) end + a = specification.mouseup + or specification.clickout if a and a ~= "" then d.U = checked(a) end + a = specification.regionin if a and a ~= "" then d.E = checked(a) end -- Enter + a = specification.regionout if a and a ~= "" then d.X = checked(a) end -- eXit + a = specification.afterkey if a and a ~= "" then d.K = checked(a) end + a = specification.format if a and a ~= "" then d.F = checked(a) end + a = specification.validate if a and a ~= "" then d.V = checked(a) end + a = specification.calculate if a and a ~= "" then d.C = checked(a) end + a = specification.focusin if a and a ~= "" then d.Fo = checked(a) end + a = specification.focusout if a and a ~= "" then d.Bl = checked(a) end + a = specification.openpage if a and a ~= "" then d.PO = checked(a) end + a = specification.closepage if a and a ~= "" then d.PC = checked(a) end + -- a = specification.visiblepage if a and a ~= "" then d.PV = checked(a) end + -- a = specification.invisiblepage if a and a ~= "" then d.PI = checked(a) end + return next(d) and pdfdictionary(d) +end + +-- fonts and color + +local pdfdocencodingvector, pdfdocencodingcapsule + +-- The pdf doc encoding vector is needed in order to +-- trigger propper unicode. Interesting is that when +-- a glyph is not in the vector, it is still visible +-- as it is taken from some other font. Messy. + +-- To be checked: only when text/line fields. + +local function checkpdfdocencoding() + report_fields("adding pdfdoc encoding vector") + local encoding = dofile(resolvers.findfile("lpdf-enc.lua")) -- no checking, fatal if not present + pdfdocencodingvector = pdfreference(pdfflushobject(encoding)) + local capsule = pdfdictionary { + PDFDocEncoding = pdfdocencodingvector + } + pdfdocencodingcapsule = pdfreference(pdfflushobject(capsule)) + checkpdfdocencoding = function() end +end + +local fontnames = { + rm = { + tf = "Times-Roman", + bf = "Times-Bold", + it = "Times-Italic", + sl = "Times-Italic", + bi = "Times-BoldItalic", + bs = "Times-BoldItalic", + }, + ss = { + tf = "Helvetica", + bf = "Helvetica-Bold", + it = "Helvetica-Oblique", + sl = "Helvetica-Oblique", + bi = "Helvetica-BoldOblique", + bs = "Helvetica-BoldOblique", + }, + tt = { + tf = "Courier", + bf = "Courier-Bold", + it = "Courier-Oblique", + sl = "Courier-Oblique", + bi = "Courier-BoldOblique", + bs = "Courier-BoldOblique", + }, + symbol = { + dingbats = "ZapfDingbats", + } +} + +local usedfonts = { } + +local function fieldsurrounding(specification) + local fontsize = specification.fontsize or "12pt" + local fontstyle = specification.fontstyle or "rm" + local fontalternative = specification.fontalternative or "tf" + local colorvalue = specification.colorvalue + local s = fontnames[fontstyle] + if not s then + fontstyle, s = "rm", fontnames.rm + end + local a = s[fontalternative] + if not a then + alternative, a = "tf", s.tf + end + local tag = fontstyle .. fontalternative + fontsize = todimen(fontsize) + fontsize = fontsize and (bpfactor * fontsize) or 12 + fontraise = 0.1 * fontsize -- todo: figure out what the natural one is and compensate for strutdp + local fontcode = format("%0.4f Tf %0.4f Ts",fontsize,fontraise) + -- we could test for colorvalue being 1 (black) and omit it then + local colorcode = lpdf.color(3,colorvalue) -- we force an rgb color space + if trace_fields then + report_fields("using font, style %a, alternative %a, size %p, tag %a, code %a",fontstyle,fontalternative,fontsize,tag,fontcode) + report_fields("using color, value %a, code %a",colorvalue,colorcode) + end + local stream = pdfstream { + pdfconstant(tag), + format("%s %s",fontcode,colorcode) + } + usedfonts[tag] = a -- the name + -- move up with "x.y Ts" + return tostring(stream) +end + +local function registerfonts() + if next(usedfonts) then + checkpdfdocencoding() -- already done + local d = pdfdictionary() + local pdffonttype, pdffontsubtype = pdfconstant("Font"), pdfconstant("Type1") + for tag, name in next, usedfonts do + local f = pdfdictionary { + Type = pdffonttype, + Subtype = pdffontsubtype, + Name = pdfconstant(tag), + BaseFont = pdfconstant(name), + Encoding = pdfdocencodingvector, + } + d[tag] = pdfreference(pdfflushobject(f)) + end + return d + end +end + +-- symbols + +local function fieldappearances(specification) + -- todo: caching + local values = specification.values + local default = specification.default -- todo + if not values then + -- error + return + end + local v = settings_to_array(values) + local n, r, d + if #v == 1 then + n, r, d = v[1], v[1], v[1] + elseif #v == 2 then + n, r, d = v[1], v[1], v[2] + else + n, r, d = v[1], v[2], v[3] + end + local appearance = pdfdictionary { + N = registeredsymbol(n), R = registeredsymbol(r), D = registeredsymbol(d), + } + return pdfshareobjectreference(appearance) +-- return pdfreference(pdfflushobject(appearance)) +end + +local YesorOn = "Yes" -- somehow On is not always working out well any longer (why o why this change) + +-- beware ... maybe we should have unique /Yes1 ... we will probably +-- change this one too. +-- +-- TODO: the same as radio .. play safe and use different names. + +local function fieldstates_check(specification,forceyes,values,default,yesdefault) + -- we don't use Opt here (too messy for radio buttons) + local values, default = values or specification.values, default or specification.default + if not values or values == "" then + -- error + return + end + local v = settings_to_array(values) + local yes, off, yesn, yesr, yesd, offn, offr, offd + if #v == 1 then + yes, off = v[1], v[1] + else + yes, off = v[1], v[2] + end + local yesshown, yesvalue = lpegmatch(splitter,yes) + if not (yesshown and yesvalue) then + yesshown = yes, yes + end + yes = settings_to_array(yesshown) + local offshown, offvalue = lpegmatch(splitter,off) + if not (offshown and offvalue) then + offshown = off, off + end + off = settings_to_array(offshown) + if #yes == 1 then + yesn, yesr, yesd = yes[1], yes[1], yes[1] + elseif #yes == 2 then + yesn, yesr, yesd = yes[1], yes[1], yes[2] + else + yesn, yesr, yesd = yes[1], yes[2], yes[3] + end + if #off == 1 then + offn, offr, offd = off[1], off[1], off[1] + elseif #off == 2 then + offn, offr, offd = off[1], off[1], off[2] + else + offn, offr, offd = off[1], off[2], off[3] + end + if not yesvalue then + yesvalue = yesdefault or yesn + end + if not offvalue then + offvalue = offn + end + if forceyes == true then + forceyes = YesorOn -- spec likes Yes more but we've used On for ages now + else + -- false or string + end + if default == yesn then + default = pdfconstant(forceyes or yesn) + else + default = pdf_off + end + local appearance + if false then -- needs testing + appearance = pdfdictionary { -- maybe also cache components + N = pdfshareobjectreference(pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) }), + R = pdfshareobjectreference(pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) }), + D = pdfshareobjectreference(pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) }), + } + else + appearance = pdfdictionary { -- maybe also cache components + N = pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) }, + R = pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) }, + D = pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) } + } + end + local appearanceref = pdfshareobjectreference(appearance) + -- local appearanceref = pdfreference(pdfflushobject(appearance)) + return appearanceref, default, yesvalue +end + +-- It looks like there is always a (MK related) symbol used and that +-- the appearances are only used as ornaments behind a symbol. So, +-- contrary to what we did when widgets showed up, we now limit +-- ourself to more dumb definitions. Especially when highlighting is +-- enabled weird interferences happen. So, we play safe (some nice code +-- has been removed that worked well till recently). + +local function fieldstates_radio(specification,name,parent) + local values = values or specification.values + local default = default or parent.default -- specification.default + if not values or values == "" then + -- error + return + end + local v = settings_to_array(values) + local yes, off, yesn, yesr, yesd, offn, offr, offd + if #v == 1 then + yes, off = v[1], v[1] + else + yes, off = v[1], v[2] + end + -- yes keys might be the same in the three appearances within a field + -- but can best be different among fields ... don't ask why + local yessymbols, yesvalue = lpegmatch(splitter,yes) -- n,r,d=>x + if not (yessymbols and yesvalue) then + yessymbols = yes + end + if not yesvalue then + yesvalue = name + end + yessymbols = settings_to_array(yessymbols) + if #yessymbols == 1 then + yesn = yessymbols[1] + yesr = yesn + yesd = yesr + elseif #yessymbols == 2 then + yesn = yessymbols[1] + yesr = yessymbols[2] + yesd = yesr + else + yesn = yessymbols[1] + yesr = yessymbols[2] + yesd = yessymbols[3] + end + -- we don't care about names, as all will be /Off + local offsymbols = lpegmatch(splitter,off) or off + offsymbols = settings_to_array(offsymbols) + if #offsymbols == 1 then + offn = offsymbols[1] + offr = offn + offd = offr + elseif #offsymbols == 2 then + offn = offsymbols[1] + offr = offsymbols[2] + offd = offr + else + offn = offsymbols[1] + offr = offsymbols[2] + offd = offsymbols[3] + end + if default == name then + default = pdfconstant(name) + else + default = pdf_off + end + -- + local appearance + if false then -- needs testing + appearance = pdfdictionary { -- maybe also cache components + N = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesn), Off = registeredsymbol(offn) }), + R = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesr), Off = registeredsymbol(offr) }), + D = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesd), Off = registeredsymbol(offd) }), + } + else + appearance = pdfdictionary { -- maybe also cache components + N = pdfdictionary { [name] = registeredsymbol(yesn), Off = registeredsymbol(offn) }, + R = pdfdictionary { [name] = registeredsymbol(yesr), Off = registeredsymbol(offr) }, + D = pdfdictionary { [name] = registeredsymbol(yesd), Off = registeredsymbol(offd) } + } + end + local appearanceref = pdfshareobjectreference(appearance) -- pdfreference(pdfflushobject(appearance)) + return appearanceref, default, yesvalue +end + +local function fielddefault(field) + local default = field.default + if not default or default == "" then + local values = settings_to_array(field.values) + default = values[1] + end + if not default or default == "" then + return pdf_off + else + return pdfconstant(default) + end +end + +local function fieldoptions(specification) + local values = specification.values + local default = specification.default + if values then + local v = settings_to_array(values) + for i=1,#v do + local vi = v[i] + local shown, value = lpegmatch(splitter,vi) + if shown and value then + v[i] = pdfarray { pdfunicode(value), shown } + else + v[i] = pdfunicode(v[i]) + end + end + return pdfarray(v) + end +end + +local mapping = { + -- acrobat compliant (messy, probably some pdfdoc encoding interference here) + check = "4", -- 0x34 + circle = "l", -- 0x6C + cross = "8", -- 0x38 + diamond = "u", -- 0x75 + square = "n", -- 0x6E + star = "H", -- 0x48 +} + +local function todingbat(n) + if n and n ~= "" then + return mapping[n] or "" + end +end + +-- local zero_bc = pdfarray { 0, 0, 0 } +-- local zero_bg = pdfarray { 1, 1, 1 } + +local function fieldrendering(specification) + local bvalue = tonumber(specification.backgroundcolorvalue) + local fvalue = tonumber(specification.framecolorvalue) + local svalue = specification.fontsymbol + if bvalue or fvalue or (svalue and svalue ~= "") then + return pdfdictionary { + BG = bvalue and pdfarray { lpdf.colorvalues(3,bvalue) } or nil, -- or zero_bg, + BC = fvalue and pdfarray { lpdf.colorvalues(3,fvalue) } or nil, -- or zero_bc, + CA = svalue and pdfstring (svalue) or nil, + } + end +end + +-- layers + +local function fieldlayer(specification) -- we can move this in line + local layer = specification.layer + return (layer and lpdf.layerreference(layer)) or nil +end + +-- defining + +local fields, radios, clones, fieldsets, calculationset = { }, { }, { }, { }, nil + +local xfdftemplate = [[ +<?xml version='1.0' encoding='UTF-8'?> + +<xfdf xmlns='http://ns.adobe.com/xfdf/'> + <f href='%s.pdf'/> + <fields> +%s + </fields> +</xfdf> +]] + +function codeinjections.exportformdata(name) + local result = { } + for k, v in table.sortedhash(fields) do + result[#result+1] = format(" <field name='%s'><value>%s</value></field>",v.name or k,v.default or "") + end + local base = file.basename(tex.jobname) + local xfdf = format(xfdftemplate,base,table.concat(result,"\n")) + if not name or name == "" then + name = base + end + io.savedata(file.addsuffix(name,"xfdf"),xfdf) +end + +function codeinjections.definefieldset(tag,list) + fieldsets[tag] = list +end + +function codeinjections.getfieldset(tag) + return fieldsets[tag] +end + +local function fieldsetlist(tag) + if tag then + local ft = fieldsets[tag] + if ft then + local a = pdfarray() + for name in gmatch(list,"[^, ]+") do + local f = field[name] + if f and f.pobj then + a[#a+1] = pdfreference(f.pobj) + end + end + return a + end + end +end + +function codeinjections.setfieldcalculationset(tag) + calculationset = tag +end + +local function predefinesymbols(specification) + local values = specification.values + if values then + local symbols = settings_to_array(values) + for i=1,#symbols do + local symbol = symbols[i] + local a, b = lpegmatch(splitter,symbol) + codeinjections.presetsymbol(a or symbol) + end + end +end + +function codeinjections.getdefaultfieldvalue(name) + local f = fields[name] + if f then + local values = f.values + local default = f.default + if not default or default == "" then + local symbols = settings_to_array(values) + local symbol = symbols[1] + if symbol then + local a, b = lpegmatch(splitter,symbol) -- splits at => + default = a or symbol + end + end + return default + end +end + +function codeinjections.definefield(specification) + local n = specification.name + local f = fields[n] + if not f then + local fieldtype = specification.type + if not fieldtype then + if trace_fields then + report_fields("invalid definition for %a, unknown type",n) + end + elseif fieldtype == "radio" then + local values = specification.values + if values and values ~= "" then + values = settings_to_array(values) + for v=1,#values do + radios[values[v]] = { parent = n } + end + fields[n] = specification + if trace_fields then + report_fields("defining %a as type %a",n,"radio") + end + elseif trace_fields then + report_fields("invalid definition of radio %a, missing values",n) + end + elseif fieldtype == "sub" then + -- not in main field list ! + local radio = radios[n] + if radio then + -- merge specification + for key, value in next, specification do + radio[key] = value + end + if trace_fields then + local p = radios[n] and radios[n].parent + report_fields("defining %a as type sub of radio %a",n,p) + end + elseif trace_fields then + report_fields("invalid definition of radio sub %a, no parent given",n) + end + predefinesymbols(specification) + elseif fieldtype == "text" or fieldtype == "line" then + fields[n] = specification + if trace_fields then + report_fields("defining %a as type %a",n,fieldtype) + end + if specification.values ~= "" and specification.default == "" then + specification.default, specification.values = specification.values, nil + end + else + fields[n] = specification + if trace_fields then + report_fields("defining %a as type %a",n,fieldtype) + end + predefinesymbols(specification) + end + elseif trace_fields then + report_fields("invalid definition for %a, already defined",n) + end +end + +function codeinjections.clonefield(specification) -- obsolete + local p, c, v = specification.parent, specification.children, specification.alternative + if not p or not c then + if trace_fields then + report_fields("invalid clone, children %a, parent %a, alternative %a",c,p,v) + end + return + end + local x = fields[p] or radios[p] + if not x then + if trace_fields then + report_fields("invalid clone, unknown parent %a",p) + end + return + end + for n in gmatch(c,"[^, ]+") do + local f, r, c = fields[n], radios[n], clones[n] + if f or r or c then + if trace_fields then + report_fields("already cloned, child %a, parent %a, alternative %a",n,p,v) + end + else + if trace_fields then + report_fields("cloning, child %a, parent %a, alternative %a",n,p,v) + end + clones[n] = specification + predefinesymbols(specification) + end + end +end + +function codeinjections.getfieldcategory(name) + local f = fields[name] or radios[name] or clones[name] + if f then + local g = f.category + if not g or g == "" then + local v, p, t = f.alternative, f.parent, f.type + if v == "clone" or v == "copy" then + f = fields[p] or radios[p] + g = f and f.category + elseif t == "sub" then + f = fields[p] + g = f and f.category + end + end + return g + end +end + +-- + +function codeinjections.validfieldcategory(name) + return fields[name] or radios[name] or clones[name] +end + +function codeinjections.validfieldset(name) + return fieldsets[tag] +end + +function codeinjections.validfield(name) + return fields[name] +end + +-- + +local alignments = { + flushleft = 0, right = 0, + center = 1, middle = 1, + flushright = 2, left = 2, +} + +local function fieldalignment(specification) + return alignments[specification.align] or 0 +end + +local function enhance(specification,option) + local so = specification.option + if so and so ~= "" then + specification.option = so .. "," .. option + else + specification.option = option + end + return specification +end + +-- finish + +local collected = pdfarray() +local forceencoding = false + +local function finishfields() + local sometext = forceencoding + for name, field in next, fields do + local kids = field.kids + if kids then + pdfflushobject(field.kidsnum,kids) + end + local opt = field.opt + if opt then + pdfflushobject(field.optnum,opt) + end + local type = field.type + if not sometext and (type == "text" or type == "line") then + sometext = true + end + end + for name, field in next, radios do + local kids = field.kids + if kids then + pdfflushobject(field.kidsnum,kids) + end + local opt = field.opt + if opt then + pdfflushobject(field.optnum,opt) + end + end + if #collected > 0 then + local acroform = pdfdictionary { + NeedAppearances = true, + Fields = pdfreference(pdfflushobject(collected)), + CO = fieldsetlist(calculationset), + } + if sometext then + checkpdfdocencoding() + usedfonts.tttf = fontnames.tt.tf + acroform.DA = "/tttf 12 Tf 0 g" + acroform.DR = pdfdictionary { + Font = registerfonts(), + Encoding = pdfdocencodingcapsule, + } + end + lpdf.addtocatalog("AcroForm",pdfreference(pdfflushobject(acroform))) + end +end + +lpdf.registerdocumentfinalizer(finishfields,"form fields") + +local methods = { } + +function nodeinjections.typesetfield(name,specification) + local field = fields[name] or radios[name] or clones[name] + if not field then + report_fields( "unknown child %a",name) + -- unknown field + return + end + local alternative, parent = field.alternative, field.parent + if alternative == "copy" or alternative == "clone" then -- only in clones + field = fields[parent] or radios[parent] + end + local method = methods[field.type] + if method then + return method(name,specification,alternative) + else + report_fields( "unknown method %a for child %a",field.type,name) + end +end + +local function save_parent(field,specification,d,hasopt) + local kidsnum = pdfreserveobject() + d.Kids = pdfreference(kidsnum) + field.kidsnum = kidsnum + field.kids = pdfarray() + if hasopt then + local optnum = pdfreserveobject() + d.Opt = pdfreference(optnum) + field.optnum = optnum + field.opt = pdfarray() + end + local pnum = pdfflushobject(d) + field.pobj = pnum + collected[#collected+1] = pdfreference(pnum) +end + +local function save_kid(field,specification,d,optname) + local kn = pdfreserveannotation() + field.kids[#field.kids+1] = pdfreference(kn) + if optname then + local opt = field.opt + if opt then + opt[#opt+1] = optname + end + end + local width, height, depth = specification.width or 0, specification.height or 0, specification.depth + local box = hpack_node(pdfannotation_node(width,height,depth,d(),kn)) + box.width, box.height, box.depth = width, height, depth -- redundant + return box +end + +local function makelineparent(field,specification) + local text = pdfunicode(field.default) + local length = tonumber(specification.length or 0) or 0 + local d = pdfdictionary { + Subtype = pdf_widget, + T = pdfunicode(specification.title), + F = fieldplus(specification), + Ff = fieldflag(specification), + OC = fieldlayer(specification), + DA = fieldsurrounding(specification), + AA = fieldactions(specification), + FT = pdf_tx, + Q = fieldalignment(specification), + MaxLen = length == 0 and 1000 or length, + DV = text, + V = text, + } + save_parent(field,specification,d) +end + +local function makelinechild(name,specification) + local field, parent = clones[name], nil + if field then + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("forcing parent text %a",parent.name) + end + makelineparent(parent,specification) + end + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent text %a",name) + end + makelineparent(parent,specification) + end + end + if trace_fields then + report_fields("using child text %a",name) + end + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(parent.pobj), + F = fieldplus(specification), + OC = fieldlayer(specification), + DA = fieldsurrounding(specification), + AA = fieldactions(specification), + MK = fieldrendering(specification), + Q = fieldalignment(specification), + } + return save_kid(parent,specification,d) +end + +function methods.line(name,specification) + return makelinechild(name,specification) +end + +function methods.text(name,specification) + return makelinechild(name,enhance(specification,"MultiLine")) +end + +local function makechoiceparent(field,specification) + local d = pdfdictionary { + Subtype = pdf_widget, + T = pdfunicode(specification.title), + F = fieldplus(specification), + Ff = fieldflag(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + FT = pdf_ch, + Opt = fieldoptions(field), -- todo + } + save_parent(field,specification,d) +end + +local function makechoicechild(name,specification) + local field, parent = clones[name], nil + if field then + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("forcing parent choice %a",parent.name) + end + makechoiceparent(parent,specification,extras) + end + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent choice %a",name) + end + makechoiceparent(parent,specification,extras) + end + end + if trace_fields then + report_fields("using child choice %a",name) + end + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(parent.pobj), + F = fieldplus(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + } + return save_kid(parent,specification,d) -- do opt here +end + +function methods.choice(name,specification) + return makechoicechild(name,specification) +end + +function methods.popup(name,specification) + return makechoicechild(name,enhance(specification,"PopUp")) +end + +function methods.combo(name,specification) + return makechoicechild(name,enhance(specification,"PopUp,Edit")) +end + +local function makecheckparent(field,specification) + local d = pdfdictionary { + T = pdfunicode(specification.title), -- todo: when tracing use a string + F = fieldplus(specification), + Ff = fieldflag(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + FT = pdf_btn, + V = fielddefault(field), + } + save_parent(field,specification,d,true) +end + +local function makecheckchild(name,specification) + local field, parent = clones[name], nil + if field then + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("forcing parent check %a",parent.name) + end + makecheckparent(parent,specification,extras) + end + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent check %a",name) + end + makecheckparent(parent,specification,extras) + end + end + if trace_fields then + report_fields("using child check %a",name) + end + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(parent.pobj), + F = fieldplus(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + H = pdf_n, + } + local fontsymbol = specification.fontsymbol + if fontsymbol and fontsymbol ~= "" then + specification.fontsymbol = todingbat(fontsymbol) + specification.fontstyle = "symbol" + specification.fontalternative = "dingbats" + d.DA = fieldsurrounding(specification) + d.MK = fieldrendering(specification) + return save_kid(parent,specification,d) + else + local appearance, default, value = fieldstates_check(field,true) + d.AS = default + d.AP = appearance + return save_kid(parent,specification,d,value) + end +end + +function methods.check(name,specification) + return makecheckchild(name,specification) +end + +local function makepushparent(field,specification) -- check if we can share with the previous + local d = pdfdictionary { + Subtype = pdf_widget, + T = pdfunicode(specification.title), + F = fieldplus(specification), + Ff = fieldflag(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + FT = pdf_btn, + AP = fieldappearances(field), + H = pdf_p, + } + save_parent(field,specification,d) +end + +local function makepushchild(name,specification) + local field, parent = clones[name], nil + if field then + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("forcing parent push %a",parent.name) + end + makepushparent(parent,specification) + end + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent push %a",name) + end + makepushparent(parent,specification) + end + end + if trace_fields then + report_fields("using child push %a",name) + end + local fontsymbol = specification.fontsymbol + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(field.pobj), + F = fieldplus(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + H = pdf_p, + } + if fontsymbol and fontsymbol ~= "" then + specification.fontsymbol = todingbat(fontsymbol) + specification.fontstyle = "symbol" + specification.fontalternative = "dingbats" + d.DA = fieldsurrounding(specification) + d.MK = fieldrendering(specification) + else + d.AP = fieldappearances(field) + end + return save_kid(parent,specification,d) +end + +function methods.push(name,specification) + return makepushchild(name,enhance(specification,"PushButton")) +end + +local function makeradioparent(field,specification) +-- specification = enhance(specification,"Radio,RadiosInUnison") + specification = enhance(specification,"Radio,RadiosInUnison,Print,NoToggleToOff") +-- specification = enhance(specification,"Radio,Print,NoToggleToOff") + local d = pdfdictionary { + T = field.name, + FT = pdf_btn, +-- F = fieldplus(specification), + Ff = fieldflag(specification), +-- H = pdf_n, + V = fielddefault(field), + } + save_parent(field,specification,d,true) +end + +-- local function makeradiochild(name,specification) +-- local field, parent = clones[name], nil +-- if field then +-- field = radios[field.parent] +-- parent = fields[field.parent] +-- if not parent.pobj then +-- if trace_fields then +-- report_fields("forcing parent radio %a",parent.name) +-- end +-- makeradioparent(parent,parent) +-- end +-- else +-- field = radios[name] +-- if not field then +-- report_fields("there is some problem with field %a",name) +-- return nil +-- end +-- parent = fields[field.parent] +-- if not parent.pobj then +-- if trace_fields then +-- report_fields("using parent radio %a",name) +-- end +-- makeradioparent(parent,parent) +-- end +-- end +-- if trace_fields then +-- report_fields("using child radio %a with values %a and default %a",name,field.values,field.default) +-- end +-- local fontsymbol = specification.fontsymbol +-- fontsymbol="star" +-- local d = pdfdictionary { +-- Subtype = pdf_widget, +-- Parent = pdfreference(parent.pobj), +-- F = fieldplus(specification), +-- OC = fieldlayer(specification), +-- AA = fieldactions(specification), +-- H = pdf_n, +-- } +-- if fontsymbol and fontsymbol ~= "" then +-- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok +-- specification.fontsymbol = todingbat(fontsymbol) +-- specification.fontstyle = "symbol" +-- specification.fontalternative = "dingbats" +-- d.DA = fieldsurrounding(specification) +-- d.MK = fieldrendering(specification) +-- d.AS = pdfconstant(value) -- default -- mandate when AP but confuses viewers +-- d.AP = appearance +-- return save_kid(parent,specification,d,value) +-- -- return save_kid(parent,specification,d,name) +-- else +-- -- local appearance, default, value = fieldstates_radio(field,true) -- false is also ok +-- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok +-- d.AS = default -- mandate when AP but confuses viewers +-- d.AP = appearance +-- return save_kid(parent,specification,d,value) +-- end +-- end + +local function makeradiochild(name,specification) + local field, parent = clones[name], nil + if field then + field = radios[field.parent] + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("forcing parent radio %a",parent.name) + end + makeradioparent(parent,parent) + end + else + field = radios[name] + if not field then + report_fields("there is some problem with field %a",name) + return nil + end + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("using parent radio %a",name) + end + makeradioparent(parent,parent) + end + end + if trace_fields then + report_fields("using child radio %a with values %a and default %a",name,field.values,field.default) + end + local fontsymbol = specification.fontsymbol + -- fontsymbol = "circle" + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(parent.pobj), + F = fieldplus(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + H = pdf_n, + } + if fontsymbol and fontsymbol ~= "" then + specification.fontsymbol = todingbat(fontsymbol) + specification.fontstyle = "symbol" + specification.fontalternative = "dingbats" + d.DA = fieldsurrounding(specification) + d.MK = fieldrendering(specification) + end + local appearance, default, value = fieldstates_radio(field,name,fields[field.parent]) + d.AP = appearance + d.AS = default -- /Whatever + return save_kid(parent,specification,d,value) +end + +function methods.sub(name,specification) + return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison")) +end |