diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2011-06-01 08:54:21 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2011-06-01 08:54:21 +0000 |
commit | d7ccb42582f85acf30568913610ccf4d602023fb (patch) | |
tree | 7292e3545a420676878e7451b68892d360c62cb6 /Master/texmf-dist/tex/context/base/lpdf-ano.lua | |
parent | 2d62a6fe9b80def59c392268022f1f9a2d6e358f (diff) |
commit context 2011.05.18
git-svn-id: svn://tug.org/texlive/trunk@22719 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lpdf-ano.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lpdf-ano.lua | 429 |
1 files changed, 269 insertions, 160 deletions
diff --git a/Master/texmf-dist/tex/context/base/lpdf-ano.lua b/Master/texmf-dist/tex/context/base/lpdf-ano.lua index e9e67e16319..9f452c34d12 100644 --- a/Master/texmf-dist/tex/context/base/lpdf-ano.lua +++ b/Master/texmf-dist/tex/context/base/lpdf-ano.lua @@ -6,43 +6,59 @@ if not modules then modules = { } end modules ['lpdf-ano'] = { license = "see context related readme files" } -local tostring, format, rep = tostring, string.rep, string.format +local next, tostring = next, tostring +local rep, format = string.rep, string.format local texcount = tex.count +local backends, lpdf = backends, lpdf + local trace_references = false trackers.register("references.references", function(v) trace_references = v end) local trace_destinations = false trackers.register("references.destinations", function(v) trace_destinations = v end) local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end) +local report_reference = logs.reporter("backend","references") +local report_destination = logs.reporter("backend","destinations") +local report_bookmark = logs.reporter("backend","bookmarks") + local variables = interfaces.variables local constants = interfaces.constants -local nodeinjections = backends.pdf.nodeinjections -local codeinjections = backends.pdf.codeinjections -local registrations = backends.pdf.registrations - -jobreferences = jobreferences or { } -jobreferences.runners = jobreferences.runners or { } -jobreferences.specials = jobreferences.specials or { } -jobreferences.handlers = jobreferences.handlers or { } -jobreferences.executers = jobreferences.executers or { } - -local runners = jobreferences.runners -local specials = jobreferences.specials -local handlers = jobreferences.handlers -local executers = jobreferences.executers - -local pdfdictionary = lpdf.dictionary -local pdfarray = lpdf.array -local pdfreference = lpdf.reference -local pdfunicode = lpdf.unicode -local pdfconstant = lpdf.constant -local pdfflushobject = lpdf.flushobject -local pdfreserveobject = lpdf.reserveobject -local pdfannotation = nodes.pdfannotation -local pdfdestination = nodes.pdfdestination - -local pdfpagereference = tex.pdfpageref +local settings_to_array = utilities.parsers.settings_to_array + +local nodeinjections = backends.pdf.nodeinjections +local codeinjections = backends.pdf.codeinjections +local registrations = backends.pdf.registrations + +local javascriptcode = interactions.javascripts.code + +local references = structures.references +local bookmarks = structures.bookmarks + +local runners = references.runners +local specials = references.specials +local handlers = references.handlers +local executers = references.executers +local nodepool = nodes.pool + +local pdfannotation_node = nodepool.pdfannotation +local pdfdestination_node = nodepool.pdfdestination +local latelua_node = nodepool.latelua + +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfreference = lpdf.reference +local pdfunicode = lpdf.unicode +local pdfconstant = lpdf.constant +local pdfflushobject = lpdf.flushobject +local pdfshareobjectreference = lpdf.shareobjectreference +local pdfimmediateobject = lpdf.immediateobject +local pdfreserveobject = lpdf.reserveobject +local pdfpagereference = lpdf.pagereference +local pdfdelayedobject = lpdf.delayedobject +local pdfregisterannotation = lpdf.registerannotation + +local pdf_annot = pdfconstant("Annot") local pdf_uri = pdfconstant("URI") local pdf_gotor = pdfconstant("GoToR") local pdf_goto = pdfconstant("GoTo") @@ -51,26 +67,35 @@ local pdf_javascript = pdfconstant("JavaScript") local pdf_link = pdfconstant("Link") local pdf_n = pdfconstant("N") local pdf_t = pdfconstant("T") +local pdf_fit = pdfconstant("Fit") +local pdf_named = pdfconstant("Named") + +-- todo: 3dview + local pdf_border = pdfarray { 0, 0, 0 } +local getinnermethod = references.getinnermethod + local cache = { } -local function pagedest(n) - local pd = cache[n] - if not pd then - local a = pdfarray { - pdfreference(pdfpagereference(n)), - pdfconstant("Fit") - } - pd = pdfreference(pdfflushobject(a)) - cache[n] = pd +local function pagedestination(n) -- only cache fit + if n > 0 then + local pd = cache[n] + if not pd then + local a = pdfarray { + pdfreference(pdfpagereference(n)), + pdf_fit, + } + pd = pdfshareobjectreference(a) + cache[n] = pd + end + return pd end - return pd end -lpdf.pagedest = pagedest +lpdf.pagedestination = pagedestination -local defaultdestination = pdfarray { 0, pdfconstant("Fit") } +local defaultdestination = pdfarray { 0, pdf_fit } local function link(url,filename,destination,page,actions) if filename and filename ~= "" then @@ -93,43 +118,36 @@ local function link(url,filename,destination,page,actions) URI = url, } elseif filename and filename ~= "" then + -- no page ? + if destination == "" then + destination = nil + end + if not destination and page then + destination = pdfarray { page - 1, pdf_fit } + end return pdfdictionary { S = pdf_gotor, -- can also be pdf_launch F = filename, - D = (destination and destination ~= "" and destination), -- or defaultdestination, + D = destination or defaultdestination, -- D is mandate NewWindow = (actions.newwindow and true) or nil, } elseif destination and destination ~= "" then - local realpage, p = texcount.realpageno, tonumber(page) - if not p then - -- sorry - elseif p > realpage then - texcount.referencepagestate = 3 - elseif p < realpage then - texcount.referencepagestate = 2 - else - texcount.referencepagestate = 1 - end - return pdfdictionary { + return pdfdictionary { -- can be cached S = pdf_goto, D = destination, } - elseif page and page ~= "" then - local realpage, p = texcount.realpageno, tonumber(page) - if p then - if p > realpage then - texcount.referencepagestate = 3 - elseif p < realpage then - texcount.referencepagestate = 2 - else - texcount.referencepagestate = 1 - end - return pdfdictionary { + else + local p = tonumber(page) + if p and p > 0 then + return pdfdictionary { -- can be cached S = pdf_goto, - D = pagedest(p), + D = pdfarray { + pdfreference(pdfpagereference(p)), + pdf_fit, + } } else - commands.writestatus("references","invalid page reference: %s",page or "?") + report_reference("invalid page reference: %s",tostring(page)) end end return false @@ -152,7 +170,7 @@ function lpdf.launch(program,parameters) end function lpdf.javascript(name,arguments) - local script = javascripts.code(name,arguments) -- make into object (hash) + local script = javascriptcode(name,arguments) -- make into object (hash) if script then return pdfdictionary { S = pdf_javascript, @@ -179,105 +197,158 @@ local function pdfaction(actions) what = what(a,actions) end if what then - action.next = what + action.Next = what action = what else -- error return nil end end - return first + return first, actions.n end end end -lpdf.pdfaction = pdfaction +lpdf.action = pdfaction -function codeinjections.prerollreference(actions) - local main = actions and pdfaction(actions) - if main then - main = pdfdictionary { - Subtype = pdf_link, - Border = pdf_border, - H = (not actions.highlight and pdf_n) or nil, - A = main, - -- does not work at all in spite of specification - -- OC = (actions.layer and lpdf.layerreferences[actions.layer]) or nil, - -- OC = backends.pdf.layerreference(actions.layer), - } - return main("A") -- todo: cache this, maybe weak +function codeinjections.prerollreference(actions) -- share can become option + if actions then + local main, n = pdfaction(actions) + if main then + main = pdfdictionary { + Subtype = pdf_link, + Border = pdf_border, + H = (not actions.highlight and pdf_n) or nil, + A = pdfshareobjectreference(main), + F = 4, -- print (mandate in pdf/a) + } + return main("A"), n + end + end +end + +local function use_normal_annotations() + + local function reference(width,height,depth,prerolled) -- keep this one + if prerolled then + if trace_references then + report_reference("w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) + end + return pdfannotation_node(width,height,depth,prerolled) + end + end + + local function finishreference() end + + return reference, finishreference + end --- local cache = { } -- no real gain in thsi --- --- function codeinjections.prerollreference(actions) --- local main = actions and pdfaction(actions) --- if main then --- main = pdfdictionary { --- Subtype = pdf_link, --- Border = pdf_border, --- H = (not actions.highlight and pdf_n) or nil, --- A = main, --- } --- local cm = cache[main] --- if not cm then --- cm = "/A ".. tostring(pdfreference(pdfflushobject(main)) --- cache[main] = cm --- end --- return cm --- end --- end - -function nodeinjections.reference(width,height,depth,prerolled) - if prerolled then - if swapdir then - width = - width +-- evenrually we can do this for special refs only + +local hashed, nofunique, nofused = { }, 0, 0 + +local function use_shared_annotations() + + local factor = number.dimenfactors.bp + + local function finishreference(width,height,depth,prerolled) -- %0.2f looks okay enough (no scaling anyway) + local h, v = pdf.h, pdf.v + local llx, lly = h*factor, (v - depth)*factor + local urx, ury = (h + width)*factor, (v + height)*factor + local annot = format("<< /Type /Annot %s /Rect [%0.2f %0.2f %0.2f %0.2f] >>",prerolled,llx,lly,urx,ury) + local n = hashed[annot] + if not n then + n = pdfdelayedobject(annot) + hashed[annot] = n + nofunique = nofunique + 1 end - if trace_references then - logs.report("references","w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) + nofused = nofused + 1 + pdfregisterannotation(n) + end + + _bpnf_ = finishreference + + local function reference(width,height,depth,prerolled) + if prerolled then + if trace_references then + report_reference("w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) + end + local luacode = format("_bpnf_(%s,%s,%s,'%s')",width,height,depth,prerolled) + return latelua_node(luacode) end - return pdfannotation(width,height,depth,prerolled) end + + statistics.register("pdf annotations", function() + if nofused > 0 then + -- table.print(hashed,"hashed_annotations") + return string.format("%s embedded, %s unique",nofused,nofunique) + else + return nil + end + end) + + + return reference, finishreference + end +local lln = latelua_node() if node.has_field(lln,'string') then + + directives.register("refences.sharelinks", function(v) + if v then + nodeinjections.reference, codeinjections.finishreference = use_shared_annotations() + else + nodeinjections.reference, codeinjections.finishreference = use_normal_annotations() + end + end) + + nodeinjections.reference, codeinjections.finishreference = use_shared_annotations() + +else + + nodeinjections.reference, codeinjections.finishreference = use_normal_annotations() + +end node.free(lln) + +-- -- -- -- +-- -- -- -- + function nodeinjections.destination(width,height,depth,name,view) - if swapdir then - width = - width - end if trace_destinations then - logs.report("destinations","w=%s, h=%s, d=%s, n=%s, v=%s",width,height,depth,name,view or "no view") + report_destination("w=%s, h=%s, d=%s, n=%s, v=%s",width,height,depth,name,view or "no view") end - return pdfdestination(width,height,depth,name,view) + return pdfdestination_node(width,height,depth,name,view) end -- runners and specials -local method = "internal" - runners["inner"] = function(var,actions) - if method == "internal" then + if getinnermethod() == "names" then local vir = var.i.references local internal = vir and vir.internal if internal then - var.inner = "aut:"..internal + var.inner = "aut:" .. internal end + else + var.inner = nil end return link(nil,nil,var.inner,var.r,actions) end runners["inner with arguments"] = function(var,actions) - logs.report("references","todo: inner with arguments") + report_reference("todo: inner with arguments") return false end runners["outer"] = function(var,actions) - local file, url = jobreferences.checkedfileorurl(var.outer,var.outer) + local file, url = references.checkedfileorurl(var.outer,var.outer) return link(url,file,var.arguments,nil,actions) end runners["outer with inner"] = function(var,actions) - local file = jobreferences.checkedfile(var.f) + local file = references.checkedfile(var.f) return link(nil,file,var.inner,var.r,actions) end @@ -287,7 +358,7 @@ runners["special outer with operation"] = function(var,actions) end runners["special outer"] = function(var,actions) - logs.report("references","todo: special outer") + report_reference("todo: special outer") return false end @@ -297,22 +368,22 @@ runners["special"] = function(var,actions) end runners["outer with inner with arguments"] = function(var,actions) - logs.report("references","todo: outer with inner with arguments") + report_reference("todo: outer with inner with arguments") return false end runners["outer with special and operation and arguments"] = function(var,actions) - logs.report("references","todo: outer with special and operation and arguments") + report_reference("todo: outer with special and operation and arguments") return false end runners["outer with special"] = function(var,actions) - logs.report("references","todo: outer with special") + report_reference("todo: outer with special") return false end runners["outer with special and operation"] = function(var,actions) - logs.report("references","todo: outer with special and operation") + report_reference("todo: outer with special and operation") return false end @@ -321,10 +392,11 @@ runners["special operation with arguments"] = runners["special"] function specials.internal(var,actions) -- better resolve in strc-ref local i = tonumber(var.operation) - local v = jobreferences.internals[i] + local v = references.internals[i] if not v then -- error - elseif method == "internal" then + report_reference("no internal reference '%s'",var.operation) + elseif getinnermethod() == "names" then -- named return link(nil,nil,"aut:"..i,v.references.realpage,actions) else @@ -333,27 +405,60 @@ function specials.internal(var,actions) -- better resolve in strc-ref end end +-- realpage already resolved + specials.i = specials.internal -function specials.page(var,actions) -- better resolve in strc-ref +local pages = references.pages + +function specials.page(var,actions) local file = var.f if file then - file = jobreferences.checkedfile(file) - return link(nil,file,nil,p or var.operation,actions) + file = references.checkedfile(file) + return link(nil,file,nil,var.operation,actions) else - local p = jobreferences.pages[var.operation] - if type(p) == "function" then - p = p() + local p = var.r + if not p then -- todo: call special from reference code + p = pages[var.operation] + if type(p) == "function" then -- double + p = p() + else + p = tonumber(references.realpageofpage(tonumber(o))) + end end return link(nil,nil,nil,p or var.operation,actions) end end +function specials.realpage(var,actions) + local file = var.f + if file then + file = references.checkedfile(file) + return link(nil,file,nil,var.operation,actions) + else + return link(nil,nil,nil,var.operation,actions) + end +end + +function specials.userpage(var,actions) + local file = var.f + if file then + file = references.checkedfile(file) + return link(nil,file,nil,var.operation,actions) + else + local p = var.r + if not p then -- todo: call special from reference code + p = tonumber(references.realpageofpage(var.operation)) + end + return link(nil,nil,nil,p,actions) + end +end + -- todo, do this in references namespace ordered instead (this is an experiment) local splitter = lpeg.splitat(":") -function specials.order(var,actions) -- jobreferences.specials ! +function specials.order(var,actions) -- references.specials ! local operation = var.operation if operation then local kind, name, n = lpegmatch(splitter,operation) @@ -369,22 +474,22 @@ function specials.order(var,actions) -- jobreferences.specials ! end function specials.url(var,actions) - local url = jobreferences.checkedurl(var.operation) + local url = references.checkedurl(var.operation) return link(url,nil,var.arguments,nil,actions) end function specials.file(var,actions) - local file = jobreferences.checkedfile(var.operation) + local file = references.checkedfile(var.operation) return link(nil,file,var.arguments,nil,actions) end function specials.fileorurl(var,actions) - local file, url = jobreferences.checkedfileorurl(var.operation,var.operation) + local file, url = references.checkedfileorurl(var.operation,var.operation) return link(url,file,var.arguments,nil,actions) end function specials.program(var,content) - local program = jobreferences.checkedprogram(var.operation) + local program = references.checkedprogram(var.operation) return lpdf.launch(program,var.arguments) end @@ -394,8 +499,6 @@ end specials.JS = specials.javascript -local pdf_named = pdfconstant("Named") - executers.importform = pdfdictionary { S = pdf_named, N = pdfconstant("AcroForm:ImportFDF") } executers.exportform = pdfdictionary { S = pdf_named, N = pdfconstant("AcroForm:ExportFDF") } executers.first = pdfdictionary { S = pdf_named, N = pdfconstant("FirstPage") } @@ -426,7 +529,7 @@ local function fieldset(arguments) end function executers.resetform(arguments) - arguments = (type(arguments) == "table" and arguments) or aux.settings_to_array(arguments) + arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments) return pdfdictionary { S = pdfconstant("ResetForm"), Field = fieldset(arguments[1]) @@ -448,7 +551,7 @@ local flags = { } function executers.submitform(arguments) - arguments = (type(arguments) == "table" and arguments) or aux.settings_to_array(arguments) + arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments) local flag = flags[formmethod] or flags.post flag = (flag and (flag[formformat] or flag.xml)) or 32 -- default: post, xml return pdfdictionary { @@ -483,7 +586,7 @@ local pdf_resume = pdfconstant("Resume") local pdf_pause = pdfconstant("Pause") local function movie_or_sound(operation,arguments) - arguments = (type(arguments) == "table" and arguments) or aux.settings_to_array(arguments) + arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments) return pdfdictionary { S = pdf_movie, T = format("movie %s",arguments[1] or "noname"), @@ -522,13 +625,15 @@ local function build(levels,start,parent,method) local startlevel = levels[start][1] local i, n = start, 0 local child, entry, m, prev, first, last, f, l --- to be tested: i can be nil while i and i <= #levels do local li = levels[i] local level, title, reference, open = li[1], li[2], li[3], li[4] - if level == startlevel then + if level < startlevel then + pdfflushobject(child,entry) + return i, n, first, last + elseif level == startlevel then if trace_bookmarks then - logs.report("bookmark","%3i %s%s %s",reference.realpage,rep(" ",level-1),(open and "+") or "-",title) + report_bookmark("%3i %s%s %s",reference.realpage,rep(" ",level-1),(open and "+") or "-",title) end local prev = child child = pdfreserveobject() @@ -544,16 +649,13 @@ local function build(levels,start,parent,method) if method == "internal" then entry.Dest = "aut:" .. reference.internal else -- if method == "page" then - entry.Dest = pagedest(reference.realpage) + entry.Dest = pagedestination(reference.realpage) end if not first then first, last = child, child end prev = child last = prev n = n + 1 i = i + 1 - elseif level < startlevel then - pdfflushobject(child,entry) - return i, n, first, last elseif i < #levels and level > startlevel then i, m, f, l = build(levels,i,pdfreference(child),method) entry.Count = (open and m) or -m @@ -576,14 +678,21 @@ local function build(levels,start,parent,method) end function codeinjections.addbookmarks(levels,method) - local parent = pdfreserveobject() - local _, m, first, last = build(levels,1,pdfreference(parent),method or "internal") - local dict = pdfdictionary { - Type = pdfconstant("Outlines"), - First = pdfreference(first), - Last = pdfreference(last), - Count = m, - } - pdfflushobject(parent,dict) - lpdf.addtocatalog("Outlines",lpdf.reference(parent)) + if #levels > 0 then + structures.bookmarks.flatten(levels) -- dirty trick for lack of structure + local parent = pdfreserveobject() + local _, m, first, last = build(levels,1,pdfreference(parent),method or "internal") + local dict = pdfdictionary { + Type = pdfconstant("Outlines"), + First = pdfreference(first), + Last = pdfreference(last), + Count = m, + } + pdfflushobject(parent,dict) + lpdf.addtocatalog("Outlines",lpdf.reference(parent)) + end end + +-- this could also be hooked into the frontend finalizer + +lpdf.registerdocumentfinalizer(function() bookmarks.place() end,1,"bookmarks") |