diff options
Diffstat (limited to 'tex/context/base/lpdf-ini.lua')
-rw-r--r-- | tex/context/base/lpdf-ini.lua | 1644 |
1 files changed, 822 insertions, 822 deletions
diff --git a/tex/context/base/lpdf-ini.lua b/tex/context/base/lpdf-ini.lua index cd601f21f..77ccd85fc 100644 --- a/tex/context/base/lpdf-ini.lua +++ b/tex/context/base/lpdf-ini.lua @@ -1,822 +1,822 @@ -if not modules then modules = { } end modules ['lpdf-ini'] = { - 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 setmetatable, getmetatable, type, next, tostring, tonumber, rawset = setmetatable, getmetatable, type, next, tostring, tonumber, rawset -local char, byte, format, gsub, concat, match, sub, gmatch = string.char, string.byte, string.format, string.gsub, table.concat, string.match, string.sub, string.gmatch -local utfchar, utfvalues = utf.char, utf.values -local sind, cosd = math.sind, math.cosd -local lpegmatch, P, C, R, S, Cc, Cs = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs -local formatters = string.formatters - -local pdfreserveobject = pdf.reserveobj -local pdfimmediateobject = pdf.immediateobj -local pdfdeferredobject = pdf.obj -local pdfreferenceobject = pdf.refobj - -local trace_finalizers = false trackers.register("backend.finalizers", function(v) trace_finalizers = v end) -local trace_resources = false trackers.register("backend.resources", function(v) trace_resources = v end) -local trace_objects = false trackers.register("backend.objects", function(v) trace_objects = v end) -local trace_detail = false trackers.register("backend.detail", function(v) trace_detail = v end) - -local report_objects = logs.reporter("backend","objects") -local report_finalizing = logs.reporter("backend","finalizing") - -local backends = backends - -backends.pdf = backends.pdf or { - comment = "backend for directly generating pdf output", - nodeinjections = { }, - codeinjections = { }, - registrations = { }, - tables = { }, -} - -lpdf = lpdf or { } -local lpdf = lpdf - -local function tosixteen(str) -- an lpeg might be faster (no table) - if not str or str == "" then - return "<feff>" -- not () as we want an indication that it's unicode - else - local r, n = { "<feff" }, 1 - for b in utfvalues(str) do - n = n + 1 - if b < 0x10000 then - r[n] = format("%04x",b) - else - r[n] = format("%04x%04x",b/1024+0xD800,b%1024+0xDC00) - end - end - n = n + 1 - r[n] = ">" - return concat(r) - end -end - -lpdf.tosixteen = tosixteen - --- lpeg is some 5 times faster than gsub (in test) on escaping - --- local escapes = { --- ["\\"] = "\\\\", --- ["/"] = "\\/", ["#"] = "\\#", --- ["<"] = "\\<", [">"] = "\\>", --- ["["] = "\\[", ["]"] = "\\]", --- ["("] = "\\(", [")"] = "\\)", --- } --- --- local escaped = Cs(Cc("(") * (S("\\/#<>[]()")/escapes + P(1))^0 * Cc(")")) --- --- local function toeight(str) --- if not str or str == "" then --- return "()" --- else --- return lpegmatch(escaped,str) --- end --- end --- --- -- no need for escaping .. just use unicode instead - --- \0 \t \n \r \f <space> ( ) [ ] { } / % - -local function toeight(str) - return "(" .. str .. ")" -end - -lpdf.toeight = toeight - ---~ local escaped = lpeg.Cs((lpeg.S("\0\t\n\r\f ()[]{}/%")/function(s) return format("#%02X",byte(s)) end + lpeg.P(1))^0) - ---~ local function cleaned(str) ---~ return (str and str ~= "" and lpegmatch(escaped,str)) or "" ---~ end - ---~ lpdf.cleaned = cleaned -- not public yet - -local function merge_t(a,b) - local t = { } - for k,v in next, a do t[k] = v end - for k,v in next, b do t[k] = v end - return setmetatable(t,getmetatable(a)) -end - -local f_key_value = formatters["/%s %s"] -local f_key_dictionary = formatters["/%s << % t >>"] -local f_dictionary = formatters["<< % t >>"] -local f_key_array = formatters["/%s [ % t ]"] -local f_array = formatters["[ % t ]"] - -local tostring_a, tostring_d - -tostring_d = function(t,contentonly,key) - if not next(t) then - if contentonly then - return "" - else - return "<< >>" - end - else - local r, rn = { }, 0 - for k, v in next, t do - rn = rn + 1 - local tv = type(v) - if tv == "string" then - r[rn] = f_key_value(k,toeight(v)) - elseif tv == "unicode" then - r[rn] = f_key_value(k,tosixteen(v)) - elseif tv == "table" then - local mv = getmetatable(v) - if mv and mv.__lpdftype then - r[rn] = f_key_value(k,tostring(v)) - elseif v[1] then - r[rn] = f_key_value(k,tostring_a(v)) - else - r[rn] = f_key_value(k,tostring_d(v)) - end - else - r[rn] = f_key_value(k,tostring(v)) - end - end - if contentonly then - return concat(r," ") - elseif key then - return f_key_dictionary(key,r) - else - return f_dictionary(r) - end - end -end - -tostring_a = function(t,contentonly,key) - local tn = #t - if tn == 0 then - if contentonly then - return "" - else - return "[ ]" - end - else - local r = { } - for k=1,tn do - local v = t[k] - local tv = type(v) - if tv == "string" then - r[k] = toeight(v) - elseif tv == "unicode" then - r[k] = tosixteen(v) - elseif tv == "table" then - local mv = getmetatable(v) - local mt = mv and mv.__lpdftype - if mt then - r[k] = tostring(v) - elseif v[1] then - r[k] = tostring_a(v) - else - r[k] = tostring_d(v) - end - else - r[k] = tostring(v) - end - end - if contentonly then - return concat(r, " ") - elseif key then - return f_key_array(key,r) - else - return f_array(r) - end - end -end - -local tostring_x = function(t) return concat(t, " ") end -local tostring_s = function(t) return toeight(t[1]) end -local tostring_u = function(t) return tosixteen(t[1]) end -local tostring_n = function(t) return tostring(t[1]) end -- tostring not needed -local tostring_c = function(t) return t[1] end -- already prefixed (hashed) -local tostring_z = function() return "null" end -local tostring_t = function() return "true" end -local tostring_f = function() return "false" end -local tostring_r = function(t) local n = t[1] return n and n > 0 and (n .. " 0 R") or "NULL" end - -local tostring_v = function(t) - local s = t[1] - if type(s) == "table" then - return concat(s,"") - else - return s - end -end - -local function value_x(t) return t end -- the call is experimental -local function value_s(t,key) return t[1] end -- the call is experimental -local function value_u(t,key) return t[1] end -- the call is experimental -local function value_n(t,key) return t[1] end -- the call is experimental -local function value_c(t) return sub(t[1],2) end -- the call is experimental -local function value_d(t) return tostring_d(t,true) end -- the call is experimental -local function value_a(t) return tostring_a(t,true) end -- the call is experimental -local function value_z() return nil end -- the call is experimental -local function value_t(t) return t.value or true end -- the call is experimental -local function value_f(t) return t.value or false end -- the call is experimental -local function value_r() return t[1] or 0 end -- the call is experimental -- NULL -local function value_v() return t[1] end -- the call is experimental - -local function add_x(t,k,v) rawset(t,k,tostring(v)) end - -local mt_x = { __lpdftype = "stream", __tostring = tostring_x, __call = value_x, __newindex = add_x } -local mt_d = { __lpdftype = "dictionary", __tostring = tostring_d, __call = value_d } -local mt_a = { __lpdftype = "array", __tostring = tostring_a, __call = value_a } -local mt_u = { __lpdftype = "unicode", __tostring = tostring_u, __call = value_u } -local mt_s = { __lpdftype = "string", __tostring = tostring_s, __call = value_s } -local mt_n = { __lpdftype = "number", __tostring = tostring_n, __call = value_n } -local mt_c = { __lpdftype = "constant", __tostring = tostring_c, __call = value_c } -local mt_z = { __lpdftype = "null", __tostring = tostring_z, __call = value_z } -local mt_t = { __lpdftype = "true", __tostring = tostring_t, __call = value_t } -local mt_f = { __lpdftype = "false", __tostring = tostring_f, __call = value_f } -local mt_r = { __lpdftype = "reference", __tostring = tostring_r, __call = value_r } -local mt_v = { __lpdftype = "verbose", __tostring = tostring_v, __call = value_v } - -local function pdfstream(t) -- we need to add attributes - if t then - for i=1,#t do - t[i] = tostring(t[i]) - end - end - return setmetatable(t or { },mt_x) -end - -local function pdfdictionary(t) - return setmetatable(t or { },mt_d) -end - -local function pdfarray(t) - if type(t) == "string" then - return setmetatable({ t },mt_a) - else - return setmetatable(t or { },mt_a) - end -end - -local function pdfstring(str,default) - return setmetatable({ str or default or "" },mt_s) -end - -local function pdfunicode(str,default) - return setmetatable({ str or default or "" },mt_u) -end - -local cache = { } -- can be weak - -local function pdfnumber(n,default) -- 0-10 - n = n or default - local c = cache[n] - if not c then - c = setmetatable({ n },mt_n) - -- cache[n] = c -- too many numbers - end - return c -end - -for i=-1,9 do cache[i] = pdfnumber(i) end - -local cache = { } -- can be weak - -local forbidden, replacements = "\0\t\n\r\f ()[]{}/%%#\\", { } -- table faster than function - -for s in gmatch(forbidden,".") do - replacements[s] = format("#%02x",byte(s)) -end - -local escaped = Cs(Cc("/") * (S(forbidden)/replacements + P(1))^0) - -local function pdfconstant(str,default) - str = str or default or "" - local c = cache[str] - if not c then - -- c = setmetatable({ "/" .. str },mt_c) - c = setmetatable({ lpegmatch(escaped,str) },mt_c) - cache[str] = c - end - return c -end - -local p_null = { } setmetatable(p_null, mt_z) -local p_true = { } setmetatable(p_true, mt_t) -local p_false = { } setmetatable(p_false,mt_f) - -local function pdfnull() - return p_null -end - ---~ print(pdfboolean(false),pdfboolean(false,false),pdfboolean(false,true)) ---~ print(pdfboolean(true),pdfboolean(true,false),pdfboolean(true,true)) ---~ print(pdfboolean(nil,true),pdfboolean(nil,false)) - -local function pdfboolean(b,default) - if type(b) == "boolean" then - return b and p_true or p_false - else - return default and p_true or p_false - end -end - -local function pdfreference(r) - return setmetatable({ r or 0 },mt_r) -end - -local function pdfverbose(t) -- maybe check for type - return setmetatable({ t or "" },mt_v) -end - -lpdf.stream = pdfstream -- THIS WILL PROBABLY CHANGE -lpdf.dictionary = pdfdictionary -lpdf.array = pdfarray -lpdf.string = pdfstring -lpdf.unicode = pdfunicode -lpdf.number = pdfnumber -lpdf.constant = pdfconstant -lpdf.null = pdfnull -lpdf.boolean = pdfboolean -lpdf.reference = pdfreference -lpdf.verbose = pdfverbose - --- n = pdf.obj(n, str) --- n = pdf.obj(n, "file", filename) --- n = pdf.obj(n, "stream", streamtext, attrtext) --- n = pdf.obj(n, "streamfile", filename, attrtext) - --- we only use immediate objects - --- todo: tracing - -local names, cache = { }, { } - -function lpdf.reserveobject(name) - if name == "annot" then - -- catch misuse - return pdfreserveobject("annot") - else - local r = pdfreserveobject() - if name then - names[name] = r - if trace_objects then - report_objects("reserving number %a under name %a",r,name) - end - elseif trace_objects then - report_objects("reserving number %a",r) - end - return r - end -end - -function lpdf.reserveannotation() - return pdfreserveobject("annot") -end - --- lpdf.immediateobject = pdfimmediateobject --- lpdf.deferredobject = pdfdeferredobject --- lpdf.object = pdfdeferredobject --- lpdf.referenceobject = pdfreferenceobject - -lpdf.pagereference = pdf.pageref or tex.pdfpageref -lpdf.registerannotation = pdf.registerannot - -function lpdf.delayedobject(data) -- we will get rid of this one - local n = pdfdeferredobject(data) - pdfreferenceobject(n) - return n -end - -function lpdf.flushobject(name,data) - if data then - local named = names[name] - if named then - if not trace_objects then - elseif trace_detail then - report_objects("flushing data to reserved object with name %a, data: %S",name,data) - else - report_objects("flushing data to reserved object with name %a",name) - end - return pdfimmediateobject(named,tostring(data)) - else - if not trace_objects then - elseif trace_detail then - report_objects("flushing data to reserved object with number %s, data: %S",name,data) - else - report_objects("flushing data to reserved object with number %s",name) - end - return pdfimmediateobject(name,tostring(data)) - end - else - if trace_objects and trace_detail then - report_objects("flushing data: %S",name) - end - return pdfimmediateobject(tostring(name)) - end -end - - -function lpdf.flushstreamobject(data,dict,compressed) -- default compressed - if trace_objects then - report_objects("flushing stream object of %s bytes",#data) - end - local dtype = type(dict) - return pdfdeferredobject { - immediate = true, - compresslevel = compressed == false and 0 or nil, - type = "stream", - string = data, - attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil, - } -end - -function lpdf.flushstreamfileobject(filename,dict,compressed) -- default compressed - if trace_objects then - report_objects("flushing stream file object %a",filename) - end - local dtype = type(dict) - return pdfdeferredobject { - immediate = true, - compresslevel = compressed == false and 0 or nil, - type = "stream", - file = filename, - attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil, - } -end - -local shareobjectcache, shareobjectreferencecache = { }, { } - -function lpdf.shareobject(content) - if content == nil then - -- invalid object not created - else - content = tostring(content) - local o = shareobjectcache[content] - if not o then - o = pdfimmediateobject(content) - shareobjectcache[content] = o - end - return o - end -end - -function lpdf.shareobjectreference(content) - if content == nil then - -- invalid object not created - else - content = tostring(content) - local r = shareobjectreferencecache[content] - if not r then - local o = shareobjectcache[content] - if not o then - o = pdfimmediateobject(content) - shareobjectcache[content] = o - end - r = pdfreference(o) - shareobjectreferencecache[content] = r - end - return r - end -end - ---~ local d = lpdf.dictionary() ---~ local e = lpdf.dictionary { ["e"] = "abc", x = lpdf.dictionary { ["f"] = "ABC" } } ---~ local f = lpdf.dictionary { ["f"] = "ABC" } ---~ local a = lpdf.array { lpdf.array { lpdf.string("xxx") } } - ---~ print(a) ---~ os.exit() - ---~ d["test"] = lpdf.string ("test") ---~ d["more"] = "more" ---~ d["bool"] = true ---~ d["numb"] = 1234 ---~ d["oeps"] = lpdf.dictionary { ["hans"] = "ton" } ---~ d["whow"] = lpdf.array { lpdf.string("ton") } - ---~ a[#a+1] = lpdf.string("xxx") ---~ a[#a+1] = lpdf.string("yyy") - ---~ d.what = a - ---~ print(e) - ---~ local d = lpdf.dictionary() ---~ d["abcd"] = { 1, 2, 3, "test" } ---~ print(d) ---~ print(d()) - ---~ local d = lpdf.array() ---~ d[#d+1] = 1 ---~ d[#d+1] = 2 ---~ d[#d+1] = 3 ---~ d[#d+1] = "test" ---~ print(d) - ---~ local d = lpdf.array() ---~ d[#d+1] = { 1, 2, 3, "test" } ---~ print(d) - ---~ local d = lpdf.array() ---~ d[#d+1] = { a=1, b=2, c=3, d="test" } ---~ print(d) - ---~ local s = lpdf.constant("xx") ---~ print(s) -- fails somehow ---~ print(s()) -- fails somehow - ---~ local s = lpdf.boolean(false) ---~ s.value = true ---~ print(s) ---~ print(s()) - --- three priority levels, default=2 - -local pagefinalizers, documentfinalizers = { { }, { }, { } }, { { }, { }, { } } - -local pageresources, pageattributes, pagesattributes - -local function resetpageproperties() - pageresources = pdfdictionary() - pageattributes = pdfdictionary() - pagesattributes = pdfdictionary() -end - -resetpageproperties() - -local function setpageproperties() - pdf.pageresources = pageresources () - pdf.pageattributes = pageattributes () - pdf.pagesattributes = pagesattributes() -end - -local function addtopageresources (k,v) pageresources [k] = v end -local function addtopageattributes (k,v) pageattributes [k] = v end -local function addtopagesattributes(k,v) pagesattributes[k] = v end - -lpdf.addtopageresources = addtopageresources -lpdf.addtopageattributes = addtopageattributes -lpdf.addtopagesattributes = addtopagesattributes - -local function set(where,what,f,when,comment) - if type(when) == "string" then - when, comment = 2, when - elseif not when then - when = 2 - end - local w = where[when] - w[#w+1] = { f, comment } - if trace_finalizers then - report_finalizing("%s set: [%s,%s]",what,when,#w) - end -end - -local function run(where,what) - if trace_finalizers then - report_finalizing("start backend, category %a, n %a",what,#where) - end - for i=1,#where do - local w = where[i] - for j=1,#w do - local wj = w[j] - if trace_finalizers then - report_finalizing("%s finalizer: [%s,%s] %s",what,i,j,wj[2] or "") - end - wj[1]() - end - end - if trace_finalizers then - report_finalizing("stop finalizing") - end -end - -local function registerpagefinalizer(f,when,comment) - set(pagefinalizers,"page",f,when,comment) -end - -local function registerdocumentfinalizer(f,when,comment) - set(documentfinalizers,"document",f,when,comment) -end - -lpdf.registerpagefinalizer = registerpagefinalizer -lpdf.registerdocumentfinalizer = registerdocumentfinalizer - -function lpdf.finalizepage() - if not environment.initex then - -- resetpageproperties() -- maybe better before - run(pagefinalizers,"page") - setpageproperties() - resetpageproperties() -- maybe better before - end -end - -function lpdf.finalizedocument() - if not environment.initex then - run(documentfinalizers,"document") - function lpdf.finalizedocument() - report_finalizing("serious error: the document is finalized multiple times") - function lpdf.finalizedocument() end - end - end -end - -backends.pdf.codeinjections.finalizepage = lpdf.finalizepage -- will go when we have hook - ---~ callbacks.register("finish_pdfpage", lpdf.finalizepage) -callbacks.register("finish_pdffile", lpdf.finalizedocument) - --- some minimal tracing, handy for checking the order - -local function trace_set(what,key) - if trace_resources then - report_finalizing("setting key %a in %a",key,what) - end -end -local function trace_flush(what) - if trace_resources then - report_finalizing("flushing %a",what) - end -end - -lpdf.protectresources = true - -local catalog = pdfdictionary { Type = pdfconstant("Catalog") } -- nicer, but when we assign we nil the Type -local info = pdfdictionary { Type = pdfconstant("Info") } -- nicer, but when we assign we nil the Type -local names = pdfdictionary { Type = pdfconstant("Names") } -- nicer, but when we assign we nil the Type - -local function flushcatalog() if not environment.initex then trace_flush("catalog") catalog.Type = nil pdf.catalog = catalog() end end -local function flushinfo () if not environment.initex then trace_flush("info") info .Type = nil pdf.info = info () end end -local function flushnames () if not environment.initex then trace_flush("names") names .Type = nil pdf.names = names () end end - -function lpdf.addtocatalog(k,v) if not (lpdf.protectresources and catalog[k]) then trace_set("catalog",k) catalog[k] = v end end -function lpdf.addtoinfo (k,v) if not (lpdf.protectresources and info [k]) then trace_set("info", k) info [k] = v end end -function lpdf.addtonames (k,v) if not (lpdf.protectresources and names [k]) then trace_set("names", k) names [k] = v end end - -local dummy = pdfreserveobject() -- else bug in hvmd due so some internal luatex conflict - --- Some day I will implement a proper minimalized resource management. - -local r_extgstates, d_extgstates = pdfreserveobject(), pdfdictionary() local p_extgstates = pdfreference(r_extgstates) -local r_colorspaces, d_colorspaces = pdfreserveobject(), pdfdictionary() local p_colorspaces = pdfreference(r_colorspaces) -local r_patterns, d_patterns = pdfreserveobject(), pdfdictionary() local p_patterns = pdfreference(r_patterns) -local r_shades, d_shades = pdfreserveobject(), pdfdictionary() local p_shades = pdfreference(r_shades) - -local function checkextgstates () if next(d_extgstates ) then addtopageresources("ExtGState", p_extgstates ) end end -local function checkcolorspaces() if next(d_colorspaces) then addtopageresources("ColorSpace",p_colorspaces) end end -local function checkpatterns () if next(d_patterns ) then addtopageresources("Pattern", p_patterns ) end end -local function checkshades () if next(d_shades ) then addtopageresources("Shading", p_shades ) end end - -local function flushextgstates () if next(d_extgstates ) then trace_flush("extgstates") pdfimmediateobject(r_extgstates, tostring(d_extgstates )) end end -local function flushcolorspaces() if next(d_colorspaces) then trace_flush("colorspaces") pdfimmediateobject(r_colorspaces,tostring(d_colorspaces)) end end -local function flushpatterns () if next(d_patterns ) then trace_flush("patterns") pdfimmediateobject(r_patterns, tostring(d_patterns )) end end -local function flushshades () if next(d_shades ) then trace_flush("shades") pdfimmediateobject(r_shades, tostring(d_shades )) end end - -function lpdf.collectedresources() - local ExtGState = next(d_extgstates ) and p_extgstates - local ColorSpace = next(d_colorspaces) and p_colorspaces - local Pattern = next(d_patterns ) and p_patterns - local Shading = next(d_shades ) and p_shades - if ExtGState or ColorSpace or Pattern or Shading then - local collected = pdfdictionary { - ExtGState = ExtGState, - ColorSpace = ColorSpace, - Pattern = Pattern, - Shading = Shading, - -- ProcSet = pdfarray { pdfconstant("PDF") }, - } - return collected() - else - return "" - end -end - -function lpdf.adddocumentextgstate (k,v) d_extgstates [k] = v end -function lpdf.adddocumentcolorspace(k,v) d_colorspaces[k] = v end -function lpdf.adddocumentpattern (k,v) d_patterns [k] = v end -function lpdf.adddocumentshade (k,v) d_shades [k] = v end - -registerdocumentfinalizer(flushextgstates,3,"extended graphic states") -registerdocumentfinalizer(flushcolorspaces,3,"color spaces") -registerdocumentfinalizer(flushpatterns,3,"patterns") -registerdocumentfinalizer(flushshades,3,"shades") - -registerdocumentfinalizer(flushcatalog,3,"catalog") -registerdocumentfinalizer(flushinfo,3,"info") -registerdocumentfinalizer(flushnames,3,"names") -- before catalog - -registerpagefinalizer(checkextgstates,3,"extended graphic states") -registerpagefinalizer(checkcolorspaces,3,"color spaces") -registerpagefinalizer(checkpatterns,3,"patterns") -registerpagefinalizer(checkshades,3,"shades") - --- in strc-bkm: lpdf.registerdocumentfinalizer(function() structures.bookmarks.place() end,1) - -function lpdf.rotationcm(a) - local s, c = sind(a), cosd(a) - return format("%0.6f %0.6f %0.6f %0.6f 0 0 cm",c,s,-s,c) -end - --- ! -> universaltime - -local timestamp = os.date("%Y-%m-%dT%X") .. os.timezone(true) - -function lpdf.timestamp() - return timestamp -end - -function lpdf.pdftimestamp(str) - local Y, M, D, h, m, s, Zs, Zh, Zm = match(str,"^(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$") - return Y and format("D:%s%s%s%s%s%s%s%s'%s'",Y,M,D,h,m,s,Zs,Zh,Zm) -end - -function lpdf.id() - return format("%s.%s",tex.jobname,timestamp) -end - -function lpdf.checkedkey(t,key,variant) - local pn = t and t[key] - if pn then - local tn = type(pn) - if tn == variant then - if variant == "string" then - return pn ~= "" and pn or nil - elseif variant == "table" then - return next(pn) and pn or nil - else - return pn - end - elseif tn == "string" and variant == "number" then - return tonumber(pn) - end - end -end - -function lpdf.checkedvalue(value,variant) -- code not shared - if value then - local tv = type(value) - if tv == variant then - if variant == "string" then - return value ~= "" and value - elseif variant == "table" then - return next(value) and value - else - return value - end - elseif tv == "string" and variant == "number" then - return tonumber(value) - end - end -end - -function lpdf.limited(n,min,max,default) - if not n then - return default - else - n = tonumber(n) - if not n then - return default - elseif n > max then - return max - elseif n < min then - return min - else - return n - end - end -end - --- lpdf.addtoinfo("ConTeXt.Version", tex.contextversiontoks) --- lpdf.addtoinfo("ConTeXt.Time", os.date("%Y.%m.%d %H:%M")) -- :%S --- lpdf.addtoinfo("ConTeXt.Jobname", environment.jobname) --- lpdf.addtoinfo("ConTeXt.Url", "www.pragma-ade.com") - -if not pdfreferenceobject then - - local delayed = { } - - local function flush() - local n = 0 - for k,v in next, delayed do - pdfimmediateobject(k,v) - n = n + 1 - end - if trace_objects then - report_objects("%s objects flushed",n) - end - delayed = { } - end - - lpdf.registerdocumentfinalizer(flush,3,"objects") -- so we need a final flush too - lpdf.registerpagefinalizer (flush,3,"objects") -- somehow this lags behind .. I need to look into that some day - - function lpdf.delayedobject(data) - local n = pdfreserveobject() - delayed[n] = data - return n - end - -end +if not modules then modules = { } end modules ['lpdf-ini'] = {
+ 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 setmetatable, getmetatable, type, next, tostring, tonumber, rawset = setmetatable, getmetatable, type, next, tostring, tonumber, rawset
+local char, byte, format, gsub, concat, match, sub, gmatch = string.char, string.byte, string.format, string.gsub, table.concat, string.match, string.sub, string.gmatch
+local utfchar, utfvalues = utf.char, utf.values
+local sind, cosd = math.sind, math.cosd
+local lpegmatch, P, C, R, S, Cc, Cs = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs
+local formatters = string.formatters
+
+local pdfreserveobject = pdf.reserveobj
+local pdfimmediateobject = pdf.immediateobj
+local pdfdeferredobject = pdf.obj
+local pdfreferenceobject = pdf.refobj
+
+local trace_finalizers = false trackers.register("backend.finalizers", function(v) trace_finalizers = v end)
+local trace_resources = false trackers.register("backend.resources", function(v) trace_resources = v end)
+local trace_objects = false trackers.register("backend.objects", function(v) trace_objects = v end)
+local trace_detail = false trackers.register("backend.detail", function(v) trace_detail = v end)
+
+local report_objects = logs.reporter("backend","objects")
+local report_finalizing = logs.reporter("backend","finalizing")
+
+local backends = backends
+
+backends.pdf = backends.pdf or {
+ comment = "backend for directly generating pdf output",
+ nodeinjections = { },
+ codeinjections = { },
+ registrations = { },
+ tables = { },
+}
+
+lpdf = lpdf or { }
+local lpdf = lpdf
+
+local function tosixteen(str) -- an lpeg might be faster (no table)
+ if not str or str == "" then
+ return "<feff>" -- not () as we want an indication that it's unicode
+ else
+ local r, n = { "<feff" }, 1
+ for b in utfvalues(str) do
+ n = n + 1
+ if b < 0x10000 then
+ r[n] = format("%04x",b)
+ else
+ r[n] = format("%04x%04x",b/1024+0xD800,b%1024+0xDC00)
+ end
+ end
+ n = n + 1
+ r[n] = ">"
+ return concat(r)
+ end
+end
+
+lpdf.tosixteen = tosixteen
+
+-- lpeg is some 5 times faster than gsub (in test) on escaping
+
+-- local escapes = {
+-- ["\\"] = "\\\\",
+-- ["/"] = "\\/", ["#"] = "\\#",
+-- ["<"] = "\\<", [">"] = "\\>",
+-- ["["] = "\\[", ["]"] = "\\]",
+-- ["("] = "\\(", [")"] = "\\)",
+-- }
+--
+-- local escaped = Cs(Cc("(") * (S("\\/#<>[]()")/escapes + P(1))^0 * Cc(")"))
+--
+-- local function toeight(str)
+-- if not str or str == "" then
+-- return "()"
+-- else
+-- return lpegmatch(escaped,str)
+-- end
+-- end
+--
+-- -- no need for escaping .. just use unicode instead
+
+-- \0 \t \n \r \f <space> ( ) [ ] { } / %
+
+local function toeight(str)
+ return "(" .. str .. ")"
+end
+
+lpdf.toeight = toeight
+
+--~ local escaped = lpeg.Cs((lpeg.S("\0\t\n\r\f ()[]{}/%")/function(s) return format("#%02X",byte(s)) end + lpeg.P(1))^0)
+
+--~ local function cleaned(str)
+--~ return (str and str ~= "" and lpegmatch(escaped,str)) or ""
+--~ end
+
+--~ lpdf.cleaned = cleaned -- not public yet
+
+local function merge_t(a,b)
+ local t = { }
+ for k,v in next, a do t[k] = v end
+ for k,v in next, b do t[k] = v end
+ return setmetatable(t,getmetatable(a))
+end
+
+local f_key_value = formatters["/%s %s"]
+local f_key_dictionary = formatters["/%s << % t >>"]
+local f_dictionary = formatters["<< % t >>"]
+local f_key_array = formatters["/%s [ % t ]"]
+local f_array = formatters["[ % t ]"]
+
+local tostring_a, tostring_d
+
+tostring_d = function(t,contentonly,key)
+ if not next(t) then
+ if contentonly then
+ return ""
+ else
+ return "<< >>"
+ end
+ else
+ local r, rn = { }, 0
+ for k, v in next, t do
+ rn = rn + 1
+ local tv = type(v)
+ if tv == "string" then
+ r[rn] = f_key_value(k,toeight(v))
+ elseif tv == "unicode" then
+ r[rn] = f_key_value(k,tosixteen(v))
+ elseif tv == "table" then
+ local mv = getmetatable(v)
+ if mv and mv.__lpdftype then
+ r[rn] = f_key_value(k,tostring(v))
+ elseif v[1] then
+ r[rn] = f_key_value(k,tostring_a(v))
+ else
+ r[rn] = f_key_value(k,tostring_d(v))
+ end
+ else
+ r[rn] = f_key_value(k,tostring(v))
+ end
+ end
+ if contentonly then
+ return concat(r," ")
+ elseif key then
+ return f_key_dictionary(key,r)
+ else
+ return f_dictionary(r)
+ end
+ end
+end
+
+tostring_a = function(t,contentonly,key)
+ local tn = #t
+ if tn == 0 then
+ if contentonly then
+ return ""
+ else
+ return "[ ]"
+ end
+ else
+ local r = { }
+ for k=1,tn do
+ local v = t[k]
+ local tv = type(v)
+ if tv == "string" then
+ r[k] = toeight(v)
+ elseif tv == "unicode" then
+ r[k] = tosixteen(v)
+ elseif tv == "table" then
+ local mv = getmetatable(v)
+ local mt = mv and mv.__lpdftype
+ if mt then
+ r[k] = tostring(v)
+ elseif v[1] then
+ r[k] = tostring_a(v)
+ else
+ r[k] = tostring_d(v)
+ end
+ else
+ r[k] = tostring(v)
+ end
+ end
+ if contentonly then
+ return concat(r, " ")
+ elseif key then
+ return f_key_array(key,r)
+ else
+ return f_array(r)
+ end
+ end
+end
+
+local tostring_x = function(t) return concat(t, " ") end
+local tostring_s = function(t) return toeight(t[1]) end
+local tostring_u = function(t) return tosixteen(t[1]) end
+local tostring_n = function(t) return tostring(t[1]) end -- tostring not needed
+local tostring_c = function(t) return t[1] end -- already prefixed (hashed)
+local tostring_z = function() return "null" end
+local tostring_t = function() return "true" end
+local tostring_f = function() return "false" end
+local tostring_r = function(t) local n = t[1] return n and n > 0 and (n .. " 0 R") or "NULL" end
+
+local tostring_v = function(t)
+ local s = t[1]
+ if type(s) == "table" then
+ return concat(s,"")
+ else
+ return s
+ end
+end
+
+local function value_x(t) return t end -- the call is experimental
+local function value_s(t,key) return t[1] end -- the call is experimental
+local function value_u(t,key) return t[1] end -- the call is experimental
+local function value_n(t,key) return t[1] end -- the call is experimental
+local function value_c(t) return sub(t[1],2) end -- the call is experimental
+local function value_d(t) return tostring_d(t,true) end -- the call is experimental
+local function value_a(t) return tostring_a(t,true) end -- the call is experimental
+local function value_z() return nil end -- the call is experimental
+local function value_t(t) return t.value or true end -- the call is experimental
+local function value_f(t) return t.value or false end -- the call is experimental
+local function value_r() return t[1] or 0 end -- the call is experimental -- NULL
+local function value_v() return t[1] end -- the call is experimental
+
+local function add_x(t,k,v) rawset(t,k,tostring(v)) end
+
+local mt_x = { __lpdftype = "stream", __tostring = tostring_x, __call = value_x, __newindex = add_x }
+local mt_d = { __lpdftype = "dictionary", __tostring = tostring_d, __call = value_d }
+local mt_a = { __lpdftype = "array", __tostring = tostring_a, __call = value_a }
+local mt_u = { __lpdftype = "unicode", __tostring = tostring_u, __call = value_u }
+local mt_s = { __lpdftype = "string", __tostring = tostring_s, __call = value_s }
+local mt_n = { __lpdftype = "number", __tostring = tostring_n, __call = value_n }
+local mt_c = { __lpdftype = "constant", __tostring = tostring_c, __call = value_c }
+local mt_z = { __lpdftype = "null", __tostring = tostring_z, __call = value_z }
+local mt_t = { __lpdftype = "true", __tostring = tostring_t, __call = value_t }
+local mt_f = { __lpdftype = "false", __tostring = tostring_f, __call = value_f }
+local mt_r = { __lpdftype = "reference", __tostring = tostring_r, __call = value_r }
+local mt_v = { __lpdftype = "verbose", __tostring = tostring_v, __call = value_v }
+
+local function pdfstream(t) -- we need to add attributes
+ if t then
+ for i=1,#t do
+ t[i] = tostring(t[i])
+ end
+ end
+ return setmetatable(t or { },mt_x)
+end
+
+local function pdfdictionary(t)
+ return setmetatable(t or { },mt_d)
+end
+
+local function pdfarray(t)
+ if type(t) == "string" then
+ return setmetatable({ t },mt_a)
+ else
+ return setmetatable(t or { },mt_a)
+ end
+end
+
+local function pdfstring(str,default)
+ return setmetatable({ str or default or "" },mt_s)
+end
+
+local function pdfunicode(str,default)
+ return setmetatable({ str or default or "" },mt_u)
+end
+
+local cache = { } -- can be weak
+
+local function pdfnumber(n,default) -- 0-10
+ n = n or default
+ local c = cache[n]
+ if not c then
+ c = setmetatable({ n },mt_n)
+ -- cache[n] = c -- too many numbers
+ end
+ return c
+end
+
+for i=-1,9 do cache[i] = pdfnumber(i) end
+
+local cache = { } -- can be weak
+
+local forbidden, replacements = "\0\t\n\r\f ()[]{}/%%#\\", { } -- table faster than function
+
+for s in gmatch(forbidden,".") do
+ replacements[s] = format("#%02x",byte(s))
+end
+
+local escaped = Cs(Cc("/") * (S(forbidden)/replacements + P(1))^0)
+
+local function pdfconstant(str,default)
+ str = str or default or ""
+ local c = cache[str]
+ if not c then
+ -- c = setmetatable({ "/" .. str },mt_c)
+ c = setmetatable({ lpegmatch(escaped,str) },mt_c)
+ cache[str] = c
+ end
+ return c
+end
+
+local p_null = { } setmetatable(p_null, mt_z)
+local p_true = { } setmetatable(p_true, mt_t)
+local p_false = { } setmetatable(p_false,mt_f)
+
+local function pdfnull()
+ return p_null
+end
+
+--~ print(pdfboolean(false),pdfboolean(false,false),pdfboolean(false,true))
+--~ print(pdfboolean(true),pdfboolean(true,false),pdfboolean(true,true))
+--~ print(pdfboolean(nil,true),pdfboolean(nil,false))
+
+local function pdfboolean(b,default)
+ if type(b) == "boolean" then
+ return b and p_true or p_false
+ else
+ return default and p_true or p_false
+ end
+end
+
+local function pdfreference(r)
+ return setmetatable({ r or 0 },mt_r)
+end
+
+local function pdfverbose(t) -- maybe check for type
+ return setmetatable({ t or "" },mt_v)
+end
+
+lpdf.stream = pdfstream -- THIS WILL PROBABLY CHANGE
+lpdf.dictionary = pdfdictionary
+lpdf.array = pdfarray
+lpdf.string = pdfstring
+lpdf.unicode = pdfunicode
+lpdf.number = pdfnumber
+lpdf.constant = pdfconstant
+lpdf.null = pdfnull
+lpdf.boolean = pdfboolean
+lpdf.reference = pdfreference
+lpdf.verbose = pdfverbose
+
+-- n = pdf.obj(n, str)
+-- n = pdf.obj(n, "file", filename)
+-- n = pdf.obj(n, "stream", streamtext, attrtext)
+-- n = pdf.obj(n, "streamfile", filename, attrtext)
+
+-- we only use immediate objects
+
+-- todo: tracing
+
+local names, cache = { }, { }
+
+function lpdf.reserveobject(name)
+ if name == "annot" then
+ -- catch misuse
+ return pdfreserveobject("annot")
+ else
+ local r = pdfreserveobject()
+ if name then
+ names[name] = r
+ if trace_objects then
+ report_objects("reserving number %a under name %a",r,name)
+ end
+ elseif trace_objects then
+ report_objects("reserving number %a",r)
+ end
+ return r
+ end
+end
+
+function lpdf.reserveannotation()
+ return pdfreserveobject("annot")
+end
+
+-- lpdf.immediateobject = pdfimmediateobject
+-- lpdf.deferredobject = pdfdeferredobject
+-- lpdf.object = pdfdeferredobject
+-- lpdf.referenceobject = pdfreferenceobject
+
+lpdf.pagereference = pdf.pageref or tex.pdfpageref
+lpdf.registerannotation = pdf.registerannot
+
+function lpdf.delayedobject(data) -- we will get rid of this one
+ local n = pdfdeferredobject(data)
+ pdfreferenceobject(n)
+ return n
+end
+
+function lpdf.flushobject(name,data)
+ if data then
+ local named = names[name]
+ if named then
+ if not trace_objects then
+ elseif trace_detail then
+ report_objects("flushing data to reserved object with name %a, data: %S",name,data)
+ else
+ report_objects("flushing data to reserved object with name %a",name)
+ end
+ return pdfimmediateobject(named,tostring(data))
+ else
+ if not trace_objects then
+ elseif trace_detail then
+ report_objects("flushing data to reserved object with number %s, data: %S",name,data)
+ else
+ report_objects("flushing data to reserved object with number %s",name)
+ end
+ return pdfimmediateobject(name,tostring(data))
+ end
+ else
+ if trace_objects and trace_detail then
+ report_objects("flushing data: %S",name)
+ end
+ return pdfimmediateobject(tostring(name))
+ end
+end
+
+
+function lpdf.flushstreamobject(data,dict,compressed) -- default compressed
+ if trace_objects then
+ report_objects("flushing stream object of %s bytes",#data)
+ end
+ local dtype = type(dict)
+ return pdfdeferredobject {
+ immediate = true,
+ compresslevel = compressed == false and 0 or nil,
+ type = "stream",
+ string = data,
+ attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil,
+ }
+end
+
+function lpdf.flushstreamfileobject(filename,dict,compressed) -- default compressed
+ if trace_objects then
+ report_objects("flushing stream file object %a",filename)
+ end
+ local dtype = type(dict)
+ return pdfdeferredobject {
+ immediate = true,
+ compresslevel = compressed == false and 0 or nil,
+ type = "stream",
+ file = filename,
+ attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil,
+ }
+end
+
+local shareobjectcache, shareobjectreferencecache = { }, { }
+
+function lpdf.shareobject(content)
+ if content == nil then
+ -- invalid object not created
+ else
+ content = tostring(content)
+ local o = shareobjectcache[content]
+ if not o then
+ o = pdfimmediateobject(content)
+ shareobjectcache[content] = o
+ end
+ return o
+ end
+end
+
+function lpdf.shareobjectreference(content)
+ if content == nil then
+ -- invalid object not created
+ else
+ content = tostring(content)
+ local r = shareobjectreferencecache[content]
+ if not r then
+ local o = shareobjectcache[content]
+ if not o then
+ o = pdfimmediateobject(content)
+ shareobjectcache[content] = o
+ end
+ r = pdfreference(o)
+ shareobjectreferencecache[content] = r
+ end
+ return r
+ end
+end
+
+--~ local d = lpdf.dictionary()
+--~ local e = lpdf.dictionary { ["e"] = "abc", x = lpdf.dictionary { ["f"] = "ABC" } }
+--~ local f = lpdf.dictionary { ["f"] = "ABC" }
+--~ local a = lpdf.array { lpdf.array { lpdf.string("xxx") } }
+
+--~ print(a)
+--~ os.exit()
+
+--~ d["test"] = lpdf.string ("test")
+--~ d["more"] = "more"
+--~ d["bool"] = true
+--~ d["numb"] = 1234
+--~ d["oeps"] = lpdf.dictionary { ["hans"] = "ton" }
+--~ d["whow"] = lpdf.array { lpdf.string("ton") }
+
+--~ a[#a+1] = lpdf.string("xxx")
+--~ a[#a+1] = lpdf.string("yyy")
+
+--~ d.what = a
+
+--~ print(e)
+
+--~ local d = lpdf.dictionary()
+--~ d["abcd"] = { 1, 2, 3, "test" }
+--~ print(d)
+--~ print(d())
+
+--~ local d = lpdf.array()
+--~ d[#d+1] = 1
+--~ d[#d+1] = 2
+--~ d[#d+1] = 3
+--~ d[#d+1] = "test"
+--~ print(d)
+
+--~ local d = lpdf.array()
+--~ d[#d+1] = { 1, 2, 3, "test" }
+--~ print(d)
+
+--~ local d = lpdf.array()
+--~ d[#d+1] = { a=1, b=2, c=3, d="test" }
+--~ print(d)
+
+--~ local s = lpdf.constant("xx")
+--~ print(s) -- fails somehow
+--~ print(s()) -- fails somehow
+
+--~ local s = lpdf.boolean(false)
+--~ s.value = true
+--~ print(s)
+--~ print(s())
+
+-- three priority levels, default=2
+
+local pagefinalizers, documentfinalizers = { { }, { }, { } }, { { }, { }, { } }
+
+local pageresources, pageattributes, pagesattributes
+
+local function resetpageproperties()
+ pageresources = pdfdictionary()
+ pageattributes = pdfdictionary()
+ pagesattributes = pdfdictionary()
+end
+
+resetpageproperties()
+
+local function setpageproperties()
+ pdf.pageresources = pageresources ()
+ pdf.pageattributes = pageattributes ()
+ pdf.pagesattributes = pagesattributes()
+end
+
+local function addtopageresources (k,v) pageresources [k] = v end
+local function addtopageattributes (k,v) pageattributes [k] = v end
+local function addtopagesattributes(k,v) pagesattributes[k] = v end
+
+lpdf.addtopageresources = addtopageresources
+lpdf.addtopageattributes = addtopageattributes
+lpdf.addtopagesattributes = addtopagesattributes
+
+local function set(where,what,f,when,comment)
+ if type(when) == "string" then
+ when, comment = 2, when
+ elseif not when then
+ when = 2
+ end
+ local w = where[when]
+ w[#w+1] = { f, comment }
+ if trace_finalizers then
+ report_finalizing("%s set: [%s,%s]",what,when,#w)
+ end
+end
+
+local function run(where,what)
+ if trace_finalizers then
+ report_finalizing("start backend, category %a, n %a",what,#where)
+ end
+ for i=1,#where do
+ local w = where[i]
+ for j=1,#w do
+ local wj = w[j]
+ if trace_finalizers then
+ report_finalizing("%s finalizer: [%s,%s] %s",what,i,j,wj[2] or "")
+ end
+ wj[1]()
+ end
+ end
+ if trace_finalizers then
+ report_finalizing("stop finalizing")
+ end
+end
+
+local function registerpagefinalizer(f,when,comment)
+ set(pagefinalizers,"page",f,when,comment)
+end
+
+local function registerdocumentfinalizer(f,when,comment)
+ set(documentfinalizers,"document",f,when,comment)
+end
+
+lpdf.registerpagefinalizer = registerpagefinalizer
+lpdf.registerdocumentfinalizer = registerdocumentfinalizer
+
+function lpdf.finalizepage()
+ if not environment.initex then
+ -- resetpageproperties() -- maybe better before
+ run(pagefinalizers,"page")
+ setpageproperties()
+ resetpageproperties() -- maybe better before
+ end
+end
+
+function lpdf.finalizedocument()
+ if not environment.initex then
+ run(documentfinalizers,"document")
+ function lpdf.finalizedocument()
+ report_finalizing("serious error: the document is finalized multiple times")
+ function lpdf.finalizedocument() end
+ end
+ end
+end
+
+backends.pdf.codeinjections.finalizepage = lpdf.finalizepage -- will go when we have hook
+
+--~ callbacks.register("finish_pdfpage", lpdf.finalizepage)
+callbacks.register("finish_pdffile", lpdf.finalizedocument)
+
+-- some minimal tracing, handy for checking the order
+
+local function trace_set(what,key)
+ if trace_resources then
+ report_finalizing("setting key %a in %a",key,what)
+ end
+end
+local function trace_flush(what)
+ if trace_resources then
+ report_finalizing("flushing %a",what)
+ end
+end
+
+lpdf.protectresources = true
+
+local catalog = pdfdictionary { Type = pdfconstant("Catalog") } -- nicer, but when we assign we nil the Type
+local info = pdfdictionary { Type = pdfconstant("Info") } -- nicer, but when we assign we nil the Type
+local names = pdfdictionary { Type = pdfconstant("Names") } -- nicer, but when we assign we nil the Type
+
+local function flushcatalog() if not environment.initex then trace_flush("catalog") catalog.Type = nil pdf.catalog = catalog() end end
+local function flushinfo () if not environment.initex then trace_flush("info") info .Type = nil pdf.info = info () end end
+local function flushnames () if not environment.initex then trace_flush("names") names .Type = nil pdf.names = names () end end
+
+function lpdf.addtocatalog(k,v) if not (lpdf.protectresources and catalog[k]) then trace_set("catalog",k) catalog[k] = v end end
+function lpdf.addtoinfo (k,v) if not (lpdf.protectresources and info [k]) then trace_set("info", k) info [k] = v end end
+function lpdf.addtonames (k,v) if not (lpdf.protectresources and names [k]) then trace_set("names", k) names [k] = v end end
+
+local dummy = pdfreserveobject() -- else bug in hvmd due so some internal luatex conflict
+
+-- Some day I will implement a proper minimalized resource management.
+
+local r_extgstates, d_extgstates = pdfreserveobject(), pdfdictionary() local p_extgstates = pdfreference(r_extgstates)
+local r_colorspaces, d_colorspaces = pdfreserveobject(), pdfdictionary() local p_colorspaces = pdfreference(r_colorspaces)
+local r_patterns, d_patterns = pdfreserveobject(), pdfdictionary() local p_patterns = pdfreference(r_patterns)
+local r_shades, d_shades = pdfreserveobject(), pdfdictionary() local p_shades = pdfreference(r_shades)
+
+local function checkextgstates () if next(d_extgstates ) then addtopageresources("ExtGState", p_extgstates ) end end
+local function checkcolorspaces() if next(d_colorspaces) then addtopageresources("ColorSpace",p_colorspaces) end end
+local function checkpatterns () if next(d_patterns ) then addtopageresources("Pattern", p_patterns ) end end
+local function checkshades () if next(d_shades ) then addtopageresources("Shading", p_shades ) end end
+
+local function flushextgstates () if next(d_extgstates ) then trace_flush("extgstates") pdfimmediateobject(r_extgstates, tostring(d_extgstates )) end end
+local function flushcolorspaces() if next(d_colorspaces) then trace_flush("colorspaces") pdfimmediateobject(r_colorspaces,tostring(d_colorspaces)) end end
+local function flushpatterns () if next(d_patterns ) then trace_flush("patterns") pdfimmediateobject(r_patterns, tostring(d_patterns )) end end
+local function flushshades () if next(d_shades ) then trace_flush("shades") pdfimmediateobject(r_shades, tostring(d_shades )) end end
+
+function lpdf.collectedresources()
+ local ExtGState = next(d_extgstates ) and p_extgstates
+ local ColorSpace = next(d_colorspaces) and p_colorspaces
+ local Pattern = next(d_patterns ) and p_patterns
+ local Shading = next(d_shades ) and p_shades
+ if ExtGState or ColorSpace or Pattern or Shading then
+ local collected = pdfdictionary {
+ ExtGState = ExtGState,
+ ColorSpace = ColorSpace,
+ Pattern = Pattern,
+ Shading = Shading,
+ -- ProcSet = pdfarray { pdfconstant("PDF") },
+ }
+ return collected()
+ else
+ return ""
+ end
+end
+
+function lpdf.adddocumentextgstate (k,v) d_extgstates [k] = v end
+function lpdf.adddocumentcolorspace(k,v) d_colorspaces[k] = v end
+function lpdf.adddocumentpattern (k,v) d_patterns [k] = v end
+function lpdf.adddocumentshade (k,v) d_shades [k] = v end
+
+registerdocumentfinalizer(flushextgstates,3,"extended graphic states")
+registerdocumentfinalizer(flushcolorspaces,3,"color spaces")
+registerdocumentfinalizer(flushpatterns,3,"patterns")
+registerdocumentfinalizer(flushshades,3,"shades")
+
+registerdocumentfinalizer(flushcatalog,3,"catalog")
+registerdocumentfinalizer(flushinfo,3,"info")
+registerdocumentfinalizer(flushnames,3,"names") -- before catalog
+
+registerpagefinalizer(checkextgstates,3,"extended graphic states")
+registerpagefinalizer(checkcolorspaces,3,"color spaces")
+registerpagefinalizer(checkpatterns,3,"patterns")
+registerpagefinalizer(checkshades,3,"shades")
+
+-- in strc-bkm: lpdf.registerdocumentfinalizer(function() structures.bookmarks.place() end,1)
+
+function lpdf.rotationcm(a)
+ local s, c = sind(a), cosd(a)
+ return format("%0.6f %0.6f %0.6f %0.6f 0 0 cm",c,s,-s,c)
+end
+
+-- ! -> universaltime
+
+local timestamp = os.date("%Y-%m-%dT%X") .. os.timezone(true)
+
+function lpdf.timestamp()
+ return timestamp
+end
+
+function lpdf.pdftimestamp(str)
+ local Y, M, D, h, m, s, Zs, Zh, Zm = match(str,"^(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$")
+ return Y and format("D:%s%s%s%s%s%s%s%s'%s'",Y,M,D,h,m,s,Zs,Zh,Zm)
+end
+
+function lpdf.id()
+ return format("%s.%s",tex.jobname,timestamp)
+end
+
+function lpdf.checkedkey(t,key,variant)
+ local pn = t and t[key]
+ if pn then
+ local tn = type(pn)
+ if tn == variant then
+ if variant == "string" then
+ return pn ~= "" and pn or nil
+ elseif variant == "table" then
+ return next(pn) and pn or nil
+ else
+ return pn
+ end
+ elseif tn == "string" and variant == "number" then
+ return tonumber(pn)
+ end
+ end
+end
+
+function lpdf.checkedvalue(value,variant) -- code not shared
+ if value then
+ local tv = type(value)
+ if tv == variant then
+ if variant == "string" then
+ return value ~= "" and value
+ elseif variant == "table" then
+ return next(value) and value
+ else
+ return value
+ end
+ elseif tv == "string" and variant == "number" then
+ return tonumber(value)
+ end
+ end
+end
+
+function lpdf.limited(n,min,max,default)
+ if not n then
+ return default
+ else
+ n = tonumber(n)
+ if not n then
+ return default
+ elseif n > max then
+ return max
+ elseif n < min then
+ return min
+ else
+ return n
+ end
+ end
+end
+
+-- lpdf.addtoinfo("ConTeXt.Version", tex.contextversiontoks)
+-- lpdf.addtoinfo("ConTeXt.Time", os.date("%Y.%m.%d %H:%M")) -- :%S
+-- lpdf.addtoinfo("ConTeXt.Jobname", environment.jobname)
+-- lpdf.addtoinfo("ConTeXt.Url", "www.pragma-ade.com")
+
+if not pdfreferenceobject then
+
+ local delayed = { }
+
+ local function flush()
+ local n = 0
+ for k,v in next, delayed do
+ pdfimmediateobject(k,v)
+ n = n + 1
+ end
+ if trace_objects then
+ report_objects("%s objects flushed",n)
+ end
+ delayed = { }
+ end
+
+ lpdf.registerdocumentfinalizer(flush,3,"objects") -- so we need a final flush too
+ lpdf.registerpagefinalizer (flush,3,"objects") -- somehow this lags behind .. I need to look into that some day
+
+ function lpdf.delayedobject(data)
+ local n = pdfreserveobject()
+ delayed[n] = data
+ return n
+ end
+
+end
|