diff options
Diffstat (limited to 'tex/context/base/lpdf-wid.lua')
-rw-r--r-- | tex/context/base/lpdf-wid.lua | 472 |
1 files changed, 301 insertions, 171 deletions
diff --git a/tex/context/base/lpdf-wid.lua b/tex/context/base/lpdf-wid.lua index d0e441c26..48beacfd8 100644 --- a/tex/context/base/lpdf-wid.lua +++ b/tex/context/base/lpdf-wid.lua @@ -6,112 +6,152 @@ if not modules then modules = { } end modules ['lpdf-wid'] = { license = "see context related readme files" } -local gmatch, gsub, find = string.gmatch, string.gsub, string.find +local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format local texbox, texcount = tex.box, tex.count local settings_to_array = utilities.parsers.settings_to_array local settings_to_hash = utilities.parsers.settings_to_hash -local report_media = logs.reporter("backend","media") +local report_media = logs.reporter("backend","media") +local report_attachment = logs.reporter("backend","attachment") local backends, lpdf, nodes = backends, lpdf, nodes -local nodeinjections = backends.pdf.nodeinjections -local codeinjections = backends.pdf.codeinjections -local registrations = backends.pdf.registrations +local nodeinjections = backends.pdf.nodeinjections +local codeinjections = backends.pdf.codeinjections +local registrations = backends.pdf.registrations -local executers = structures.references.executers -local variables = interfaces.variables +local executers = structures.references.executers +local variables = interfaces.variables -local pdfconstant = lpdf.constant -local pdfdictionary = lpdf.dictionary -local pdfarray = lpdf.array -local pdfreference = lpdf.reference -local pdfunicode = lpdf.unicode -local pdfstring = lpdf.string -local pdfcolorspec = lpdf.colorspec -local pdfflushobject = lpdf.flushobject -local pdfreserveannotation = lpdf.reserveannotation -local pdfreserveobject = lpdf.reserveobject -local pdfimmediateobject = lpdf.immediateobject -local pdfpagereference = lpdf.pagereference +local v_hidden = variables.hidden +local v_normal = variables.normal +local v_auto = variables.auto +local v_embed = variables.embed +local v_unknown = variables.unknown -local nodepool = nodes.pool +local pdfconstant = lpdf.constant +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfreference = lpdf.reference +local pdfunicode = lpdf.unicode +local pdfstring = lpdf.string +local pdfcolorspec = lpdf.colorspec +local pdfflushobject = lpdf.flushobject +local pdfreserveannotation = lpdf.reserveannotation +local pdfreserveobject = lpdf.reserveobject +local pdfimmediateobject = lpdf.immediateobject +local pdfpagereference = lpdf.pagereference +local pdfshareobjectreference = lpdf.shareobjectreference -local pdfannotation_node = nodepool.pdfannotation +local nodepool = nodes.pool -local hpack_node, write_node = node.hpack, node.write +local pdfannotation_node = nodepool.pdfannotation -local pdf_border = pdfarray { 0, 0, 0 } -- can be shared +local hpack_node = node.hpack +local write_node = node.write + +local pdf_border = pdfarray { 0, 0, 0 } -- can be shared -- symbols local presets = { } -- xforms -function codeinjections.registersymbol(name,n) +local function registersymbol(name,n) presets[name] = pdfreference(n) end -function codeinjections.registeredsymbol(name) +local function registeredsymbol(name) return presets[name] end -function codeinjections.presetsymbol(symbol) +local function presetsymbol(symbol) if not presets[symbol] then context.predefinesymbol { symbol } end end -function codeinjections.presetsymbollist(list) +local function presetsymbollist(list) if list then for symbol in gmatch(list,"[^, ]+") do - codeinjections.presetsymbol(symbol) + presetsymbol(symbol) end end end +codeinjections.registersymbol = registersymbol +codeinjections.registeredsymbol = registeredsymbol +codeinjections.presetsymbol = presetsymbol +codeinjections.presetsymbollist = presetsymbollist + -- comments -local symbols = { - New = pdfconstant("Insert"), - Insert = pdfconstant("Insert"), - Balloon = pdfconstant("Comment"), +-- local symbols = { +-- Addition = pdfconstant("NewParagraph"), +-- Attachment = pdfconstant("Attachment"), +-- Balloon = pdfconstant("Comment"), +-- Check = pdfconstant("Check Mark"), +-- CheckMark = pdfconstant("Check Mark"), +-- Circle = pdfconstant("Circle"), +-- Cross = pdfconstant("Cross"), +-- CrossHairs = pdfconstant("Cross Hairs"), +-- Graph = pdfconstant("Graph"), +-- InsertText = pdfconstant("Insert Text"), +-- New = pdfconstant("Insert"), +-- Paperclip = pdfconstant("Paperclip"), +-- RightArrow = pdfconstant("Right Arrow"), +-- RightPointer = pdfconstant("Right Pointer"), +-- Star = pdfconstant("Star"), +-- Tag = pdfconstant("Tag"), +-- Text = pdfconstant("Note"), +-- TextNote = pdfconstant("Text Note"), +-- UpArrow = pdfconstant("Up Arrow"), +-- UpLeftArrow = pdfconstant("Up-Left Arrow"), +-- } + +local attachment_symbols = { + Graph = pdfconstant("GraphPushPin"), + Paperclip = pdfconstant("PaperclipTag"), + Pushpin = pdfconstant("PushPin"), +} + +attachment_symbols.PushPin = attachment_symbols.Pushpin +attachment_symbols.Default = attachment_symbols.Pushpin + +local comment_symbols = { Comment = pdfconstant("Comment"), - Text = pdfconstant("Note"), - Addition = pdfconstant("NewParagraph"), - NewParagraph = pdfconstant("NewParagraph"), Help = pdfconstant("Help"), - Paragraph = pdfconstant("Paragraph"), + Insert = pdfconstant("Insert"), Key = pdfconstant("Key"), - Graph = pdfconstant("Graph"), - Paperclip = pdfconstant("Paperclip"), - Attachment = pdfconstant("Attachment"), - Tag = pdfconstant("Tag"), + Newparagraph = pdfconstant("NewParagraph"), + Note = pdfconstant("Note"), + Paragraph = pdfconstant("Paragraph"), } -symbols[variables.normal] = pdfconstant("Note") +comment_symbols.NewParagraph = Newparagraph +comment_symbols.Default = Note -local nofcomments, usepopupcomments, stripleading = 0, true, true - -local function analyzesymbol(symbol) +local function analyzesymbol(symbol,collection) if not symbol or symbol == "" then - return symbols.normal, nil - elseif symbols[symbol] then - return symbols[symbol], nil + return collection.Default, nil + elseif collection[symbol] then + return collection[symbol], nil else + local setn, setr, setd local set = settings_to_array(symbol) - local normal, down = set[1], set[2] - if normal then - normal = codeinjections.registeredsymbol(down or normal) - end - if down then - down = codeinjections.registeredsymbol(normal) - end - if down or normal then - return nil, pdfdictionary { - N = normal, - D = down, - } + if #set == 1 then + setn, setr, setd = set[1], set[1], set[1] + elseif #set == 2 then + setn, setr, setd = set[1], set[1], set[2] + else + setn, setr, setd = set[1], set[2], set[3] end + local appearance = pdfdictionary { + N = setn and registeredsymbol(setn), + R = setr and registeredsymbol(setr), + D = setd and registeredsymbol(setd), + } + local appearanceref = pdfshareobjectreference(appearance) + return nil, appearanceref end end @@ -119,67 +159,34 @@ local function analyzelayer(layer) -- todo: (specification.layer ~= "" and pdfreference(specification.layer)) or nil, -- todo: ref to layer end -function codeinjections.registercomment(specification) - nofcomments = nofcomments + 1 - local text = buffers.collectcontent(specification.buffer) - text = string.strip(text) - if stripleading then -- maybe just strip all leading and trailing spacing - text = gsub(text,"[\n\r] *","\n") - end - local name, appearance = analyzesymbol(specification.symbol) - local d = pdfdictionary { - Subtype = pdfconstant("Text"), - Open = specification.open, - Contents = pdfunicode(text), - T = (specification.title ~= "" and pdfunicode(specification.title)) or nil, - C = pdfcolorspec(specification.colormodel,specification.colorvalue), - OC = analyzelayer(specification.layer), - Name = name, - AP = appearance, - } - -- - -- watch the nice feed back to tex hack - -- - -- we can consider replacing nodes by user nodes that do a latelua - -- so that we get rid of all annotation whatsits - if usepopupcomments then - local nd = pdfreserveannotation() - local nc = pdfreserveannotation() - local c = pdfdictionary { - Subtype = pdfconstant("Popup"), - Parent = pdfreference(nd), - } - d.Popup = pdfreference(nc) - texbox["commentboxone"] = hpack_node(pdfannotation_node(0,0,0,d(),nd)) -- current dir - texbox["commentboxtwo"] = hpack_node(pdfannotation_node(specification.width,specification.height,0,c(),nc)) -- current dir - else - texbox["commentboxone"] = hpack_node(pdfannotation_node(0,0,0,d())) -- current dir - texbox["commentboxtwo"] = nil - end +local function analyzecolor(colorvalue,colormodel) + local cvalue = colorvalue and tonumber(colorvalue) + local cmodel = colormodel and tonumber(colormodel) or 3 + return cvalue and pdfarray { lpdf.colorvalues(cmodel,cvalue) } or nil end --- +local function analyzetransparency(transparencyvalue) + local tvalue = transparencyvalue and tonumber(transparencyvalue) + return tvalue and lpdf.transparencyvalue(tvalue) or nil +end -local nofattachments, attachments, filestreams, referenced = 0, { }, { }, { } +-- Attachments --- todo: hash and embed once +local nofattachments, attachments, filestreams, referenced = 0, { }, { }, { } local ignorereferenced = true -- fuzzy pdf spec .. twice in attachment list, can become an option local function flushembeddedfiles() if next(filestreams) then local e = pdfarray() - for name, reference in next, filestreams do - if reference then - if ignorereferenced and referenced[name] then - reference = nil - end - if reference then - e[#e+1] = pdfstring(name) - e[#e+1] = reference -- already a reference - end + for tag, reference in next, filestreams do + if not reference then + report_attachment("unreferenced file: tag '%s'",tag) + elseif referenced[name] == "hidden" then + e[#e+1] = pdfstring(tag) + e[#e+1] = reference -- already a reference else - -- we can issue a message + -- messy spec ... when annot not in named else twice in menu list acrobat end end lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e }))) @@ -188,78 +195,201 @@ end lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles") -function codeinjections.embedfile(filename) - local r = filestreams[filename] - if r == false then - return nil - elseif r then - return r - elseif not lfs.isfile(filename) then - interfaces.showmessage("interactions",5,filename) - filestreams[filename] = false - return nil +function codeinjections.embedfile(specification) + local data = specification.data + local filename = specification.file + local name = specification.name or "" + local title = specification.title or "" + local hash = specification.hash or filename + if filename == "" then + filename = nil + end + if data then + local r = filestreams[hash] + if r == false then + return nil + elseif r then + return r + elseif not filename then + filename = specification.tag + if not filename or filename == "" then + filename = specification.registered + end + if not filename or filename == "" then + filename = hash + end + end else - local basename = file.basename(filename) - local a = pdfdictionary { Type = pdfconstant("EmbeddedFile") } - local f = pdfimmediateobject("streamfile",filename,a()) - local d = pdfdictionary { - Type = pdfconstant("Filespec"), - F = pdfstring(newname or basename), - UF = pdfstring(newname or basename), - EF = pdfdictionary { F = pdfreference(f) }, - } - local r = pdfreference(pdfflushobject(d)) - filestreams[filename] = r - return r + if not filename then + return nil + end + local r = filestreams[hash] + if r == false then + return nil + elseif r then + return r + elseif not lfs.isfile(filename) then + filestreams[filename] = false + return nil + end end + local basename = file.basename(filename) + local savename = file.addsuffix(name ~= "" and name or basename,"txt") -- else no valid file + local a = pdfdictionary { Type = pdfconstant("EmbeddedFile") } + local f + if data then + f = pdfimmediateobject("stream",data,a()) + specification.data = true -- signal that still data but already flushed + else + f = pdfimmediateobject("streamfile",filename,a()) + end + local d = pdfdictionary { + Type = pdfconstant("Filespec"), + F = pdfstring(savename), + UF = pdfstring(savename), + EF = pdfdictionary { F = pdfreference(f) }, + Desc = title ~= "" and pdfunicode(title) or nil, + } + local r = pdfreference(pdfflushobject(d)) + filestreams[hash] = r + return r end -function codeinjections.attachfile(specification) - local attachment = interactions.attachments.attachment(specification.label) - if not attachment then - -- todo: message - return - end - local filename = attachment.filename - if not filename or filename == "" then - -- todo: message - return +function nodeinjections.attachfile(specification) + local registered = specification.registered or "<unset>" + local data = specification.data + local hash + if data then + hash = md5.HEX(data) + else + local filename = specification.file + if not filename or filename == "" then + report_attachment("missing file specification: registered '%s', using registered instead",registered) + filename = registered + specification.file = registered + end + if not lfs.isfile(filename) then + report_attachment("invalid file specification: registered '%s', filename '%s'",registered,filename) + return + end + hash = filename end - referenced[filename] = true + specification.hash = hash nofattachments = nofattachments + 1 - local label = attachment.label or "" - local title = attachment.title or "" - local newname = attachment.newname or "" - if label == "" then label = filename end - if title == "" then title = label end - if newname == "" then newname = filename end - local aref = attachments[label] + local registered = specification.registered or "" + local title = specification.title or "" + local subtitle = specification.subtitle or "" + local author = specification.author or "" + if registered == "" then + registered = filename + end + if author == "" then + author = title + title = "" + end + if author == "" then + author = v_unknown + end + if title == "" then + title = registered + end + local aref = attachments[registered] if not aref then - aref = codeinjections.embedfile(filename,newname) - attachments[label] = aref + aref = codeinjections.embedfile(specification) + attachments[registered] = aref + end + if not aref then + report_attachment("skipping: registered '%s'",registered) + -- already reported + elseif specification.method == v_hidden then + referenced[hash] = "hidden" + else + referenced[hash] = "annotation" + local name, appearance = analyzesymbol(specification.symbol,attachment_symbols) + local d = pdfdictionary { + Subtype = pdfconstant("FileAttachment"), + FS = aref, + Contents = pdfunicode(title), + Name = name, + NM = pdfstring(format("attachment:%s",nofattachments)), + T = author ~= "" and pdfunicode(author) or nil, + Subj = subtitle ~= "" and pdfunicode(subtitle) or nil, + C = analyzecolor(specification.colorvalue,specification.colormodel), + CA = analyzetransparency(specification.transparencyvalue), + AP = appearance, + OC = analyzelayer(specification.layer), + } + local width, height, depth = specification.width or 0, specification.height or 0, specification.depth + local box = hpack_node(pdfannotation_node(width,height,depth,d())) + box.width, box.height, box.depth = width, height, depth + return box end - local name, appearance = analyzesymbol(specification.symbol) - local d = pdfdictionary { - Subtype = pdfconstant("FileAttachment"), - FS = aref, - Contents = pdfunicode(title), - Name = name, - AP = appearance, - OC = analyzelayer(specification.layer), - C = pdfcolorspec(specification.colormodel,specification.colorvalue), - } - -- as soon as we can ask for the dimensions of an xform we can - -- use them here - local width = specification.width or 0 - local height = specification.height or 0 - local depth = specification.depth or 0 - write_node(pdfannotation_node(width,height,depth,d())) -- somehow the dimensions come out wrong end -function codeinjections.attachmentid(filename) +function codeinjections.attachmentid(filename) -- not used in context return filestreams[filename] end +local nofcomments, usepopupcomments, stripleading = 0, false, true + +function nodeinjections.comment(specification) + nofcomments = nofcomments + 1 + local text = string.strip(specification.data or "") + if stripleading then + text = gsub(text,"[\n\r] *","\n") + end + local name, appearance = analyzesymbol(specification.symbol,comment_symbols) + local tag = specification.tag or "" -- this is somewhat messy as recent + local title = specification.title or "" -- versions of acrobat see the title + local subtitle = specification.subtitle or "" -- as author + local author = specification.author or "" + if author == "" then + if title == "" then + title = tag + end + else + if subtitle == "" then + subtitle = title + elseif title ~= "" then + subtitle = subtitle .. ", " .. title + end + title = author + end + local d = pdfdictionary { + Subtype = pdfconstant("Text"), + -- Open = specification.open, -- now options + Contents = pdfunicode(text), + T = title ~= "" and pdfunicode(title) or nil, + Subj = subtitle ~= "" and pdfunicode(subtitle) or nil, + C = analyzecolor(specification.colorvalue,specification.colormodel), + CA = analyzetransparency(specification.transparencyvalue), + OC = analyzelayer(specification.layer), + Name = name, + NM = pdfstring(format("comment:%s",nofcomments)), + AP = appearance, + } + local width, height, depth = specification.width or 0, specification.height or 0, specification.depth + local box + if usepopupcomments then + -- rather useless as we can hide/vide + local nd = pdfreserveannotation() + local nc = pdfreserveannotation() + local c = pdfdictionary { + Subtype = pdfconstant("Popup"), + Parent = pdfreference(nd), + } + d.Popup = pdfreference(nc) + box = hpack_node( + pdfannotation_node(0,0,0,d(),nd), + pdfannotation_node(width,height,depth,c(),nc) + ) + else + box = hpack_node(pdfannotation_node(width,height,depth,d())) + end + box.width, box.height, box.depth = width, height, depth -- redundant + return box +end + -- rendering stuff -- -- object_1 -> <</Type /Rendition /S /MR /C << /Type /MediaClip ... >> >> @@ -308,7 +438,7 @@ local function insertrenderingwindow(specification) local label = specification.label --~ local openpage = specification.openpage --~ local closepage = specification.closepage - if specification.options == variables.auto then + if specification.options == v_auto then if openpageaction then -- \handlereferenceactions{\v!StartRendering{#2}} end @@ -384,7 +514,7 @@ local function insertrendering(specification) } if isurl then descriptor.FS = pdfconstant("URL") - elseif options[variables.embed] then + elseif options[v_embed] then descriptor.EF = codeinjections.embedfile(filename) end local clip = pdfdictionary { @@ -430,7 +560,7 @@ function codeinjections.processrendering(label) local specification = interactions.renderings.rendering(label) if not specification then -- error - elseif specification.kind == "external" then + elseif specification.type == "external" then insertrendering(specification) else insertrenderingobject(specification) |