diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2011-06-01 08:54:21 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2011-06-01 08:54:21 +0000 |
commit | d7ccb42582f85acf30568913610ccf4d602023fb (patch) | |
tree | 7292e3545a420676878e7451b68892d360c62cb6 /Master/texmf-dist/tex/context/base/lpdf-fld.lua | |
parent | 2d62a6fe9b80def59c392268022f1f9a2d6e358f (diff) |
commit context 2011.05.18
git-svn-id: svn://tug.org/texlive/trunk@22719 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lpdf-fld.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lpdf-fld.lua | 899 |
1 files changed, 541 insertions, 358 deletions
diff --git a/Master/texmf-dist/tex/context/base/lpdf-fld.lua b/Master/texmf-dist/tex/context/base/lpdf-fld.lua index c034aec6c91..590aee1abe5 100644 --- a/Master/texmf-dist/tex/context/base/lpdf-fld.lua +++ b/Master/texmf-dist/tex/context/base/lpdf-fld.lua @@ -6,38 +6,78 @@ if not modules then modules = { } end modules ['lpdf-fld'] = { license = "see context related readme files" } --- cleaned up, e.g. no longer older viewers --- always kids so no longer explicit main / clone / copy --- some optimizations removed (will come bakc if needed) +-- 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. 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("widgets.fields", function(v) trace_fields = v end) +local trace_fields = false trackers.register("backends.fields", function(v) trace_fields = v end) -local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes +local report_fields = logs.reporter("backend","fields") -local variables = interfaces.variables +local backends, lpdf = backends, lpdf -local nodeinjections = backends.pdf.nodeinjections -local codeinjections = backends.pdf.codeinjections -local registrations = backends.pdf.registrations +local variables = interfaces.variables +local context = context -local registeredsymbol = codeinjections.registeredsymbol +local references = structures.references +local settings_to_array = utilities.parsers.settings_to_array -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 pdfreserveobject = lpdf.reserveobject -local pdfannotation = nodes.pdfannotation +local pdfbackend = backends.pdf -local submitoutputformat = 0 -- 0=unknown 1=HTML 2=FDF 3=XML => not yet used, needs to be checked +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 pdfimmediateobject = lpdf.immediateobject +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") +local 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("=>") @@ -46,7 +86,7 @@ local formats = { } function codeinjections.setformsmethod(name) - submitoutputformat = formats[lower(name)] or 3 + submitoutputformat = formats[lower(name)] or formats.xml end local flag = { @@ -94,7 +134,7 @@ plus.auto = plus.AutoView -- some day .. lpeg with function or table local function fieldflag(specification) - local o, n = specification.options, 0 + local o, n = specification.option, 0 if o and o ~= "" then for f in gmatch(o,"[^, ]+") do n = n + (flag[f] or 0) @@ -104,7 +144,7 @@ local function fieldflag(specification) end local function fieldplus(specification) - local o, n = specification.options, 0 + local o, n = specification.option, 0 if o and o ~= "" then for p in gmatch(o,"[^, ]+") do n = n + (plus[p] or 0) @@ -113,29 +153,30 @@ local function fieldplus(specification) return n end - local function checked(what) - if what and what ~= "" then - local set, bug = jobreferences.identify("",what) - return not bug and #set > 0 and lpdf.pdfaction(set) + 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 ---~ print(table.serialize(specification)) local d, a = { }, nil - a = specification.mousedown if a and a ~= "" then d.D = checked(a) end - a = specification.mouseup if a and a ~= "" then d.U = checked(a) end + 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.afterkeystroke if a and a ~= "" then d.K = checked(a) end - a = specification.formatresult if a and a ~= "" then d.F = checked(a) end - a = specification.validateresult if a and a ~= "" then d.V = checked(a) end - a = specification.calculatewhatever if a and a ~= "" then d.C = checked(a) end + 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.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) @@ -143,6 +184,26 @@ 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", @@ -167,44 +228,59 @@ local fontnames = { sl = "Courier-Oblique", bi = "Courier-BoldOblique", bs = "Courier-BoldOblique", + }, + symbol = { + dingbats = "ZapfDingbats", } } local usedfonts = { } local function fieldsurrounding(specification) - local size = specification.fontsize or "12pt" - local style = specification.fontstyle or "rm" - local alternative = specification.fontalternative or "tf" - local s = fontnames[style] + 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 - style, s = "rm", fontnames.rm + fontstyle, s = "rm", fontnames.rm end - local a = s[alternative] + local a = s[fontalternative] if not a then alternative, a = "tf", s.tf end - local tag = style .. alternative - size = string.todimen(size) + 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("fontcode : %s %s @ %s => %s => %s",fontstyle,fontalternative,fontsize,tag,fontcode) + report_fields("colorcode: %s => %s",colorvalue,colorcode) + end local stream = pdfstream { pdfconstant(tag), - format("%0.4f Tf",(size and (number.dimenfactors.bp * size)) or 12), + format("%s %s",fontcode,colorcode) } usedfonts[tag] = a -- the name - -- add color to stream: 0 g -- 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 = pdfconstant("Font"), - Subtype = pdfconstant("Type1"), -- todo + Type = pdffonttype, + Subtype = pdffontsubtype, Name = pdfconstant(tag), BaseFont = pdfconstant(name), + Encoding = pdfdocencodingvector, } d[tag] = pdfreference(pdfflushobject(f)) end @@ -212,16 +288,6 @@ local function registerfonts() end end --- cache - -local function fieldattributes(specification) ---~ return pdfarray { ---~ -- BG = -- backgroundcolor ---~ -- BC = -- framecolor ---~ } - return nil -end - -- symbols local function fieldappearances(specification) @@ -232,7 +298,7 @@ local function fieldappearances(specification) -- error return end - local v = aux.settings_to_array(values) + local v = settings_to_array(values) local n, r, d if #v == 1 then n, r, d = v[1], v[1], v[1] @@ -241,21 +307,23 @@ local function fieldappearances(specification) else n, r, d = v[1], v[2], v[3] end - local appearance = pdfdictionary { -- cache this one + local appearance = pdfdictionary { N = registeredsymbol(n), R = registeredsymbol(r), D = registeredsymbol(d), } - return lpdf.sharedobj(tostring(appearance)) + return pdfshareobjectreference(appearance) end +local YesorOn = "Yes" -- somehow On is not always working out well any longer (why o why this change) + local function fieldstates(specification,forceyes,values,default) -- 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 then + if not values or values == "" then -- error return end - local v = aux.settings_to_array(values) - local yes, off + 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 @@ -265,12 +333,12 @@ local function fieldstates(specification,forceyes,values,default) if not (yesshown and yesvalue) then yesshown = yes, yes end - yes = aux.settings_to_array(yesshown) + yes = settings_to_array(yesshown) local offshown, offvalue = lpegmatch(splitter,off) if not (offshown and offvalue) then offshown = off, off end - off = aux.settings_to_array(offshown) + off = settings_to_array(offshown) if #yes == 1 then yesn, yesr, yesd = yes[1], yes[1], yes[1] elseif #yes == 2 then @@ -292,29 +360,42 @@ local function fieldstates(specification,forceyes,values,default) offvalue = offn end if forceyes == true then - forceyes = forceyes and "On" -- spec likes Yes more but we've used On for ages now + forceyes = forceyes and 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 = pdfconstant("Off") + default = pdf_off end local 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) } } - local appearanceref = lpdf.sharedobj(tostring(appearance)) - return appearanceref, default + local appearanceref = pdfshareobjectreference(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 = aux.settings_to_array(values) + local v = settings_to_array(values) for i=1,#v do local vi = v[i] local shown, value = lpegmatch(splitter,vi) @@ -328,21 +409,32 @@ local function fieldoptions(specification) end end -local function radiodefault(parent,field,forceyes) - local default, values = parent.default, parent.values - if not default or default == "" then - values = aux.settings_to_array(values) - default = values[1] +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 - local name = field.name - local fieldvalues = aux.settings_to_array(field.values) - local yes, off = fieldvalues[1], fieldvalues[2] or fieldvalues[1] - if not default then - return pdfconstant((forceyes and "On") or yes) - elseif default == name then - return pdfconstant((forceyes and "On") or default) - else - return pdfconstant("Off") +end + +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, + BC = fvalue and pdfarray { lpdf.colorvalues(3,fvalue) } or nil, + CA = svalue and pdfstring (svalue) or nil, + } end end @@ -350,7 +442,7 @@ end local function fieldlayer(specification) -- we can move this in line local layer = specification.layer - return (layer and lpdf.layerreferences[layer]) or nil + return (layer and lpdf.layerreference(layer)) or nil end -- defining @@ -388,7 +480,7 @@ end local function predefinesymbols(specification) local values = specification.values if values then - local symbols = aux.settings_to_array(values) + local symbols = settings_to_array(values) for i=1,#symbols do local symbol = symbols[i] local a, b = lpegmatch(splitter,symbol) @@ -403,16 +495,14 @@ function codeinjections.getdefaultfieldvalue(name) local values = f.values local default = f.default if not default or default == "" then - local symbols = aux.settings_to_array(values) + 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 - if default then - tex.sprint(ctxcatcodes,default) - end + return default end end @@ -420,26 +510,26 @@ function codeinjections.definefield(specification) local n = specification.name local f = fields[n] if not f then - local kind = specification.kind - if not kind then + local fieldtype = specification.type + if not fieldtype then if trace_fields then - logs.report("fields","invalid definition of '%s': unknown type",n) + report_fields("invalid definition of '%s': unknown type",n) end - elseif kind == "radio" then + elseif fieldtype == "radio" then local values = specification.values if values and values ~= "" then - values = aux.settings_to_array(values) + values = settings_to_array(values) for v=1,#values do radios[values[v]] = { parent = n } end fields[n] = specification if trace_fields then - logs.report("fields","defining '%s' as radio",n or "?") + report_fields("defining '%s' as radio",n or "?") end elseif trace_fields then - logs.report("fields","invalid definition of radio '%s': missing values",n) + report_fields("invalid definition of radio '%s': missing values",n) end - elseif kind == "sub" then + elseif fieldtype == "sub" then -- not in main field list ! local radio = radios[n] if radio then @@ -449,16 +539,16 @@ function codeinjections.definefield(specification) end if trace_fields then local p = radios[n] and radios[n].parent - logs.report("fields","defining '%s' as sub of radio '%s'",n or "?",p or "?") + report_fields("defining '%s' as sub of radio '%s'",n or "?",p or "?") end elseif trace_fields then - logs.report("fields","invalid definition of radio sub '%s': no parent",n) + report_fields("invalid definition of radio sub '%s': no parent",n) end predefinesymbols(specification) - elseif kind == "text" or kind == "line" then + elseif fieldtype == "text" or fieldtype == "line" then fields[n] = specification if trace_fields then - logs.report("fields","defining '%s' as %s",n,kind) + report_fields("defining '%s' as %s",n,fieldtype) end if specification.values ~= "" and specification.default == "" then specification.default, specification.values = specification.values, nil @@ -466,69 +556,76 @@ function codeinjections.definefield(specification) else fields[n] = specification if trace_fields then - logs.report("fields","defining '%s' as %s",n,kind) + report_fields("defining '%s' as %s",n,fieldtype) end predefinesymbols(specification) end elseif trace_fields then - logs.report("fields","invalid definition of '%s': already defined",n) + report_fields("invalid definition of '%s': already defined",n) end end -function codeinjections.clonefield(specification) - local p, c, v = specification.parent, specification.children, specification.variant +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 - logs.report("fields","invalid clone: children: '%s', parent '%s', variant: '%s'",p or "?",c or "?", v or "?") + report_fields("invalid clone: children: '%s', parent '%s', alternative: '%s'",p or "?",c or "?", v or "?") end - else - for n in gmatch(c,"[^, ]+") do - local f, r, c, x = fields[n], radios[n], clones[n], fields[p] - if f or r or c then - if trace_fields then - logs.report("fields","already cloned: child: '%s', parent '%s', variant: '%s'",p or "?",n or "?", v or "?") - end - elseif x then - if trace_fields then - logs.report("fields","invalid clone: child: '%s', variant: '%s', no parent",n or "?", v or "?") - end - else - if trace_fields then - logs.report("fields","cloning: child: '%s', parent '%s', variant: '%s'",p or "?",n or "?", v or "?") - end - clones[n] = specification - predefinesymbols(specification) + return + end + local x = fields[p] + if not x then + if trace_fields then + report_fields("cloning: unknown parent '%s'",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: '%s', parent '%s', alternative: '%s'",p,n, v or "?") end + else + if trace_fields then + report_fields("cloning: child: '%s', parent '%s', alternative: '%s'",p,n, v or "?") + end + clones[n] = specification + predefinesymbols(specification) end end end -function codeinjections.getfieldgroup(name) +function codeinjections.getfieldcategory(name) local f = fields[name] or radios[name] or clones[name] - local g = f and f.group - if not g or g == "" then - local v, p, k = f.variant, f.parent, f.kind - if v == "clone" or v == "copy" then - f = fields[p] or radios[p] - g = f and f.group - elseif k == "sub" then - f = fields[p] - g = f and f.group + 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 - end - if g then - texsprint(ctxcatcodes,g) + return g end end -- -function codeinjections.doiffieldset(tag) - commands.testcase(fieldsets[tag]) +function codeinjections.validfieldcategory(name) + return fields[name] or radios[name] or clones[name] +end + +function codeinjections.validfieldset(name) + return fieldsets[tag] end -function codeinjections.doiffieldelse(name) - commands.testcase(fields[name]) +function codeinjections.validfield(name) + return fields[name] end -- @@ -544,342 +641,428 @@ local function fieldalignment(specification) end local function enhance(specification,option) - local so = specification.options + local so = specification.option if so and so ~= "" then - specification.options = so .. "," .. option + specification.option = so .. "," .. option else - specification.options = option + specification.option = option end return specification end -- finish -local collected = pdfarray() +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.kobj,kids) + 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 - local pobj = field.pobj end for name, field in next, radios do local kids = field.kids if kids then - pdfflushobject(field.kobj,kids) + pdfflushobject(field.kidsnum,kids) + end + local opt = field.opt + if opt then + pdfflushobject(field.optnum,opt) end end if #collected > 0 then - usedfonts.tttf = fontnames.tt.tf local acroform = pdfdictionary { NeedAppearances = true, - Fields = pdfreference(pdfflushobject(collected)), - DR = pdfdictionary { Font = registerfonts() }, - CO = fieldsetlist(calculationset), - DA = "/tttf 12 Tf 0 g", + 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) - -local pdf_widget = pdfconstant("Widget") -local pdf_tx = pdfconstant("Tx") -local pdf_ch = pdfconstant("Ch") -local pdf_btn = pdfconstant("Btn") -local pdf_yes = pdfconstant("Yes") -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 } +lpdf.registerdocumentfinalizer(finishfields,"form fields") local methods = { } -function codeinjections.typesetfield(name,specification) +function nodeinjections.typesetfield(name,specification) local field = fields[name] or radios[name] or clones[name] if not field then - logs.report("fields", "unknown child '%s'",name) + report_fields( "unknown child '%s'",name) -- unknown field return end - local variant, parent = field.variant, field.parent - if variant == "copy" or variant == "clone" then -- only in clones + 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.kind] + local method = methods[field.type] if method then - method(name,specification,variant) + return method(name,specification,alternative) else - logs.report("fields", "unknown method '%s' for child '%s'",field.kind,name) + report_fields( "unknown method '%s' for child '%s'",field.type,name) end end --- can be optional multipass optimization (share objects) - -local function save_parent(field,specification,d) - local kn = pdfreserveobject() - d.Kids = pdfreference(kn) - field.kobj = kn +local function save_parent(field,specification,d,hasopt) + local kidsnum = pdfreserveobject() + d.Kids = pdfreference(kidsnum) + field.kidsnum = kidsnum field.kids = pdfarray() - local pn = pdfflushobject(d) - field.pobj = pn - collected[#collected+1] = pdfreference(pn) + 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) - local kn = pdfreserveobject() +local function save_kid(field,specification,d,optname) + local kn = pdfreserveannotation() field.kids[#field.kids+1] = pdfreference(kn) - node.write(pdfannotation(specification.width,specification.height,0,d(),kn)) + if optname then + field.opt[#field.opt+1] = optname + 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 -function methods.line(name,specification,variant,extras) - local field = fields[name] - if variant == "copy" or variant == "clone" then - logs.report("fields","todo: clones of text fields") - end - local kind = field.kind - if not field.pobj then - if trace_fields then - logs.report("fields","using parent text '%s'",name) +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 '%s'",parent.name) + end + makelineparent(parent,specification) end - if extras then - enhance(specification,extras) + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent text '%s'",name) + end + makelineparent(parent,specification) end - local text = pdfunicode(field.default) - local d = pdfdictionary { - Subtype = pdf_widget, - T = pdfunicode(specification.title), - F = fieldplus(specification), - Ff = fieldflag(specification), - OC = fieldlayer(specification), - MK = fieldsurrounding(specification), -- needed ? - DA = fieldsurrounding(specification), - AA = fieldactions(specification), - FT = pdf_tx, - Q = fieldalignment(specification), - MaxLen = (specification.length == 0 and 1000) or specification.length, - DV = text, - V = text, - } - save_parent(field,specification,d) - field.specification = specification end - specification = field.specification or { } -- todo: radio spec if trace_fields then - logs.report("fields","using child text '%s'",name) + report_fields("using child text '%s'",name) end local d = pdfdictionary { Subtype = pdf_widget, - Parent = pdfreference(field.pobj), + Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), - MK = fieldsurrounding(specification), DA = fieldsurrounding(specification), AA = fieldactions(specification), + MK = fieldrendering(specification), Q = fieldalignment(specification), } - save_kid(field,specification,d) + return save_kid(parent,specification,d) end -function methods.text(name,specification,variant) - methods.line(name,specification,variant,"MultiLine") +function methods.line(name,specification) + return makelinechild(name,specification) end -function methods.choice(name,specification,variant,extras) - local field = fields[name] - if variant == "copy" or variant == "clone" then - logs.report("fields","todo: clones of choice fields") - end - local kind = field.kind - local d - if not field.pobj then - if trace_fields then - logs.report("fields","using parent choice '%s'",name) +function methods.text(name,specification) + return makelinechine(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 '%s'",parent.name) + end + makechoiceparent(parent,specification,extras) end - if extras then - enhance(specification,extras) + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent choice '%s'",name) + end + makechoiceparent(parent,specification,extras) end - 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), - } - save_parent(field,specification,d) - field.specification = specification end - specification = field.specification or { } if trace_fields then - logs.report("fields","using child choice '%s'",name) + report_fields("using child choice '%s'",name) end local d = pdfdictionary { Subtype = pdf_widget, - Parent = pdfreference(field.pobj), + Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), } - save_kid(field,specification,d) + 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,variant) - methods.choice(name,specification,variant,"PopUp") +function methods.popup(name,specification) + return makechoicechild(name,enhance(specification,"PopUp")) end -function methods.combo(name,specification,variant) - methods.choice(name,specification,variant,"PopUp,Edit") + +function methods.combo(name,specification) + return makechoicechild(name,enhance(specification,"PopUp,Edit")) end --- Probably no default appearance needed for first kid and no javascripts for the --- parent ... I will look into it when I have to make a complex document. +local function makecheckparent(field,specification) + local d = pdfdictionary { + T = pdfunicode(specification.title), + 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 -function methods.check(name,specification,variant) - -- no /Opt because (1) it's messy - see pdf spec, (2) it discouples kids and - -- contrary to radio there is no way to associate then - local field = fields[name] - if variant == "copy" or variant == "clone" then - logs.report("fields","todo: clones of check fields") - end - local kind = field.kind - local appearance, default = fieldstates(field,true) - if not field.pobj then - if trace_fields then - logs.report("fields","using parent check '%s'",name) +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 '%s'",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 '%s'",name) + end + makecheckparent(parent,specification,extras) end - 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, - DV = default, - V = default, - AS = default, - AP = appearance, - H = pdf_n, - } - save_parent(field,specification,d) - field.specification = specification end - specification = field.specification or { } -- todo: radio spec if trace_fields then - logs.report("fields","using child check '%s'",name) + report_fields("using child check '%s'",name) end local d = pdfdictionary { Subtype = pdf_widget, - Parent = pdfreference(field.pobj), + Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), - DV = default, - V = default, - AS = default, - AP = appearance, H = pdf_n, } - save_kid(field,specification,d) + 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(field,true) + d.AS = default + d.AP = appearance + return save_kid(parent,specification,d,value) + end end -function methods.push(name,specification,variant) - local field = fields[name] - if variant == "copy" or variant == "clone" then - logs.report("fields","todo: clones of push fields") - end - local kind = field.kind - if not field.pobj then - if trace_fields then - logs.report("fields","using parent push '%s'",name) +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 '%s'",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 '%s'",name) + end + makepushparent(parent,specification) end - enhance(specification,"PushButton") - 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) - field.specification = specification end - specification = field.specification or { } -- todo: radio spec if trace_fields then - logs.report("fields","using child push '%s'",name) + report_fields("using child push '%s'",name) end + local fontsymbol = specification.fontsymbol local d = pdfdictionary { Subtype = pdf_widget, Parent = pdfreference(field.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), - AP = fieldappearances(field), H = pdf_p, } - save_kid(field,specification,d) + 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") + 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 -function methods.sub(name,specification,variant) - local field = radios[name] or fields[name] or clones[name] -- fields in case of a clone, maybe use dedicated clones - local values - if variant == "copy" or variant == "clone" then - name = field.parent - values = field.values -- clone only, copy has nil so same as parent +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 '%s'",parent.name) + end + makeradioparent(parent,parent) + end field = radios[name] else - values = field.values - end - local parent = fields[field.parent] - if not parent then - return - end - local appearance = fieldstates(field,name,values) -- we need to force the 'On' name - local default = radiodefault(parent,field) - if not parent.pobj then - if trace_fields then - logs.report("fields","using parent '%s' of radio '%s' with values '%s' and default '%s'",parent.name,name,parent.values or "?",parent.default or "?") + field = radios[name] + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("using parent radio '%s'",name) + end + makeradioparent(parent,parent) end - local specification = parent.specification or { } - -- enhance(specification,"Radio,RadiosInUnison") - enhance(specification,"RadiosInUnison") -- maybe also PushButton as acrobat does - local d = pdfdictionary { - T = parent.name, - FT = pdf_btn, - Rect = pdf_no_rect, - F = fieldplus(specification), - Ff = fieldflag(specification), - H = pdf_n, - V = default, - } - save_parent(parent,specification,d) end if trace_fields then - logs.report("fields","using child radio '%s' with values '%s'",name,values or "?") + report_fields("using child radio '%s' with values '%s' and default '%s'",name,field.values or "?",field.default or "?") end + local fontsymbol = specification.fontsymbol local d = pdfdictionary { Subtype = pdf_widget, Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), - AS = default, - AP = appearance, H = pdf_n, } - save_kid(parent,specification,d) + 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(field,true) -- false is also ok + d.AS = default -- needed ? + d.AP = appearance + return save_kid(parent,specification,d,value) + end +end + +function methods.sub(name,specification) + return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison")) end |