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 b9f8cfc7c..4bf98edcc 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
|