From 85b7bc695629926641c7cb752fd478adfdf374f3 Mon Sep 17 00:00:00 2001 From: Marius Date: Sun, 4 Jul 2010 15:32:09 +0300 Subject: stable 2010-05-24 13:10 --- tex/context/base/lpdf-fld.lua | 885 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 885 insertions(+) create mode 100644 tex/context/base/lpdf-fld.lua (limited to 'tex/context/base/lpdf-fld.lua') diff --git a/tex/context/base/lpdf-fld.lua b/tex/context/base/lpdf-fld.lua new file mode 100644 index 000000000..c034aec6c --- /dev/null +++ b/tex/context/base/lpdf-fld.lua @@ -0,0 +1,885 @@ +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" +} + +-- 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) + +local gmatch, lower, format = string.gmatch, string.lower, string.format +local lpegmatch = lpeg.match + +local trace_fields = false trackers.register("widgets.fields", function(v) trace_fields = v end) + +local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes + +local variables = interfaces.variables + +local nodeinjections = backends.pdf.nodeinjections +local codeinjections = backends.pdf.codeinjections +local registrations = backends.pdf.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 pdfreserveobject = lpdf.reserveobject +local pdfannotation = nodes.pdfannotation + +local submitoutputformat = 0 -- 0=unknown 1=HTML 2=FDF 3=XML => not yet used, needs to be checked + +local splitter = lpeg.splitat("=>") + +local formats = { + html = 1, fdf = 2, xml = 3, +} + +function codeinjections.setformsmethod(name) + submitoutputformat = formats[lower(name)] or 3 +end + +local flag = { + MultiLine = 4096, -- 13 + NoToggleToOff = 16384, -- 15 + Radio = 32768, -- 16 + PushButton = 65536, -- 17 + PopUp = 131072, -- 18 + Edit = 262144, -- 19 + RadiosInUnison = 33554432, -- 26 + DoNotSpellCheck = 4194304, -- 23 + DoNotScroll = 8388608, -- 24 + ReadOnly = 1, -- 1 + Required = 2, -- 2 + NoExport = 4, -- 3 + Password = 8192, -- 14 + Sort = 524288, -- 20 + FileSelect = 1048576, -- 21 +} + +local plus = { + Invisible = 1, -- 1 + Hidden = 2, -- 2 + Printable = 4, -- 3 + NoView = 32, -- 6 + ToggleNoView = 256, -- 9 + 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) + local o, n = specification.options, 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) + local o, n = specification.options, 0 + if o and o ~= "" then + for p in gmatch(o,"[^, ]+") do + n = n + (plus[p] or 0) + end + end + 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) + 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.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.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 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", + } +} + +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] + if not s then + style, s = "rm", fontnames.rm + end + local a = s[alternative] + if not a then + alternative, a = "tf", s.tf + end + local tag = style .. alternative + size = string.todimen(size) + local stream = pdfstream { + pdfconstant(tag), + format("%0.4f Tf",(size and (number.dimenfactors.bp * size)) or 12), + } + 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 + local d = pdfdictionary() + for tag, name in next, usedfonts do + local f = pdfdictionary { + Type = pdfconstant("Font"), + Subtype = pdfconstant("Type1"), -- todo + Name = pdfconstant(tag), + BaseFont = pdfconstant(name), + } + d[tag] = pdfreference(pdfflushobject(f)) + end + return d + end +end + +-- cache + +local function fieldattributes(specification) +--~ return pdfarray { +--~ -- BG = -- backgroundcolor +--~ -- BC = -- framecolor +--~ } + return nil +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 = aux.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 { -- cache this one + N = registeredsymbol(n), R = registeredsymbol(r), D = registeredsymbol(d), + } + return lpdf.sharedobj(tostring(appearance)) +end + +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 + -- error + return + end + local v = aux.settings_to_array(values) + local yes, off + 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 = aux.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) + 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 = yesn + end + if not offvalue then + offvalue = offn + end + if forceyes == true then + forceyes = forceyes and "On" -- 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") + 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 +end + +local function fieldoptions(specification) + local values = specification.values + local default = specification.default + if values then + local v = aux.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 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] + 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 +end + +-- layers + +local function fieldlayer(specification) -- we can move this in line + local layer = specification.layer + return (layer and lpdf.layerreferences[layer]) or nil +end + +-- defining + +local fields, radios, clones, fieldsets, calculationset = { }, { }, { }, { }, nil + +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 = aux.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 = aux.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 + end +end + +function codeinjections.definefield(specification) + local n = specification.name + local f = fields[n] + if not f then + local kind = specification.kind + if not kind then + if trace_fields then + logs.report("fields","invalid definition of '%s': unknown type",n) + end + elseif kind == "radio" then + local values = specification.values + if values and values ~= "" then + values = aux.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 "?") + end + elseif trace_fields then + logs.report("fields","invalid definition of radio '%s': missing values",n) + end + elseif kind == "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 + logs.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) + end + predefinesymbols(specification) + elseif kind == "text" or kind == "line" then + fields[n] = specification + if trace_fields then + logs.report("fields","defining '%s' as %s",n,kind) + end + if specification.values ~= "" and specification.default == "" then + specification.default, specification.values = specification.values, nil + end + else + fields[n] = specification + if trace_fields then + logs.report("fields","defining '%s' as %s",n,kind) + end + predefinesymbols(specification) + end + elseif trace_fields then + logs.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 + 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 "?") + 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) + end + end + end +end + +function codeinjections.getfieldgroup(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 + end + end + if g then + texsprint(ctxcatcodes,g) + end +end + +-- + +function codeinjections.doiffieldset(tag) + commands.testcase(fieldsets[tag]) +end + +function codeinjections.doiffieldelse(name) + commands.testcase(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.options + if so and so ~= "" then + specification.options = so .. "," .. option + else + specification.options = option + end + return specification +end + +-- finish + +local collected = pdfarray() + +local function finishfields() + for name, field in next, fields do + local kids = field.kids + if kids then + pdfflushobject(field.kobj,kids) + end + local pobj = field.pobj + end + for name, field in next, radios do + local kids = field.kids + if kids then + pdfflushobject(field.kobj,kids) + 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", + } + 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 } + +local methods = { } + +function codeinjections.typesetfield(name,specification) + local field = fields[name] or radios[name] or clones[name] + if not field then + logs.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 + field = fields[parent] or radios[parent] + end + local method = methods[field.kind] + if method then + method(name,specification,variant) + else + logs.report("fields", "unknown method '%s' for child '%s'",field.kind,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 + field.kids = pdfarray() + local pn = pdfflushobject(d) + field.pobj = pn + collected[#collected+1] = pdfreference(pn) +end + +local function save_kid(field,specification,d) + local kn = pdfreserveobject() + field.kids[#field.kids+1] = pdfreference(kn) + node.write(pdfannotation(specification.width,specification.height,0,d(),kn)) +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) + end + if extras then + enhance(specification,extras) + 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) + end + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(field.pobj), + F = fieldplus(specification), + DA = fieldattributes(specification), + OC = fieldlayer(specification), + MK = fieldsurrounding(specification), + DA = fieldsurrounding(specification), + AA = fieldactions(specification), + Q = fieldalignment(specification), + } + save_kid(field,specification,d) +end + +function methods.text(name,specification,variant) + methods.line(name,specification,variant,"MultiLine") +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) + end + if extras then + enhance(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) + end + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(field.pobj), + F = fieldplus(specification), + DA = fieldattributes(specification), + OC = fieldlayer(specification), + AA = fieldactions(specification), + } + save_kid(field,specification,d) +end + +function methods.popup(name,specification,variant) + methods.choice(name,specification,variant,"PopUp") +end +function methods.combo(name,specification,variant) + methods.choice(name,specification,variant,"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. + +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) + 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) + end + local d = pdfdictionary { + Subtype = pdf_widget, + Parent = pdfreference(field.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) +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) + 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) + end + 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) +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 + 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 "?") + 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 "?") + end + 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) +end -- cgit v1.2.3