diff options
Diffstat (limited to 'tex/context/base/lpdf-epd.lua')
-rw-r--r-- | tex/context/base/lpdf-epd.lua | 702 |
1 files changed, 351 insertions, 351 deletions
diff --git a/tex/context/base/lpdf-epd.lua b/tex/context/base/lpdf-epd.lua index 4bf98edcc..b9f8cfc7c 100644 --- a/tex/context/base/lpdf-epd.lua +++ b/tex/context/base/lpdf-epd.lua @@ -1,351 +1,351 @@ -if not modules then modules = { } end modules ['lpdf-epd'] = {
- version = 1.001,
- comment = "companion to lpdf-epa.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This is an experimental layer around the epdf library. The reason for
--- this layer is that I want to be independent of the library (which
--- implements a selection of what a file provides) and also because I
--- want an interface closer to Lua's table model while the API stays
--- close to the original xpdf library. Of course, after prototyping a
--- solution, we can optimize it using the low level epdf accessors.
-
--- It will be handy when we have a __length and __next that can trigger
--- the resolve till then we will provide .n as #.
-
--- As there can be references to the parent we cannot expand a tree. I
--- played with some expansion variants but it does to pay off.
-
--- Maybe we need a close().
--- We cannot access all destinations in one run.
-
-local setmetatable, rawset, rawget, tostring, tonumber = setmetatable, rawset, rawget, tostring, tonumber
-local lower, match, char, find, sub = string.lower, string.match, string.char, string.find, string.sub
-local concat = table.concat
-local toutf = string.toutf
-
-local report_epdf = logs.reporter("epdf")
-
--- a bit of protection
-
-local limited = false
-
-directives.register("system.inputmode", function(v)
- if not limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- epdf.open = i_limiter.protect(epdf.open)
- limited = true
- end
- end
-end)
-
---
-
-function epdf.type(o)
- local t = lower(match(tostring(o),"[^ :]+"))
- return t or "?"
-end
-
-lpdf = lpdf or { }
-local lpdf = lpdf
-
-lpdf.epdf = { }
-
-local checked_access
-
-local function prepare(document,d,t,n,k)
- for i=1,n do
- local v = d:getVal(i)
- local r = d:getValNF(i)
- if r:getTypeName() == "ref" then
- r = r:getRef().num
- local c = document.cache[r]
- if c then
- --
- else
- c = checked_access[v:getTypeName()](v,document,r)
- if c then
- document.cache[r] = c
- document.xrefs[c] = r
- end
- end
- t[d:getKey(i)] = c
- else
- t[d:getKey(i)] = checked_access[v:getTypeName()](v,document)
- end
- end
- getmetatable(t).__index = nil
- return t[k]
-end
-
-local function some_dictionary(d,document,r)
- local n = d and d:getLength() or 0
- if n > 0 then
- local t = { }
- setmetatable(t, { __index = function(t,k) return prepare(document,d,t,n,k) end } )
- return t
- end
-end
-
-local done = { }
-
-local function prepare(document,a,t,n,k)
- for i=1,n do
- local v = a:get(i)
- local r = a:getNF(i)
- if v:getTypeName() == "null" then
- -- TH: weird, but appears possible
- elseif r:getTypeName() == "ref" then
- r = r:getRef().num
- local c = document.cache[r]
- if c then
- --
- else
- c = checked_access[v:getTypeName()](v,document,r)
- document.cache[r] = c
- document.xrefs[c] = r
- end
- t[i] = c
- else
- t[i] = checked_access[v:getTypeName()](v,document)
- end
- end
- getmetatable(t).__index = nil
- return t[k]
-end
-
-local function some_array(a,document,r)
- local n = a and a:getLength() or 0
- if n > 0 then
- local t = { n = n }
- setmetatable(t, { __index = function(t,k) return prepare(document,a,t,n,k) end } )
- return t
- end
-end
-
-local function streamaccess(s,_,what)
- if not what or what == "all" or what == "*all" then
- local t, n = { }, 0
- s:streamReset()
- while true do
- local c = s:streamGetChar()
- if c < 0 then
- break
- else
- n = n + 1
- t[n] = char(c)
- end
- end
- return concat(t)
- end
-end
-
-local function some_stream(d,document,r)
- if d then
- d:streamReset()
- local s = some_dictionary(d:streamGetDict(),document,r)
- getmetatable(s).__call = function(...) return streamaccess(d,...) end
- return s
- end
-end
-
--- we need epdf.getBool
-
-checked_access = {
- dictionary = function(d,document,r)
- return some_dictionary(d:getDict(),document,r)
- end,
- array = function(a,document,r)
- return some_array(a:getArray(),document,r)
- end,
- stream = function(v,document,r)
- return some_stream(v,document,r)
- end,
- real = function(v)
- return v:getReal()
- end,
- integer = function(v)
- return v:getNum()
- end,
- string = function(v)
- return toutf(v:getString())
- end,
- boolean = function(v)
- return v:getBool()
- end,
- name = function(v)
- return v:getName()
- end,
- ref = function(v)
- return v:getRef()
- end,
- null = function()
- return nil
- end,
-}
-
--- checked_access.real = epdf.real
--- checked_access.integer = epdf.integer
--- checked_access.string = epdf.string
--- checked_access.boolean = epdf.boolean
--- checked_access.name = epdf.name
--- checked_access.ref = epdf.ref
-
-local function getnames(document,n,target) -- direct
- if n then
- local Names = n.Names
- if Names then
- if not target then
- target = { }
- end
- for i=1,Names.n,2 do
- target[Names[i]] = Names[i+1]
- end
- else
- local Kids = n.Kids
- if Kids then
- for i=1,Kids.n do
- target = getnames(document,Kids[i],target)
- end
- end
- end
- return target
- end
-end
-
-local function getkids(document,n,target) -- direct
- if n then
- local Kids = n.Kids
- if Kids then
- for i=1,Kids.n do
- target = getkids(document,Kids[i],target)
- end
- elseif target then
- target[#target+1] = n
- else
- target = { n }
- end
- return target
- end
-end
-
--- /OCProperties <<
--- /OCGs [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ]
--- /D <<
--- /Order [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ]
--- /ON [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ]
--- /OFF [ ]
--- >>
--- >>
-
-local function getlayers(document)
- local properties = document.Catalog.OCProperties
- if properties then
- local layers = properties.OCGs
- if layers then
- local t = { }
- local n = layers.n
- for i=1,n do
- local layer = layers[i]
---~ print(document.xrefs[layer])
- t[i] = layer.Name
- end
- t.n = n
- return t
- end
- end
-end
-
-local function getpages(document)
- local data = document.data
- local xrefs = document.xrefs
- local cache = document.cache
- local cata = data:getCatalog()
- local xref = data:getXRef()
- local pages = { }
- local nofpages = cata:getNumPages()
- for pagenumber=1,nofpages do
- local pagereference = cata:getPageRef(pagenumber).num
- local pagedata = some_dictionary(xref:fetch(pagereference,0):getDict(),document,pagereference)
- if pagedata then
- pagedata.number = pagenumber
- pages[pagenumber] = pagedata
- xrefs[pagedata] = pagereference
- cache[pagereference] = pagedata
- else
- report_epdf("missing pagedata at slot %i",i)
- end
- end
- pages.n = nofpages
- return pages
-end
-
--- loader
-
-local function delayed(document,tag,f)
- local t = { }
- setmetatable(t, { __index = function(t,k)
- local result = f()
- if result then
- document[tag] = result
- return result[k]
- end
- end } )
- return t
-end
-
-local loaded = { }
-
-function lpdf.epdf.load(filename)
- local document = loaded[filename]
- if not document then
- statistics.starttiming(lpdf.epdf)
- local data = epdf.open(filename) -- maybe resolvers.find_file
- if data then
- document = {
- filename = filename,
- cache = { },
- xrefs = { },
- data = data,
- }
- local Catalog = some_dictionary(data:getXRef():getCatalog():getDict(),document)
- local Info = some_dictionary(data:getXRef():getDocInfo():getDict(),document)
- document.Catalog = Catalog
- document.Info = Info
- -- document.catalog = Catalog
- -- a few handy helper tables
- document.pages = delayed(document,"pages", function() return getpages(document) end)
- document.destinations = delayed(document,"destinations", function() return getnames(document,Catalog.Names and Catalog.Names.Dests) end)
- document.javascripts = delayed(document,"javascripts", function() return getnames(document,Catalog.Names and Catalog.Names.JS) end)
- document.widgets = delayed(document,"widgets", function() return getnames(document,Catalog.Names and Catalog.Names.AcroForm) end)
- document.embeddedfiles = delayed(document,"embeddedfiles",function() return getnames(document,Catalog.Names and Catalog.Names.EmbeddedFiles) end)
- document.layers = delayed(document,"layers", function() return getlayers(document) end)
- else
- document = false
- end
- loaded[filename] = document
- statistics.stoptiming(lpdf.epdf)
- -- print(statistics.elapsedtime(lpdf.epdf))
- end
- return document
-end
-
--- for k, v in next, expand(t) do
-
-function lpdf.epdf.expand(t)
- if type(t) == "table" then
- local dummy = t.dummy
- end
- return t
-end
-
--- helpers
-
--- function lpdf.epdf.getdestinationpage(document,name)
--- local destination = document.data:findDest(name)
--- return destination and destination.number
--- end
+if not modules then modules = { } end modules ['lpdf-epd'] = { + version = 1.001, + comment = "companion to lpdf-epa.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This is an experimental layer around the epdf library. The reason for +-- this layer is that I want to be independent of the library (which +-- implements a selection of what a file provides) and also because I +-- want an interface closer to Lua's table model while the API stays +-- close to the original xpdf library. Of course, after prototyping a +-- solution, we can optimize it using the low level epdf accessors. + +-- It will be handy when we have a __length and __next that can trigger +-- the resolve till then we will provide .n as #. + +-- As there can be references to the parent we cannot expand a tree. I +-- played with some expansion variants but it does to pay off. + +-- Maybe we need a close(). +-- We cannot access all destinations in one run. + +local setmetatable, rawset, rawget, tostring, tonumber = setmetatable, rawset, rawget, tostring, tonumber +local lower, match, char, find, sub = string.lower, string.match, string.char, string.find, string.sub +local concat = table.concat +local toutf = string.toutf + +local report_epdf = logs.reporter("epdf") + +-- a bit of protection + +local limited = false + +directives.register("system.inputmode", function(v) + if not limited then + local i_limiter = io.i_limiter(v) + if i_limiter then + epdf.open = i_limiter.protect(epdf.open) + limited = true + end + end +end) + +-- + +function epdf.type(o) + local t = lower(match(tostring(o),"[^ :]+")) + return t or "?" +end + +lpdf = lpdf or { } +local lpdf = lpdf + +lpdf.epdf = { } + +local checked_access + +local function prepare(document,d,t,n,k) + for i=1,n do + local v = d:getVal(i) + local r = d:getValNF(i) + if r:getTypeName() == "ref" then + r = r:getRef().num + local c = document.cache[r] + if c then + -- + else + c = checked_access[v:getTypeName()](v,document,r) + if c then + document.cache[r] = c + document.xrefs[c] = r + end + end + t[d:getKey(i)] = c + else + t[d:getKey(i)] = checked_access[v:getTypeName()](v,document) + end + end + getmetatable(t).__index = nil + return t[k] +end + +local function some_dictionary(d,document,r) + local n = d and d:getLength() or 0 + if n > 0 then + local t = { } + setmetatable(t, { __index = function(t,k) return prepare(document,d,t,n,k) end } ) + return t + end +end + +local done = { } + +local function prepare(document,a,t,n,k) + for i=1,n do + local v = a:get(i) + local r = a:getNF(i) + if v:getTypeName() == "null" then + -- TH: weird, but appears possible + elseif r:getTypeName() == "ref" then + r = r:getRef().num + local c = document.cache[r] + if c then + -- + else + c = checked_access[v:getTypeName()](v,document,r) + document.cache[r] = c + document.xrefs[c] = r + end + t[i] = c + else + t[i] = checked_access[v:getTypeName()](v,document) + end + end + getmetatable(t).__index = nil + return t[k] +end + +local function some_array(a,document,r) + local n = a and a:getLength() or 0 + if n > 0 then + local t = { n = n } + setmetatable(t, { __index = function(t,k) return prepare(document,a,t,n,k) end } ) + return t + end +end + +local function streamaccess(s,_,what) + if not what or what == "all" or what == "*all" then + local t, n = { }, 0 + s:streamReset() + while true do + local c = s:streamGetChar() + if c < 0 then + break + else + n = n + 1 + t[n] = char(c) + end + end + return concat(t) + end +end + +local function some_stream(d,document,r) + if d then + d:streamReset() + local s = some_dictionary(d:streamGetDict(),document,r) + getmetatable(s).__call = function(...) return streamaccess(d,...) end + return s + end +end + +-- we need epdf.getBool + +checked_access = { + dictionary = function(d,document,r) + return some_dictionary(d:getDict(),document,r) + end, + array = function(a,document,r) + return some_array(a:getArray(),document,r) + end, + stream = function(v,document,r) + return some_stream(v,document,r) + end, + real = function(v) + return v:getReal() + end, + integer = function(v) + return v:getNum() + end, + string = function(v) + return toutf(v:getString()) + end, + boolean = function(v) + return v:getBool() + end, + name = function(v) + return v:getName() + end, + ref = function(v) + return v:getRef() + end, + null = function() + return nil + end, +} + +-- checked_access.real = epdf.real +-- checked_access.integer = epdf.integer +-- checked_access.string = epdf.string +-- checked_access.boolean = epdf.boolean +-- checked_access.name = epdf.name +-- checked_access.ref = epdf.ref + +local function getnames(document,n,target) -- direct + if n then + local Names = n.Names + if Names then + if not target then + target = { } + end + for i=1,Names.n,2 do + target[Names[i]] = Names[i+1] + end + else + local Kids = n.Kids + if Kids then + for i=1,Kids.n do + target = getnames(document,Kids[i],target) + end + end + end + return target + end +end + +local function getkids(document,n,target) -- direct + if n then + local Kids = n.Kids + if Kids then + for i=1,Kids.n do + target = getkids(document,Kids[i],target) + end + elseif target then + target[#target+1] = n + else + target = { n } + end + return target + end +end + +-- /OCProperties << +-- /OCGs [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ] +-- /D << +-- /Order [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ] +-- /ON [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ] +-- /OFF [ ] +-- >> +-- >> + +local function getlayers(document) + local properties = document.Catalog.OCProperties + if properties then + local layers = properties.OCGs + if layers then + local t = { } + local n = layers.n + for i=1,n do + local layer = layers[i] +--~ print(document.xrefs[layer]) + t[i] = layer.Name + end + t.n = n + return t + end + end +end + +local function getpages(document) + local data = document.data + local xrefs = document.xrefs + local cache = document.cache + local cata = data:getCatalog() + local xref = data:getXRef() + local pages = { } + local nofpages = cata:getNumPages() + for pagenumber=1,nofpages do + local pagereference = cata:getPageRef(pagenumber).num + local pagedata = some_dictionary(xref:fetch(pagereference,0):getDict(),document,pagereference) + if pagedata then + pagedata.number = pagenumber + pages[pagenumber] = pagedata + xrefs[pagedata] = pagereference + cache[pagereference] = pagedata + else + report_epdf("missing pagedata at slot %i",i) + end + end + pages.n = nofpages + return pages +end + +-- loader + +local function delayed(document,tag,f) + local t = { } + setmetatable(t, { __index = function(t,k) + local result = f() + if result then + document[tag] = result + return result[k] + end + end } ) + return t +end + +local loaded = { } + +function lpdf.epdf.load(filename) + local document = loaded[filename] + if not document then + statistics.starttiming(lpdf.epdf) + local data = epdf.open(filename) -- maybe resolvers.find_file + if data then + document = { + filename = filename, + cache = { }, + xrefs = { }, + data = data, + } + local Catalog = some_dictionary(data:getXRef():getCatalog():getDict(),document) + local Info = some_dictionary(data:getXRef():getDocInfo():getDict(),document) + document.Catalog = Catalog + document.Info = Info + -- document.catalog = Catalog + -- a few handy helper tables + document.pages = delayed(document,"pages", function() return getpages(document) end) + document.destinations = delayed(document,"destinations", function() return getnames(document,Catalog.Names and Catalog.Names.Dests) end) + document.javascripts = delayed(document,"javascripts", function() return getnames(document,Catalog.Names and Catalog.Names.JS) end) + document.widgets = delayed(document,"widgets", function() return getnames(document,Catalog.Names and Catalog.Names.AcroForm) end) + document.embeddedfiles = delayed(document,"embeddedfiles",function() return getnames(document,Catalog.Names and Catalog.Names.EmbeddedFiles) end) + document.layers = delayed(document,"layers", function() return getlayers(document) end) + else + document = false + end + loaded[filename] = document + statistics.stoptiming(lpdf.epdf) + -- print(statistics.elapsedtime(lpdf.epdf)) + end + return document +end + +-- for k, v in next, expand(t) do + +function lpdf.epdf.expand(t) + if type(t) == "table" then + local dummy = t.dummy + end + return t +end + +-- helpers + +-- function lpdf.epdf.getdestinationpage(document,name) +-- local destination = document.data:findDest(name) +-- return destination and destination.number +-- end |