diff options
Diffstat (limited to 'tex/context/base/lpdf-wid.lua')
-rw-r--r-- | tex/context/base/lpdf-wid.lua | 1290 |
1 files changed, 645 insertions, 645 deletions
diff --git a/tex/context/base/lpdf-wid.lua b/tex/context/base/lpdf-wid.lua index 20fc14679..9ea4744f1 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 |