diff options
author | Hans Hagen <pragma@wxs.nl> | 2013-05-19 19:27:00 +0200 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 2013-05-19 19:27:00 +0200 |
commit | 9a10021cd4cb23995ad3ffa915fc5b7f6890aaf8 (patch) | |
tree | 983e02c29872e4713a10a9dc8ae7708c6da3de88 /tex/context/base/lpdf-fld.lua | |
parent | 0deffde58a47f5c85a46a7d999ff9cf817b81cef (diff) | |
download | context-9a10021cd4cb23995ad3ffa915fc5b7f6890aaf8.tar.gz |
beta 2013.05.19 19:27
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 a9b9fd72d..0a15bb850 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
|