diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/lpdf-pde.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/lpdf-pde.lua | 1157 |
1 files changed, 0 insertions, 1157 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/lpdf-pde.lua b/Master/texmf-dist/tex/context/base/mkiv/lpdf-pde.lua deleted file mode 100644 index 9d14f8f5e82..00000000000 --- a/Master/texmf-dist/tex/context/base/mkiv/lpdf-pde.lua +++ /dev/null @@ -1,1157 +0,0 @@ -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", - history = "this one replaces the poppler/pdfe binding", -} - --- \enabledirectives[graphics.pdf.uselua] --- \enabledirectives[graphics.pdf.recompress] --- \enabledirectives[graphics.pdf.stripmarked] - --- maximum integer : +2^32 --- maximum real : +2^15 --- minimum real : 1/(2^16) - --- get_flagged : does that still work - --- ppdoc_permissions (ppdoc *pdf); - --- PPSTRING_ENCODED 1 << 0 --- PPSTRING_DECODED 1 << 1 --- PPSTRING_EXEC 1 << 2 postscript only --- PPSTRING_PLAIN 0 --- PPSTRING_BASE16 1 << 3 --- PPSTRING_BASE85 1 << 4 --- PPSTRING_UTF16BE 1 << 5 --- PPSTRING_UTF16LE 1 << 6 - --- PPDOC_ALLOW_PRINT 1 << 2 printing --- PPDOC_ALLOW_MODIFY 1 << 3 filling form fields, signing, creating template pages --- PPDOC_ALLOW_COPY 1 << 4 copying, copying for accessibility --- PPDOC_ALLOW_ANNOTS 1 << 5 filling form fields, copying, signing --- PPDOC_ALLOW_EXTRACT 1 << 9 contents copying for accessibility --- PPDOC_ALLOW_ASSEMBLY 1 << 10 no effect --- PPDOC_ALLOW_PRINT_HIRES 1 << 11 no effect - --- PPCRYPT_NONE 0 no encryption, go ahead --- PPCRYPT_DONE 1 encryption present but password succeeded, go ahead --- PPCRYPT_PASS -1 encryption present, need non-empty password --- PPCRYPT_FAIL -2 invalid or unsupported encryption (eg. undocumented in pdf spec) - -local setmetatable, rawset, rawget, type, next = setmetatable, rawset, rawget, type, next -local tostring, tonumber, unpack = tostring, tonumber, unpack -local char, byte, find = string.char, string.byte, string.find -local abs = math.abs -local concat, swapped, sortedhash, sortedkeys = table.concat, table.swapped, table.sortedhash, table.sortedkeys -local utfchar = string.char -local setmetatableindex = table.setmetatableindex - -local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns -local P, C, S, R, Ct, Cc, V, Carg, Cs, Cf, Cg = lpeg.P, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cc, lpeg.V, lpeg.Carg, lpeg.Cs, lpeg.Cf, lpeg.Cg - -if not lpdf then - require("lpdf-aux") -end - -if not (number and number.dimenfactors) then - require("util-dim") -end - -local epdf = pdfe - lpdf = lpdf or { } -local lpdf = lpdf -local lpdf_epdf = { } -lpdf.epdf = lpdf_epdf - -local openPDF = epdf.open -local newPDF = epdf.new -local closePDF = epdf.close - -local getcatalog = epdf.getcatalog -local getinfo = epdf.getinfo -local gettrailer = epdf.gettrailer -local getnofpages = epdf.getnofpages -local getversion = epdf.getversion -local getbox = epdf.getbox -local getstatus = epdf.getstatus -local unencrypt = epdf.unencrypt - -local dictionarytotable = epdf.dictionarytotable -local arraytotable = epdf.arraytotable -local pagestotable = epdf.pagestotable -local readwholestream = epdf.readwholestream - -local getfromreference = pdfe.getfromreference - -local report_epdf = logs.reporter("epdf") - -local allocate = utilities.storage.allocate - -local bpfactor = number.dimenfactors.bp - -local objectcodes = { [0] = - "none", - "null", - "bool", - "integer", - "number", - "name", - "string", - "array", - "dictionary", - "stream", - "reference", -} - -local encryptioncodes = { - [0] = "notencrypted", - [1] = "unencrypted", - [-1] = "protected", - [-2] = "failure", -} - -objectcodes = allocate(swapped(objectcodes,objectcodes)) -encryptioncodes = allocate(swapped(encryptioncodes,encryptioncodes)) - -pdfe.objectcodes = objectcodes -pdfe.encryptioncodes = encryptioncodes - -local null_object_code = objectcodes.null -local reference_object_code = objectcodes.reference - -local none_object_code = objectcodes.none -local null_object_code = objectcodes.null -local bool_object_code = objectcodes.bool -local integer_object_code = objectcodes.integer -local number_object_code = objectcodes.number -local name_object_code = objectcodes.name -local string_object_code = objectcodes.string -local array_object_code = objectcodes.array -local dictionary_object_code = objectcodes.dictionary -local stream_object_code = objectcodes.stream -local reference_object_code = objectcodes.reference - -local checked_access -local get_flagged -- from pdfe -> lpdf - -if lpdf.dictionary then - - -- we're in context - - local pdfdictionary = lpdf.dictionary - local pdfarray = lpdf.array - local pdfconstant = lpdf.constant - local pdfstring = lpdf.string - local pdfunicode = lpdf.unicode - - get_flagged = function(t,f,k) - local tk = t[k] -- triggers resolve - local fk = f[k] - if not fk then - return tk - elseif fk == "name" then - return pdfconstant(tk) - elseif fk == "array" then - return pdfarray(tk) - elseif fk == "dictionary" then - return pdfarray(tk) - elseif fk == "rawtext" then - return pdfstring(tk) - elseif fk == "unicode" then - return pdfunicode(tk) - else - return tk - end - end - -else - - get_flagged = function(t,f,k) - return t[k] - end - -end - --- We need to convert the string from utf16 although there is no way to --- check if we have a regular string starting with a bom. So, we have --- na dilemma here: a pdf doc encoded string can be invalid utf. - --- <hex encoded> : implicit 0 appended if odd --- (byte encoded) : \( \) \\ escaped --- --- <FE><FF> : utf16be --- --- \r \r \t \b \f \( \) \\ \NNN and \<newline> : append next line --- --- the getString function gives back bytes so we don't need to worry about --- the hex aspect. - -local some_dictionary -local some_array -local some_stream -local some_reference - -local some_string = lpdf.frombytes - -local function get_value(document,t,key) - if not key then - return - end - local value = t[key] - if not value then - return - end - if type(value) ~= "table" then - return value - end - -- we can assume names to be simple and strings to be tables - local kind = value[1] - if kind == name_object_code then - return value[2] - elseif kind == string_object_code then - return some_string(value[2],value[3]) - elseif kind == array_object_code then - return some_array(value[2],document) - elseif kind == dictionary_object_code then - return some_dictionary(value[2],document) - elseif kind == stream_object_code then - return some_stream(value,document) - elseif kind == reference_object_code then - return some_reference(value,document) - end - return value -end - -some_dictionary = function (d,document) - local f = dictionarytotable(d,true) - local t = setmetatable({ __raw__ = f, __type__ = dictionary_object_code }, { - __index = function(t,k) - return get_value(document,f,k) - end, - __call = function(t,k) - return get_flagged(t,f,k) - end, - } ) - return t, "dictionary" -end - -some_array = function (a,document) - local f = arraytotable(a,true) - local n = #f - local t = setmetatable({ __raw__ = f, __type__ = array_object_code, n = n }, { - __index = function(t,k) - return get_value(document,f,k) - end, - __call = function(t,k) - return get_flagged(t,f,k) - end, - __len = function(t,k) - return n - end, - } ) - return t, "array" -end - -some_stream = function(s,d,document) - local f = dictionarytotable(d,true) - local t = setmetatable({ __raw__ = f, __type__ = stream_object_code }, { - __index = function(t,k) - return get_value(document,f,k) - end, - __call = function(t,raw) - if raw == false then - return readwholestream(s,false) -- original - else - return readwholestream(s,true) -- uncompressed - end - end, - } ) - return t, "stream" -end - -some_reference = function(r,document) - local objnum = r[3] - local cached = document.__cache__[objnum] - if not cached then - local kind, object, b, c = getfromreference(r[2]) - if kind == dictionary_object_code then - cached = some_dictionary(object,document) - elseif kind == array_object_code then - cached = some_array(object,document) - elseif kind == stream_object_code then - cached = some_stream(object,b,document) - else - cached = { kind, object, b, c } - -- really cache this? - end - document.__cache__[objnum] = cached - document.__xrefs__[cached] = objnum - end - return cached -end - -local resolvers = { } -lpdf_epdf.resolvers = resolvers - -local function resolve(document,k) - local resolver = resolvers[k] - if resolver then - local entry = resolver(document) - document[k] = entry - return entry - end -end - -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,2 do - target[Names[i]] = Names[i+1] - end - else - local Kids = n.Kids - if Kids then - for i=1,#Kids 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 do - target = getkids(document,Kids[i],target) - end - elseif target then - target[#target+1] = n - else - target = { n } - end - return target - end -end - -function resolvers.destinations(document) - local Names = document.Catalog.Names - return getnames(document,Names and Names.Dests) -end - -function resolvers.javascripts(document) - local Names = document.Catalog.Names - return getnames(document,Names and Names.JavaScript) -end - -function resolvers.widgets(document) - local Names = document.Catalog.AcroForm - return Names and Names.Fields -end - -function resolvers.embeddedfiles(document) - local Names = document.Catalog.Names - return getnames(document,Names and Names.EmbeddedFiles) -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 [ ] --- >> --- >> - -function resolvers.layers(document) - local properties = document.Catalog.OCProperties - if properties then - local layers = properties.OCGs - if layers then - local t = { } - for i=1,#layers do - local layer = layers[i] - t[i] = layer.Name - end - -- t.n = n - return t - end - end -end - -function resolvers.structure(document) - -- this might become a tree - return document.Catalog.StructTreeRoot -end - -function resolvers.pages(document) - local __data__ = document.__data__ - local __xrefs__ = document.__xrefs__ - local __cache__ = document.__cache__ - -- - local nofpages = document.nofpages - local pages = { } - local rawpages = pagestotable(__data__) - document.pages = pages - -- - for pagenumber=1,nofpages do - local rawpagedata = rawpages[pagenumber] - local pagereference = rawpagedata[3] - local pageobject = rawpagedata[1] - local pagedata = some_dictionary(pageobject,document) - if pagedata and pageobject then - pagedata.number = pagenumber - pagedata.MediaBox = getbox(pageobject,"MediaBox") - pagedata.CropBox = getbox(pageobject,"CropBox") - pagedata.BleedBox = getbox(pageobject,"BleedBox") - pagedata.ArtBox = getbox(pageobject,"ArtBox") - pagedata.TrimBox = getbox(pageobject,"TrimBox") - pages[pagenumber] = pagedata - __xrefs__[pagedata] = pagereference - __cache__[pagereference] = pagedata - else - report_epdf("missing pagedata for page %i",i) - end - end - -- - -- pages.n = nofpages - -- - return pages -end - -local loaded = { } -local nofloaded = 0 - -function lpdf_epdf.load(filename,userpassword,ownerpassword,fromstring) - local document = loaded[filename] - if not document then - statistics.starttiming(lpdf_epdf) - local __data__ - if fromstring then - __data__ = newPDF(filename,#filename) - else - __data__ = openPDF(filename) - end - if __data__ then - if userpassword and getstatus(__data__) < 0 then - unencrypt(__data__,userpassword,nil) - end - if ownerpassword and getstatus(__data__) < 0 then - unencrypt(__data__,nil,ownerpassword) - end - if getstatus(__data__) < 0 then - report_epdf("the document is encrypted, provide proper passwords",getstatus(__data__)) - __data__ = false - end - if __data__ then - document = { - filename = filename, - __cache__ = { }, - __xrefs__ = { }, - __fonts__ = { }, - __copied__ = { }, - __data__ = __data__, - } - document.Catalog = some_dictionary(getcatalog(__data__),document) - document.Info = some_dictionary(getinfo(__data__),document) - document.Trailer = some_dictionary(gettrailer(__data__),document) - -- - setmetatableindex(document,resolve) - -- - document.majorversion, document.minorversion = getversion(__data__) - -- - document.nofpages = getnofpages(__data__) - else - document = false - end - else - document = false - end - loaded[filename] = document - loaded[document] = document - statistics.stoptiming(lpdf_epdf) - -- print(statistics.elapsedtime(lpdf_epdf)) - end - return document or nil -end - -function lpdf_epdf.unload(filename) - if type(filename) == "table" then - filename = filename.filename - end - if type(filename) == "string" then - local document = loaded[filename] - if document then --- report_epdf("%04i closed: %s",nofloaded,filename) --- nofloaded = nofloaded - 1 - loaded[document] = nil - loaded[filename] = nil - end - end -end - --- for k, v in expanded(t) do - -local function expanded(t) - local function iterator(raw,k) - local k, v = next(raw,k) - if v then - return k, t[k] - end - end - return iterator, t.__raw__, nil -end - ----------.expand = expand -lpdf_epdf.expanded = expanded - --- we could resolve the text stream in one pass if we directly handle the --- font but why should we complicate things - -local spaces = lpegpatterns.whitespace^1 -local optspaces = lpegpatterns.whitespace^0 -local numchar = P("\\")/"" * (R("09")^3/function(s) return char(tonumber(s,8)) end) - + P("\\") * P(1) -local key = P("/") * C(R("AZ","az","09","__")^1) -local number = Ct(Cc("number") * (lpegpatterns.number/tonumber)) -local keyword = Ct(Cc("name") * key) -local operator = C((R("AZ","az")+P("*")+P("'")+P('"'))^1) - -local grammar = P { "start", - start = (keyword + number + V("dictionary") + V("array") + V("hexstring") + V("decstring") + spaces)^1, - keyvalue = key * optspaces * V("start"), - array = Ct(Cc("array") * P("[") * Ct(V("start")^1) * P("]")), - dictionary = Ct(Cc("dict") * P("<<") * Ct(V("keyvalue")^1) * P(">>")), - hexstring = Ct(Cc("hex") * P("<") * Cs(( 1-P(">"))^1) * P(">")), - decstring = Ct(Cc("dec") * P("(") * Cs((numchar+1-(P")"))^1) * P(")")), -- untested -} - -local operation = Ct(grammar^1 * operator) -local parser = Ct((operation + P(1))^1) - --- todo: speed this one up - -local numchar = P("\\") * (R("09")^3 + P(1)) -local number = lpegpatterns.number -local keyword = P("/") * R("AZ","az","09","__")^1 -local operator = (R("AZ","az")+P("*")+P("'")+P('"'))^1 - -local skipstart = P("BDC") + P("BMC") + P("DP") + P("MP") -local skipstop = P("EMC") -local skipkeep = P("/ActualText") - -local grammar = P { "skip", - start = keyword + number + V("dictionary") + V("array") + V("hexstring") + V("decstring") + spaces, - keyvalue = optspaces * (keyword * optspaces * V("start") * optspaces)^1, - xeyvalue = optspaces * ((keyword - skipkeep) * optspaces * V("start") * optspaces)^1, - array = P("[") * V("start")^0 * P("]"), - dictionary = P("<<") * V("keyvalue")^0 * P(">>"), - xictionary = P("<<") * V("xeyvalue")^0 * P(">>"), - hexstring = P("<") * ( 1-P(">"))^0 * P(">"), - decstring = P("(") * (numchar+1-(P")"))^0 * P(")"), - skip = (optspaces * ( keyword * optspaces * V("xictionary") * optspaces * skipstart + skipstop) / "") - + V("start") - + operator -} - -local stripper = Cs((grammar + P(1))^1) - -function lpdf_epdf.parsecontent(str) - return lpegmatch(parser,str) -end - -function lpdf_epdf.stripcontent(str) - if find(str,"EMC") then - return lpegmatch(stripper,str) - else - return str - end -end - --- beginbfrange : <start> <stop> <firstcode> --- <start> <stop> [ <firstsequence> <firstsequence> <firstsequence> ] --- beginbfchar : <code> <newcodes> - -local fromsixteen = lpdf.fromsixteen -- maybe inline the lpeg ... but not worth it - -local function f_bfchar(t,a,b) - t[tonumber(a,16)] = fromsixteen(b) -end - -local function f_bfrange_1(t,a,b,c) - print("todo 1",a,b,c) - -- c is string - -- todo t[tonumber(a,16)] = fromsixteen(b) -end - -local function f_bfrange_2(t,a,b,c) - print("todo 2",a,b,c) - -- c is table - -- todo t[tonumber(a,16)] = fromsixteen(b) -end - -local optionals = spaces^0 -local hexstring = optionals * P("<") * C((1-P(">"))^1) * P(">") -local bfchar = Carg(1) * hexstring * hexstring / f_bfchar -local bfrange = Carg(1) * hexstring * hexstring * hexstring / f_bfrange_1 - + Carg(1) * hexstring * hexstring * optionals * P("[") * Ct(hexstring^1) * optionals * P("]") / f_bfrange_2 -local fromunicode = ( - P("beginbfchar" ) * bfchar ^1 * optionals * P("endbfchar" ) + - P("beginbfrange") * bfrange^1 * optionals * P("endbfrange") + - spaces + - P(1) -)^1 * Carg(1) - -local function analyzefonts(document,resources) -- unfinished, see mtx-pdf for better code - local fonts = document.__fonts__ - if resources then - local fontlist = resources.Font - if fontlist then - for id, data in expanded(fontlist) do - if not fonts[id] then - -- a quick hack ... I will look into it more detail if I find a real - -- -application for it - local tounicode = data.ToUnicode() - if tounicode then - tounicode = lpegmatch(fromunicode,tounicode,1,{}) - end - fonts[id] = { - tounicode = type(tounicode) == "table" and tounicode or { } - } - setmetatableindex(fonts[id],"self") - end - end - end - end - return fonts -end - -local more = 0 -local unic = nil -- cheaper than passing each time as Carg(1) - -local p_hex_to_utf = C(4) / function(s) -- needs checking ! - local now = tonumber(s,16) - if more > 0 then - now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong - more = 0 - return unic[now] or utfchar(now) - elseif now >= 0xD800 and now <= 0xDBFF then - more = now - -- return "" - else - return unic[now] or utfchar(now) - end -end - -local p_dec_to_utf = C(1) / function(s) -- needs checking ! - local now = byte(s) - return unic[now] or utfchar(now) -end - -local p_hex_to_utf = P(true) / function() more = 0 end * Cs(p_hex_to_utf^1) -local p_dec_to_utf = P(true) / function() more = 0 end * Cs(p_dec_to_utf^1) - -function lpdf_epdf.getpagecontent(document,pagenumber) - - local page = document.pages[pagenumber] - - if not page then - return - end - - local fonts = analyzefonts(document,page.Resources) - - local content = page.Contents() or "" - local list = lpegmatch(parser,content) - local font = nil - -- local unic = nil - - for i=1,#list do - local entry = list[i] - local size = #entry - local operator = entry[size] - if operator == "Tf" then - font = fonts[entry[1]] - unic = font.tounicode - elseif operator == "TJ" then -- { array, TJ } - local list = entry[1] - for i=1,#list do - local li = list[i] - if type(li) == "table" then - if li[1] == "hex" then - list[i] = lpegmatch(p_hex_to_utf,li[2]) - else - list[i] = lpegmatch(p_dec_to_utf,li[2]) - end - else - -- kern - end - end - elseif operator == "Tj" or operator == "'" or operator == '"' then -- { string, Tj } { string, ' } { n, m, string, " } - local list = entry[size-1] - if list[1] == "hex" then - list[2] = lpegmatch(p_hex_to_utf,li[2]) - else - list[2] = lpegmatch(p_dec_to_utf,li[2]) - end - end - end - - unic = nil -- can be collected - - return list - -end - --- This is also an experiment. When I really need it I can improve it, for instance --- with proper position calculating. It might be usefull for some search or so. - -local softhyphen = utfchar(0xAD) .. "$" -local linefactor = 1.3 - -function lpdf_epdf.contenttotext(document,list) -- maybe signal fonts - local last_y = 0 - local last_f = 0 - local text = { } - local last = 0 - - for i=1,#list do - local entry = list[i] - local size = #entry - local operator = entry[size] - if operator == "Tf" then - last_f = entry[2] - elseif operator == "TJ" then - local list = entry[1] - for i=1,#list do - local li = list[i] - if type(li) == "string" then - last = last + 1 - text[last] = li - elseif li < -50 then - last = last + 1 - text[last] = " " - end - end - line = concat(list) - elseif operator == "Tj" then - last = last + 1 - text[last] = entry[size-1] - elseif operator == "cm" or operator == "Tm" then - local ty = entry[6] - local dy = abs(last_y - ty) - if dy > linefactor*last_f then - if last > 0 then - if find(text[last],softhyphen,1,true) then - -- ignore - else - last = last + 1 - text[last] = "\n" - end - end - end - last_y = ty - end - end - - return concat(text) -end - -function lpdf_epdf.getstructure(document,list) -- just a test - local depth = 0 - for i=1,#list do - local entry = list[i] - local size = #entry - local operator = entry[size] - if operator == "BDC" then - report_epdf("%w%s : %s",depth,entry[1] or "?",entry[2] and entry[2].MCID or "?") - depth = depth + 1 - elseif operator == "EMC" then - depth = depth - 1 - elseif operator == "TJ" then - local list = entry[1] - for i=1,#list do - local li = list[i] - if type(li) == "string" then - report_epdf("%w > %s",depth,li) - elseif li < -50 then - report_epdf("%w >",depth,li) - end - end - elseif operator == "Tj" then - report_epdf("%w > %s",depth,entry[size-1]) - end - end -end - -if img then do - - -- This can be made a bit faster (just get raw data and pass it) but I will - -- do that later. In the end the benefit is probably neglectable. - - local recompress = false - local stripmarked = false - - local copydictionary = nil - local copyarray = nil - - local pdfreserveobject = lpdf.reserveobject - local shareobjectreference = lpdf.shareobjectreference - local pdfflushobject = lpdf.flushobject - local pdfflushstreamobject = lpdf.flushstreamobject - local pdfreference = lpdf.reference - local pdfconstant = lpdf.constant - local pdfarray = lpdf.array - local pdfdictionary = lpdf.dictionary - local pdfnull = lpdf.null - local pdfliteral = lpdf.literal - - local report = logs.reporter("backend","xobjects") - - local factor = 65536 / (7200/7227) -- 1/number.dimenfactors.bp - - local createimage = images.create - - directives.register("graphics.pdf.recompress", function(v) recompress = v end) - directives.register("graphics.pdf.stripmarked", function(v) stripmarked = v end) - - local function scaledbbox(b) - return { b[1]*factor, b[2]*factor, b[3]*factor, b[4]*factor } - end - - local codecs = { - ASCIIHexDecode = true, - ASCII85Decode = true, - RunLengthDecode = true, - FlateDecode = true, - LZWDecode = true, - } - - local function deepcopyobject(xref,copied,value) - -- no need for tables, just nested loop with obj - local objnum = xref[value] - if objnum then - local usednum = copied[objnum] - if usednum then - -- report("%s object %i is reused",kind,objnum) - else - usednum = pdfreserveobject() - copied[objnum] = usednum - local entry = value - local kind = entry.__type__ - if kind == array_object_code then - local a = copyarray(xref,copied,entry) - pdfflushobject(usednum,tostring(a)) - elseif kind == dictionary_object_code then - local d = copydictionary(xref,copied,entry) - pdfflushobject(usednum,tostring(d)) - elseif kind == stream_object_code then - local d = copydictionary(xref,copied,entry) - local filter = d.Filter - if filter and codecs[filter] and recompress then - -- recompress - d.Filter = nil - d.Length = nil - d.DecodeParms = nil -- relates to filter - d.DL = nil -- needed? - local s = entry() -- get uncompressed stream - pdfflushstreamobject(s,d,true,usednum) -- compress stream - else - -- keep as-is, even Length which indicates the - -- decompressed length - local s = entry(false) -- get compressed stream - -- pdfflushstreamobject(s,d,false,usednum,true) -- don't compress stream - pdfflushstreamobject(s,d,"raw",usednum) -- don't compress stream - end - else - local t = type(value) - if t == "string" then - value = pdfconstant(value) - elseif t == "table" then - local kind = value[1] - local entry = value[2] - if kind == name_object_code then - value = pdfconstant(entry) - elseif kind == string_object_code then - value = pdfliteral(entry,value[3]) - elseif kind == null_object_code then - value = pdfnull() - elseif kind == reference_object_code then - value = deepcopyobject(xref,copied,entry) - else - value = tostring(entry) - end - end - pdfflushobject(usednum,value) - end - end - return pdfreference(usednum) - elseif kind == stream_object_code then - report("stream not done: %s", objectcodes[kind] or "?") - else - report("object not done: %s", objectcodes[kind] or "?") - end - end - - local function copyobject(xref,copied,object,key,value) - if not value then - value = object.__raw__[key] - end - local t = type(value) - if t == "string" then - return pdfconstant(value) - elseif t ~= "table" then - return value - end - local kind = value[1] - if kind == name_object_code then - return pdfconstant(value[2]) - elseif kind == string_object_code then - return pdfliteral(value[2],value[3]) - elseif kind == array_object_code then - return copyarray(xref,copied,object[key]) - elseif kind == dictionary_object_code then - return copydictionary(xref,copied,object[key]) - elseif kind == null_object_code then - return pdfnull() - elseif kind == reference_object_code then - -- expand - return deepcopyobject(xref,copied,object[key]) - else - report("weird: %s", objecttypes[kind] or "?") - end - end - - copyarray = function (xref,copied,object) - local target = pdfarray() - local source = object.__raw__ - for i=1,#source do - target[i] = copyobject(xref,copied,object,i,source[i]) - end - return target - end - - local plugins = nil - - -- Sorting the hash slows down upto 5% bit but it is still as fast as the C - -- code. We could loop over the index instead but sorting might be nicer in - -- the end. - - copydictionary = function (xref,copied,object) - local target = pdfdictionary() - local source = object.__raw__ - -- for key, value in next, source do - for key, value in sortedhash(source) do - if plugins then - local p = plugins[key] - if p then - target[key] = p(xref,copied,object,key,value,copyobject) -- maybe a table of methods - else - target[key] = copyobject(xref,copied,object,key,value) - end - else - target[key] = copyobject(xref,copied,object,key,value) - end - end - return target - end - - -- local function copyresources(pdfdoc,xref,copied,pagedata) - -- local Resources = pagedata.Resources - -- if Resources then - -- local r = pdfreserveobject() - -- local d = copydictionary(xref,copied,Resources) - -- pdfflushobject(r,tostring(d)) - -- return pdfreference(r) - -- end - -- end - - local function copyresources(pdfdoc,xref,copied,pagedata) - local Resources = pagedata.Resources - -- - -- -- This needs testing: - -- - -- if not Resources then - -- local Parent = page.Parent - -- while (Parent and (Parent.__type__ == dictionary_object_code or Parent.__type__ == reference_object_code) do - -- Resources = Parent.Resources - -- if Resources then - -- break - -- end - -- Parent = Parent.Parent - -- end - -- end - if Resources then - local d = copydictionary(xref,copied,Resources) - return shareobjectreference(d) - end - end - - local openpdf = lpdf_epdf.load - local closepdf = lpdf_epdf.unload - - local function newpdf(str,userpassword,ownerpassword) - return openpdf(str,userpassword,ownerpassword,true) - end - - local function querypdf(pdfdoc,pagenumber) - if pdfdoc then - if not pagenumber then - pagenumber = 1 - end - local root = pdfdoc.Catalog - local page = pdfdoc.pages[pagenumber] - if page then - -- todo - local mediabox = page.MediaBox or { 0, 0, 0, 0 } - local cropbox = page.CropBox or mediabox - return { - filename = pdfdoc.filename, - pagenumber = pagenumber, - nofpages = pdfdoc.nofpages, - boundingbox = scaledbbox(cropbox), - cropbox = cropbox, - mediabox = mediabox, - bleedbox = page.BleedBox or cropbox, - trimbox = page.TrimBox or cropbox, - artbox = page.ArtBox or cropbox, - rotation = page.Rotate or 0, - xsize = cropbox[3] - cropbox[1], - ysize = cropbox[4] - cropbox[2], - } - end - end - end - - local function copypage(pdfdoc,pagenumber,attributes,compact,width,height,attr) - if pdfdoc then - local root = pdfdoc.Catalog - local page = pdfdoc.pages[pagenumber or 1] - local pageinfo = querypdf(pdfdoc,pagenumber) - local contents = page.Contents - local xref = pdfdoc.__xrefs__ - local copied = pdfdoc.__copied__ - if compact and lpdf_epdf.plugin then - plugins = lpdf_epdf.plugin(pdfdoc,xref,copied,page) - end - local xobject = pdfdictionary { - Type = pdfconstant("XObject"), - Subtype = pdfconstant("Form"), - FormType = 1, - Group = copyobject(xref,copied,page,"Group"), - LastModified = copyobject(xref,copied,page,"LastModified"), - Metadata = copyobject(xref,copied,page,"Metadata"), - PieceInfo = copyobject(xref,copied,page,"PieceInfo"), - Resources = copyresources(pdfdoc,xref,copied,page), - SeparationInfo = copyobject(xref,copied,page,"SeparationInfo"), - } + attr - if attributes then - for k, v in expanded(attributes) do - page[k] = v -- maybe nested - end - end - local content = "" - local nolength = nil - local ctype = contents.__type__ - -- we always recompress because image object streams can not be - -- influenced (yet) - if ctype == stream_object_code then - if stripmarked then - content = contents() -- uncompressed - local stripped = lpdf_epdf.stripcontent(content) - if stripped ~= content then - -- report("%i bytes stripped on page %i",#content-#stripped,pagenumber or 1) - content = stripped - end - elseif recompress then - content = contents() -- uncompressed - else - local Filter = copyobject(xref,copied,contents,"Filter") - local Length = copyobject(xref,copied,contents,"Length") - if Length and Filter then - nolength = true - xobject.Length = Length - xobject.Filter = Filter - content = contents(false) -- uncompressed - else - content = contents() -- uncompressed - end - end - elseif ctype == array_object_code then - content = { } - for i=1,#contents do - content[i] = contents[i]() -- uncompressed - end - content = concat(content," ") - end - -- still not nice: we double wrap now - plugins = nil - local rotation = pageinfo.rotation - local boundingbox = pageinfo.boundingbox - local transform = nil - if rotation == 90 then - transform = 3 - elseif rotation == 180 then - transform = 2 - elseif rotation == 270 then - transform = 1 - elseif rotation > 1 and rotation < 4 then - transform = rotation - end - xobject.BBox = pdfarray { - boundingbox[1] * bpfactor, - boundingbox[2] * bpfactor, - boundingbox[3] * bpfactor, - boundingbox[4] * bpfactor, - } - -- maybe like bitmaps - return createimage { -- beware: can be a img.new or a dummy - bbox = boundingbox, - transform = transform, - nolength = nolength, - nobbox = true, - notype = true, - stream = content, -- todo: no compress, pass directly also length, filter etc - attr = xobject(), - kind = images.types.stream, - } - end - end - - lpdf_epdf.image = { - open = openpdf, - close = closepdf, - new = newpdf, - query = querypdf, - copy = copypage, - } - --- lpdf.injectors.pdf = function(specification) --- local d = lpdf_epdf.load(specification.filename) --- print(d) --- end - - -end end - --- local d = lpdf_epdf.load("e:/tmp/oeps.pdf") --- inspect(d) --- inspect(d.Catalog.Lang) --- inspect(d.Catalog.OCProperties.D.AS[1].Event) --- inspect(d.Catalog.Metadata()) --- inspect(d.Catalog.Pages.Kids[1]) --- inspect(d.layers) --- inspect(d.pages) --- inspect(d.destinations) --- inspect(lpdf_epdf.getpagecontent(d,1)) --- inspect(lpdf_epdf.contenttotext(document,lpdf_epdf.getpagecontent(d,1))) --- inspect(lpdf_epdf.getstructure(document,lpdf_epdf.getpagecontent(d,1))) |