summaryrefslogtreecommitdiff
path: root/tex/context/base/lpdf-wid.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/lpdf-wid.lua')
-rw-r--r--tex/context/base/lpdf-wid.lua1290
1 files changed, 645 insertions, 645 deletions
diff --git a/tex/context/base/lpdf-wid.lua b/tex/context/base/lpdf-wid.lua
index 9ea4744f1..20fc14679 100644
--- a/tex/context/base/lpdf-wid.lua
+++ b/tex/context/base/lpdf-wid.lua
@@ -1,645 +1,645 @@
-if not modules then modules = { } end modules ['lpdf-wid'] = {
- 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"
-}
-
-local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format
-local stripstring = string.strip
-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_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 executers = structures.references.executers
-local variables = interfaces.variables
-
-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 v_max = variables.max
-
-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 pdfboolean = lpdf.boolean
-local pdfcolorspec = lpdf.colorspec
-local pdfflushobject = lpdf.flushobject
-local pdfflushstreamobject = lpdf.flushstreamobject
-local pdfflushstreamfileobject = lpdf.flushstreamfileobject
-local pdfreserveannotation = lpdf.reserveannotation
-local pdfreserveobject = lpdf.reserveobject
-local pdfpagereference = lpdf.pagereference
-local pdfshareobjectreference = lpdf.shareobjectreference
-
-local nodepool = nodes.pool
-
-local pdfannotation_node = nodepool.pdfannotation
-
-local hpack_node = node.hpack
-local write_node = node.write -- test context(...) instead
-
-local pdf_border = pdfarray { 0, 0, 0 } -- can be shared
-
--- symbols
-
-local presets = { } -- xforms
-
-local function registersymbol(name,n)
- presets[name] = pdfreference(n)
-end
-
-local function registeredsymbol(name)
- return presets[name]
-end
-
-local function presetsymbol(symbol)
- if not presets[symbol] then
- context.predefinesymbol { symbol }
- end
-end
-
-local function presetsymbollist(list)
- if list then
- for symbol in gmatch(list,"[^, ]+") do
- presetsymbol(symbol)
- end
- end
-end
-
-codeinjections.registersymbol = registersymbol
-codeinjections.registeredsymbol = registeredsymbol
-codeinjections.presetsymbol = presetsymbol
-codeinjections.presetsymbollist = presetsymbollist
-
--- comments
-
--- 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"),
- Help = pdfconstant("Help"),
- Insert = pdfconstant("Insert"),
- Key = pdfconstant("Key"),
- Newparagraph = pdfconstant("NewParagraph"),
- Note = pdfconstant("Note"),
- Paragraph = pdfconstant("Paragraph"),
-}
-
-comment_symbols.NewParagraph = Newparagraph
-comment_symbols.Default = Note
-
-local function analyzesymbol(symbol,collection)
- if not symbol or symbol == "" then
- return collection.Default, nil
- elseif collection[symbol] then
- return collection[symbol], nil
- else
- local setn, setr, setd
- local set = settings_to_array(symbol)
- 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
-
-local function analyzelayer(layer)
- -- todo: (specification.layer ~= "" and pdfreference(specification.layer)) or nil, -- todo: ref to layer
-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
-
--- Attachments
-
-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 tag, reference in next, filestreams do
- if not reference then
- report_attachment("unreferenced file, tag %a",tag)
- elseif referenced[tag] == "hidden" then
- e[#e+1] = pdfstring(tag)
- e[#e+1] = reference -- already a reference
- else
- -- messy spec ... when annot not in named else twice in menu list acrobat
- end
- end
- lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e })))
- end
-end
-
-lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles")
-
-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
- local keepdir = specification.keepdir -- can change
- local usedname = specification.usedname
- 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
- if not filename then
- return nil
- end
- local r = filestreams[hash]
- if r == false then
- return nil
- elseif r then
- return r
- else
- local foundname = resolvers.findbinfile(filename) or ""
- if foundname == "" or not lfs.isfile(foundname) then
- filestreams[filename] = false
- return nil
- else
- specification.foundname = foundname
- end
- end
- end
- usedname = usedname ~= "" and usedname or filename
- local basename = keepdir == true and usedname or file.basename(usedname)
-local basename = gsub(basename,"%./","")
- 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 = pdfflushstreamobject(data,a)
- specification.data = true -- signal that still data but already flushed
- else
- local foundname = specification.foundname or filename
- f = pdfflushstreamfileobject(foundname,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 nodeinjections.attachfile(specification)
- local registered = specification.registered or "<unset>"
- local data = specification.data
- local hash
- local filename
- if data then
- hash = md5.HEX(data)
- else
- filename = specification.file
- if not filename or filename == "" then
- report_attachment("no file specified, using registered %a instead",registered)
- filename = registered
- specification.file = registered
- end
- local foundname = resolvers.findbinfile(filename) or ""
- if foundname == "" or not lfs.isfile(foundname) then
- report_attachment("invalid filename %a, ignoring registered %a",filename,registered)
- return nil
- else
- specification.foundname = foundname
- end
- hash = filename
- end
- specification.hash = hash
- nofattachments = nofattachments + 1
- 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 = filename or "<unknown>"
- end
- if title == "" then
- title = registered
- end
- local aref = attachments[registered]
- if not aref then
- aref = codeinjections.embedfile(specification)
- attachments[registered] = aref
- end
- if not aref then
- report_attachment("skipping attachment, registered %a",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
-end
-
-function codeinjections.attachmentid(filename) -- not used in context
- return filestreams[filename]
-end
-
-local nofcomments, usepopupcomments, stripleading = 0, false, true
-
-local defaultattributes = {
- ["xmlns"] = "http://www.w3.org/1999/xhtml",
- ["xmlns:xfa"] = "http://www.xfa.org/schema/xfa-data/1.0/",
- ["xfa:contentType"] = "text/html",
- ["xfa:APIVersion"] = "Acrobat:8.0.0",
- ["xfa:spec"] = "2.4",
-}
-
-local function checkcontent(text,option)
- if option and option.xml then
- local root = xml.convert(text)
- if root and not root.er then
- xml.checkbom(root)
- local body = xml.first(root,"/body")
- if body then
- local at = body.at
- for k, v in next, defaultattributes do
- if not at[k] then
- at[k] = v
- end
- end
- -- local content = xml.textonly(root)
- local richcontent = xml.tostring(root)
- return nil, pdfunicode(richcontent)
- end
- end
- end
- return pdfunicode(text)
-end
-
-function nodeinjections.comment(specification) -- brrr: seems to be done twice
- nofcomments = nofcomments + 1
- local text = stripstring(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 ""
- local option = settings_to_hash(specification.option 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 content, richcontent = checkcontent(text,option)
- local d = pdfdictionary {
- Subtype = pdfconstant("Text"),
- Open = option[v_max] and pdfboolean(true) or nil,
- Contents = content,
- RC = richcontent,
- 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 ... >> >>
--- object_2 -> <</Type /Rendition /S /MR /C << /Type /MediaClip ... >> >>
--- rendering -> <</Type /Rendition /S /MS [objref_1 objref_2]>>
---
--- we only work foreward here (currently)
--- annotation is to be packed at the tex end
-
--- aiff audio/aiff
--- au audio/basic
--- avi video/avi
--- mid audio/midi
--- mov video/quicktime
--- mp3 audio/x-mp3 (mpeg)
--- mp4 audio/mp4
--- mp4 video/mp4
--- mpeg video/mpeg
--- smil application/smil
--- swf application/x-shockwave-flash
-
--- P media play parameters (evt /BE for controls etc
--- A boolean (audio)
--- C boolean (captions)
--- O boolean (overdubs)
--- S boolean (subtitles)
--- PL pdfconstant("ADBE_MCI"),
-
--- F = flags,
--- T = title,
--- Contents = rubish,
--- AP = irrelevant,
-
--- sound is different, no window (or zero) so we need to collect them and
--- force them if not set
-
-local ms, mu, mf = { }, { }, { }
-
-local function delayed(label)
- local a = pdfreserveannotation()
- mu[label] = a
- return pdfreference(a)
-end
-
-local function insertrenderingwindow(specification)
- local label = specification.label
---~ local openpage = specification.openpage
---~ local closepage = specification.closepage
- if specification.option == v_auto then
- if openpageaction then
- -- \handlereferenceactions{\v!StartRendering{#2}}
- end
- if closepageaction then
- -- \handlereferenceactions{\v!StopRendering {#2}}
- end
- end
- local actions = nil
- if openpage or closepage then
- actions = pdfdictionary {
- PO = (openpage and lpdf.action(openpage )) or nil,
- PC = (closepage and lpdf.action(closepage)) or nil,
- }
- end
- local page = tonumber(specification.page) or texcount.realpageno -- todo
- local r = mu[label] or pdfreserveannotation() -- why the reserve here?
- local a = pdfdictionary {
- S = pdfconstant("Rendition"),
- R = mf[label],
- OP = 0,
- AN = pdfreference(r),
- }
- local d = pdfdictionary {
- Subtype = pdfconstant("Screen"),
- P = pdfreference(pdfpagereference(page)),
- A = a, -- needed in order to make the annotation clickable (i.e. don't bark)
- Border = pdf_border,
- AA = actions,
- }
- local width = specification.width or 0
- local height = specification.height or 0
- if height == 0 or width == 0 then
- -- todo: sound needs no window
- end
- write_node(pdfannotation_node(width,height,0,d(),r)) -- save ref
- return pdfreference(r)
-end
-
--- some dictionaries can have a MH (must honor) or BE (best effort) capsule
-
-local function insertrendering(specification)
- local label = specification.label
- local option = settings_to_hash(specification.option)
- if not mf[label] then
- local filename = specification.filename
- local isurl = find(filename,"://")
- --~ local start = pdfdictionary {
- --~ Type = pdfconstant("MediaOffset"),
- --~ S = pdfconstant("T"), -- time
- --~ T = pdfdictionary { -- time
- --~ Type = pdfconstant("Timespan"),
- --~ S = pdfconstant("S"),
- --~ V = 3, -- time in seconds
- --~ },
- --~ }
- --~ local start = pdfdictionary {
- --~ Type = pdfconstant("MediaOffset"),
- --~ S = pdfconstant("F"), -- frame
- --~ F = 100 -- framenumber
- --~ }
- --~ local start = pdfdictionary {
- --~ Type = pdfconstant("MediaOffset"),
- --~ S = pdfconstant("M"), -- mark
- --~ M = "somemark",
- --~ }
- --~ local parameters = pdfdictionary {
- --~ BE = pdfdictionary {
- --~ B = start,
- --~ }
- --~ }
- --~ local parameters = pdfdictionary {
- --~ Type = pdfconstant(MediaPermissions),
- --~ TF = pdfstring("TEMPALWAYS") }, -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS
- --~ }
- local descriptor = pdfdictionary {
- Type = pdfconstant("Filespec"),
- F = filename,
- }
- if isurl then
- descriptor.FS = pdfconstant("URL")
- elseif option[v_embed] then
- descriptor.EF = codeinjections.embedfile { file = filename }
- end
- local clip = pdfdictionary {
- Type = pdfconstant("MediaClip"),
- S = pdfconstant("MCD"),
- N = label,
- CT = specification.mime,
- Alt = pdfarray { "", "file not found" }, -- language id + message
- D = pdfreference(pdfflushobject(descriptor)),
- -- P = pdfreference(pdfflushobject(parameters)),
- }
- local rendition = pdfdictionary {
- Type = pdfconstant("Rendition"),
- S = pdfconstant("MR"),
- N = label,
- C = pdfreference(pdfflushobject(clip)),
- }
- mf[label] = pdfreference(pdfflushobject(rendition))
- end
-end
-
-local function insertrenderingobject(specification) -- todo
- local label = specification.label
- if not mf[label] then
- report_media("unknown medium, label %a",label)
- local clip = pdfdictionary { -- does not work that well one level up
- Type = pdfconstant("MediaClip"),
- S = pdfconstant("MCD"),
- N = label,
- D = pdfreference(unknown), -- not label but objectname, hm .. todo?
- }
- local rendition = pdfdictionary {
- Type = pdfconstant("Rendition"),
- S = pdfconstant("MR"),
- N = label,
- C = pdfreference(pdfflushobject(clip)),
- }
- mf[label] = pdfreference(pdfflushobject(rendition))
- end
-end
-
-function codeinjections.processrendering(label)
- local specification = interactions.renderings.rendering(label)
- if not specification then
- -- error
- elseif specification.type == "external" then
- insertrendering(specification)
- else
- insertrenderingobject(specification)
- end
-end
-
-function codeinjections.insertrenderingwindow(specification)
- local label = specification.label
- codeinjections.processrendering(label)
- ms[label] = insertrenderingwindow(specification)
-end
-
-local function set(operation,arguments)
- codeinjections.processrendering(arguments)
- return pdfdictionary {
- S = pdfconstant("Rendition"),
- OP = operation,
- R = mf[arguments],
- AN = ms[arguments] or delayed(arguments),
- }
-end
-
-function executers.startrendering (arguments) return set(0,arguments) end
-function executers.stoprendering (arguments) return set(1,arguments) end
-function executers.pauserendering (arguments) return set(2,arguments) end
-function executers.resumerendering(arguments) return set(3,arguments) end
+if not modules then modules = { } end modules ['lpdf-wid'] = {
+ 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"
+}
+
+local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format
+local stripstring = string.strip
+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_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 executers = structures.references.executers
+local variables = interfaces.variables
+
+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 v_max = variables.max
+
+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 pdfboolean = lpdf.boolean
+local pdfcolorspec = lpdf.colorspec
+local pdfflushobject = lpdf.flushobject
+local pdfflushstreamobject = lpdf.flushstreamobject
+local pdfflushstreamfileobject = lpdf.flushstreamfileobject
+local pdfreserveannotation = lpdf.reserveannotation
+local pdfreserveobject = lpdf.reserveobject
+local pdfpagereference = lpdf.pagereference
+local pdfshareobjectreference = lpdf.shareobjectreference
+
+local nodepool = nodes.pool
+
+local pdfannotation_node = nodepool.pdfannotation
+
+local hpack_node = node.hpack
+local write_node = node.write -- test context(...) instead
+
+local pdf_border = pdfarray { 0, 0, 0 } -- can be shared
+
+-- symbols
+
+local presets = { } -- xforms
+
+local function registersymbol(name,n)
+ presets[name] = pdfreference(n)
+end
+
+local function registeredsymbol(name)
+ return presets[name]
+end
+
+local function presetsymbol(symbol)
+ if not presets[symbol] then
+ context.predefinesymbol { symbol }
+ end
+end
+
+local function presetsymbollist(list)
+ if list then
+ for symbol in gmatch(list,"[^, ]+") do
+ presetsymbol(symbol)
+ end
+ end
+end
+
+codeinjections.registersymbol = registersymbol
+codeinjections.registeredsymbol = registeredsymbol
+codeinjections.presetsymbol = presetsymbol
+codeinjections.presetsymbollist = presetsymbollist
+
+-- comments
+
+-- 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"),
+ Help = pdfconstant("Help"),
+ Insert = pdfconstant("Insert"),
+ Key = pdfconstant("Key"),
+ Newparagraph = pdfconstant("NewParagraph"),
+ Note = pdfconstant("Note"),
+ Paragraph = pdfconstant("Paragraph"),
+}
+
+comment_symbols.NewParagraph = Newparagraph
+comment_symbols.Default = Note
+
+local function analyzesymbol(symbol,collection)
+ if not symbol or symbol == "" then
+ return collection.Default, nil
+ elseif collection[symbol] then
+ return collection[symbol], nil
+ else
+ local setn, setr, setd
+ local set = settings_to_array(symbol)
+ 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
+
+local function analyzelayer(layer)
+ -- todo: (specification.layer ~= "" and pdfreference(specification.layer)) or nil, -- todo: ref to layer
+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
+
+-- Attachments
+
+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 tag, reference in next, filestreams do
+ if not reference then
+ report_attachment("unreferenced file, tag %a",tag)
+ elseif referenced[tag] == "hidden" then
+ e[#e+1] = pdfstring(tag)
+ e[#e+1] = reference -- already a reference
+ else
+ -- messy spec ... when annot not in named else twice in menu list acrobat
+ end
+ end
+ lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e })))
+ end
+end
+
+lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles")
+
+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
+ local keepdir = specification.keepdir -- can change
+ local usedname = specification.usedname
+ 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
+ if not filename then
+ return nil
+ end
+ local r = filestreams[hash]
+ if r == false then
+ return nil
+ elseif r then
+ return r
+ else
+ local foundname = resolvers.findbinfile(filename) or ""
+ if foundname == "" or not lfs.isfile(foundname) then
+ filestreams[filename] = false
+ return nil
+ else
+ specification.foundname = foundname
+ end
+ end
+ end
+ usedname = usedname ~= "" and usedname or filename
+ local basename = keepdir == true and usedname or file.basename(usedname)
+local basename = gsub(basename,"%./","")
+ 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 = pdfflushstreamobject(data,a)
+ specification.data = true -- signal that still data but already flushed
+ else
+ local foundname = specification.foundname or filename
+ f = pdfflushstreamfileobject(foundname,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 nodeinjections.attachfile(specification)
+ local registered = specification.registered or "<unset>"
+ local data = specification.data
+ local hash
+ local filename
+ if data then
+ hash = md5.HEX(data)
+ else
+ filename = specification.file
+ if not filename or filename == "" then
+ report_attachment("no file specified, using registered %a instead",registered)
+ filename = registered
+ specification.file = registered
+ end
+ local foundname = resolvers.findbinfile(filename) or ""
+ if foundname == "" or not lfs.isfile(foundname) then
+ report_attachment("invalid filename %a, ignoring registered %a",filename,registered)
+ return nil
+ else
+ specification.foundname = foundname
+ end
+ hash = filename
+ end
+ specification.hash = hash
+ nofattachments = nofattachments + 1
+ 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 = filename or "<unknown>"
+ end
+ if title == "" then
+ title = registered
+ end
+ local aref = attachments[registered]
+ if not aref then
+ aref = codeinjections.embedfile(specification)
+ attachments[registered] = aref
+ end
+ if not aref then
+ report_attachment("skipping attachment, registered %a",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
+end
+
+function codeinjections.attachmentid(filename) -- not used in context
+ return filestreams[filename]
+end
+
+local nofcomments, usepopupcomments, stripleading = 0, false, true
+
+local defaultattributes = {
+ ["xmlns"] = "http://www.w3.org/1999/xhtml",
+ ["xmlns:xfa"] = "http://www.xfa.org/schema/xfa-data/1.0/",
+ ["xfa:contentType"] = "text/html",
+ ["xfa:APIVersion"] = "Acrobat:8.0.0",
+ ["xfa:spec"] = "2.4",
+}
+
+local function checkcontent(text,option)
+ if option and option.xml then
+ local root = xml.convert(text)
+ if root and not root.er then
+ xml.checkbom(root)
+ local body = xml.first(root,"/body")
+ if body then
+ local at = body.at
+ for k, v in next, defaultattributes do
+ if not at[k] then
+ at[k] = v
+ end
+ end
+ -- local content = xml.textonly(root)
+ local richcontent = xml.tostring(root)
+ return nil, pdfunicode(richcontent)
+ end
+ end
+ end
+ return pdfunicode(text)
+end
+
+function nodeinjections.comment(specification) -- brrr: seems to be done twice
+ nofcomments = nofcomments + 1
+ local text = stripstring(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 ""
+ local option = settings_to_hash(specification.option 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 content, richcontent = checkcontent(text,option)
+ local d = pdfdictionary {
+ Subtype = pdfconstant("Text"),
+ Open = option[v_max] and pdfboolean(true) or nil,
+ Contents = content,
+ RC = richcontent,
+ 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 ... >> >>
+-- object_2 -> <</Type /Rendition /S /MR /C << /Type /MediaClip ... >> >>
+-- rendering -> <</Type /Rendition /S /MS [objref_1 objref_2]>>
+--
+-- we only work foreward here (currently)
+-- annotation is to be packed at the tex end
+
+-- aiff audio/aiff
+-- au audio/basic
+-- avi video/avi
+-- mid audio/midi
+-- mov video/quicktime
+-- mp3 audio/x-mp3 (mpeg)
+-- mp4 audio/mp4
+-- mp4 video/mp4
+-- mpeg video/mpeg
+-- smil application/smil
+-- swf application/x-shockwave-flash
+
+-- P media play parameters (evt /BE for controls etc
+-- A boolean (audio)
+-- C boolean (captions)
+-- O boolean (overdubs)
+-- S boolean (subtitles)
+-- PL pdfconstant("ADBE_MCI"),
+
+-- F = flags,
+-- T = title,
+-- Contents = rubish,
+-- AP = irrelevant,
+
+-- sound is different, no window (or zero) so we need to collect them and
+-- force them if not set
+
+local ms, mu, mf = { }, { }, { }
+
+local function delayed(label)
+ local a = pdfreserveannotation()
+ mu[label] = a
+ return pdfreference(a)
+end
+
+local function insertrenderingwindow(specification)
+ local label = specification.label
+--~ local openpage = specification.openpage
+--~ local closepage = specification.closepage
+ if specification.option == v_auto then
+ if openpageaction then
+ -- \handlereferenceactions{\v!StartRendering{#2}}
+ end
+ if closepageaction then
+ -- \handlereferenceactions{\v!StopRendering {#2}}
+ end
+ end
+ local actions = nil
+ if openpage or closepage then
+ actions = pdfdictionary {
+ PO = (openpage and lpdf.action(openpage )) or nil,
+ PC = (closepage and lpdf.action(closepage)) or nil,
+ }
+ end
+ local page = tonumber(specification.page) or texcount.realpageno -- todo
+ local r = mu[label] or pdfreserveannotation() -- why the reserve here?
+ local a = pdfdictionary {
+ S = pdfconstant("Rendition"),
+ R = mf[label],
+ OP = 0,
+ AN = pdfreference(r),
+ }
+ local d = pdfdictionary {
+ Subtype = pdfconstant("Screen"),
+ P = pdfreference(pdfpagereference(page)),
+ A = a, -- needed in order to make the annotation clickable (i.e. don't bark)
+ Border = pdf_border,
+ AA = actions,
+ }
+ local width = specification.width or 0
+ local height = specification.height or 0
+ if height == 0 or width == 0 then
+ -- todo: sound needs no window
+ end
+ write_node(pdfannotation_node(width,height,0,d(),r)) -- save ref
+ return pdfreference(r)
+end
+
+-- some dictionaries can have a MH (must honor) or BE (best effort) capsule
+
+local function insertrendering(specification)
+ local label = specification.label
+ local option = settings_to_hash(specification.option)
+ if not mf[label] then
+ local filename = specification.filename
+ local isurl = find(filename,"://")
+ --~ local start = pdfdictionary {
+ --~ Type = pdfconstant("MediaOffset"),
+ --~ S = pdfconstant("T"), -- time
+ --~ T = pdfdictionary { -- time
+ --~ Type = pdfconstant("Timespan"),
+ --~ S = pdfconstant("S"),
+ --~ V = 3, -- time in seconds
+ --~ },
+ --~ }
+ --~ local start = pdfdictionary {
+ --~ Type = pdfconstant("MediaOffset"),
+ --~ S = pdfconstant("F"), -- frame
+ --~ F = 100 -- framenumber
+ --~ }
+ --~ local start = pdfdictionary {
+ --~ Type = pdfconstant("MediaOffset"),
+ --~ S = pdfconstant("M"), -- mark
+ --~ M = "somemark",
+ --~ }
+ --~ local parameters = pdfdictionary {
+ --~ BE = pdfdictionary {
+ --~ B = start,
+ --~ }
+ --~ }
+ --~ local parameters = pdfdictionary {
+ --~ Type = pdfconstant(MediaPermissions),
+ --~ TF = pdfstring("TEMPALWAYS") }, -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS
+ --~ }
+ local descriptor = pdfdictionary {
+ Type = pdfconstant("Filespec"),
+ F = filename,
+ }
+ if isurl then
+ descriptor.FS = pdfconstant("URL")
+ elseif option[v_embed] then
+ descriptor.EF = codeinjections.embedfile { file = filename }
+ end
+ local clip = pdfdictionary {
+ Type = pdfconstant("MediaClip"),
+ S = pdfconstant("MCD"),
+ N = label,
+ CT = specification.mime,
+ Alt = pdfarray { "", "file not found" }, -- language id + message
+ D = pdfreference(pdfflushobject(descriptor)),
+ -- P = pdfreference(pdfflushobject(parameters)),
+ }
+ local rendition = pdfdictionary {
+ Type = pdfconstant("Rendition"),
+ S = pdfconstant("MR"),
+ N = label,
+ C = pdfreference(pdfflushobject(clip)),
+ }
+ mf[label] = pdfreference(pdfflushobject(rendition))
+ end
+end
+
+local function insertrenderingobject(specification) -- todo
+ local label = specification.label
+ if not mf[label] then
+ report_media("unknown medium, label %a",label)
+ local clip = pdfdictionary { -- does not work that well one level up
+ Type = pdfconstant("MediaClip"),
+ S = pdfconstant("MCD"),
+ N = label,
+ D = pdfreference(unknown), -- not label but objectname, hm .. todo?
+ }
+ local rendition = pdfdictionary {
+ Type = pdfconstant("Rendition"),
+ S = pdfconstant("MR"),
+ N = label,
+ C = pdfreference(pdfflushobject(clip)),
+ }
+ mf[label] = pdfreference(pdfflushobject(rendition))
+ end
+end
+
+function codeinjections.processrendering(label)
+ local specification = interactions.renderings.rendering(label)
+ if not specification then
+ -- error
+ elseif specification.type == "external" then
+ insertrendering(specification)
+ else
+ insertrenderingobject(specification)
+ end
+end
+
+function codeinjections.insertrenderingwindow(specification)
+ local label = specification.label
+ codeinjections.processrendering(label)
+ ms[label] = insertrenderingwindow(specification)
+end
+
+local function set(operation,arguments)
+ codeinjections.processrendering(arguments)
+ return pdfdictionary {
+ S = pdfconstant("Rendition"),
+ OP = operation,
+ R = mf[arguments],
+ AN = ms[arguments] or delayed(arguments),
+ }
+end
+
+function executers.startrendering (arguments) return set(0,arguments) end
+function executers.stoprendering (arguments) return set(1,arguments) end
+function executers.pauserendering (arguments) return set(2,arguments) end
+function executers.resumerendering(arguments) return set(3,arguments) end