diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lpdf-ini.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lpdf-ini.lua | 461 |
1 files changed, 306 insertions, 155 deletions
diff --git a/Master/texmf-dist/tex/context/base/lpdf-ini.lua b/Master/texmf-dist/tex/context/base/lpdf-ini.lua index 3335c623756..e0ffd40521b 100644 --- a/Master/texmf-dist/tex/context/base/lpdf-ini.lua +++ b/Master/texmf-dist/tex/context/base/lpdf-ini.lua @@ -1,20 +1,25 @@ -if not modules then modules = { } end modules ['back-pdf'] = { +if not modules then modules = { } end modules ['lpdf-ini'] = { version = 1.001, - comment = "companion to lpdf-ini.tex", + 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" } --- This code is very experimental ! - local setmetatable, getmetatable, type, next, tostring, tonumber, rawset = setmetatable, getmetatable, type, next, tostring, tonumber, rawset -local char, byte, format, gsub, concat = string.char, string.byte, string.format, string.gsub, table.concat +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 utfvalues = string.utfvalues -local texwrite = tex.write +local texwrite, texset, texsprint, ctxcatcodes = tex.write, tex.set, tex.sprint, tex.ctxcatcodes local sind, cosd = math.sind, math.cosd +local lpegmatch = lpeg.match + +local pdfreserveobj = pdf and pdf.reserveobj or function() return 1 end -- for testing +local pdfimmediateobj = pdf and pdf.immediateobj or function() return 2 end -- for testing 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) lpdf = lpdf or { } @@ -39,42 +44,41 @@ lpdf.tosixteen = tosixteen -- lpeg is some 5 times faster than gsub (in test) on escaping -local escapes = { - ["\\"] = "\\\\", - ["/"] = "\\/", ["#"] = "\\#", - ["<"] = "\\<", [">"] = "\\>", - ["["] = "\\[", ["]"] = "\\]", - ["("] = "\\(", [")"] = "\\)", -} +-- local escapes = { +-- ["\\"] = "\\\\", +-- ["/"] = "\\/", ["#"] = "\\#", +-- ["<"] = "\\<", [">"] = "\\>", +-- ["["] = "\\[", ["]"] = "\\]", +-- ["("] = "\\(", [")"] = "\\)", +-- } +-- +-- local escaped = lpeg.Cs(lpeg.Cc("(") * (lpeg.S("\\/#<>[]()")/escapes + lpeg.P(1))^0 * lpeg.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 -local escaped = lpeg.Cs(lpeg.Cc("(") * (lpeg.S("\\/#<>[]()")/escapes + lpeg.P(1))^0 * lpeg.Cc(")")) +-- \0 \t \n \r \f <space> ( ) [ ] { } / % local function toeight(str) - -- if not str or str == "" then - -- return "()" - -- else - -- return escaped:match(str) - -- end - -- - -- no need for escaping .. just use unicode instead return "(" .. str .. ")" end lpdf.toeight = toeight -local escapes = "-" - -local escaped = lpeg.Cs(lpeg.Cc("(") * (lpeg.S("\\/#<>[]()")/escapes + lpeg.P(1))^0 * lpeg.Cc(")")) - -local function cleaned(str) - if not str or str == "" then - return "()" - else - return escaped:match(str) - end -end - -lpdf.cleaned = cleaned +--~ local escaped = lpeg.Cs((lpeg.S("\0\t\n\r\f ()[]{}/%")/"#" + 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 = { } @@ -140,7 +144,8 @@ tostring_a = function(t,contentonly,key) r[#r+1] = tosixteen(v) elseif tv == "table" then local mv = getmetatable(v) - if mv and mv.__lpdftype then + local mt = mv and mv.__lpdftype + if mt then r[#r+1] = tostring(v) elseif v[1] then r[#r+1] = tostring_a(v) @@ -170,7 +175,15 @@ 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) return t[1] .. " 0 R" end -local tostring_v = function(t) return concat(t[1],"") 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 @@ -180,8 +193,8 @@ local function value_c(t) return sub(t[1],2) end -- the call is e local function value_d(t) return tostring_d(t,true,key) end -- the call is experimental local function value_a(t) return tostring_a(t,true,key) end -- the call is experimental local function value_z() return nil end -- the call is experimental -local function value_t() return true end -- the call is experimental -local function value_b() return false 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] end -- the call is experimental local function value_v() return t[1] end -- the call is experimental @@ -200,7 +213,7 @@ local mt_f = { __lpdftype = "false", __tostring = tostring_f, __call = valu local mt_r = { __lpdftype = "reference", __tostring = tostring_r, __call = value_r } local mt_v = { __lpdftype = "verbose", __tostring = tostring_v, __call = value_v } -function lpdf.stream(t) +local function pdfstream(t) -- we need to add attrbutes if t then for i=1,#t do t[i] = tostring(t[i]) @@ -209,29 +222,29 @@ function lpdf.stream(t) return setmetatable(t or { },mt_x) end -function lpdf.dictionary(t) +local function pdfdictionary(t) return setmetatable(t or { },mt_d) end -function lpdf.array(t) - if type(t) == "string"then +local function pdfarray(t) + if type(t) == "string" then return setmetatable({ t },mt_a) else return setmetatable(t or { },mt_a) end end -function lpdf.string(str,default) +local function pdfstring(str,default) return setmetatable({ str or default or "" },mt_s) end -function lpdf.unicode(str,default) +local function pdfunicode(str,default) return setmetatable({ str or default or "" },mt_u) end local cache = { } -- can be weak -function lpdf.number(n,default) -- 0-10 +local function pdfnumber(n,default) -- 0-10 n = n or default local c = cache[n] if not c then @@ -241,15 +254,24 @@ function lpdf.number(n,default) -- 0-10 return c end -for i=-1,9 do cache[i] = lpdf.number(i) end +for i=-1,9 do cache[i] = pdfnumber(i) end local cache = { } -- can be weak -function lpdf.constant(str,default) +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 = lpeg.Cs(lpeg.Cc("/") * (lpeg.S(forbidden)/replacements + lpeg.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({ "/" .. str },mt_c) + c = setmetatable({ lpegmatch(escaped,str) },mt_c) cache[str] = c end return c @@ -259,30 +281,114 @@ local p_null = { } setmetatable(p_null, mt_z) local p_true = { } setmetatable(p_true, mt_t) local p_false = { } setmetatable(p_false,mt_f) -function lpdf.null() +local function pdfnull() return p_null end -function lpdf.boolean(b,default) - if ((type(b) == boolean) and b) or default then - return p_true +--~ 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 p_false + return default and p_true or p_false end end -function lpdf.reference(r) +local function pdfreference(r) return setmetatable({ r or 0 },mt_r) end -function lpdf.verbose(t) -- maybe check for type +local function pdfverbose(t) -- maybe check for type return setmetatable({ t or "" },mt_v) end +lpdf.stream = pdfstream +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) + local r = pdfreserveobj() + if name then + names[name] = r + if trace_objects then + logs.report("backends", "reserving object number %s under name '%s'",r,name) + end + elseif trace_objects then + logs.report("backends", "reserving object number %s",r) + end + return r +end + +--~ local pdfreserveobject = lpdf.reserveobject + +function lpdf.flushobject(name,data) + if data then + name = names[name] or name + if name then + if trace_objects then + if trace_detail then + logs.report("backends", "flushing object data to reserved object with name '%s' -> %s",name,tostring(data)) + else + logs.report("backends", "flushing object data to reserved object with name '%s'",name) + end + end + return pdfimmediateobj(name,tostring(data)) + else + if trace_objects then + if trace_detail then + logs.report("backends", "flushing object data to reserved object with number %s -> %s",name,tostring(data)) + else + logs.report("backends", "flushing object data to reserved object with number %s",name) + end + end + return pdfimmediateobj(tostring(data)) + end + else + if trace_objects and trace_detail then + logs.report("backends", "flushing object data -> %s",tostring(name)) + end + return pdfimmediateobj(tostring(name)) + end +end + +function lpdf.sharedobj(content) + local r = cache[content] + if not r then + r = pdfreference(pdfimmediateobj(content)) + cache[content] = r + end + return r +end + --~ local d = lpdf.dictionary() ---~ local e = lpdf.dictionary { ["e"] = "abc" } +--~ local e = lpdf.dictionary { ["e"] = "abc", x = lpdf.dictionary { ["f"] = "ABC" } } --~ local f = lpdf.dictionary { ["f"] = "ABC" } ---~ local a = lpdf.array() +--~ local a = lpdf.array { lpdf.array { lpdf.string("xxx") } } + +--~ print(a) +--~ os.exit() --~ d["test"] = lpdf.string ("test") --~ d["more"] = "more" @@ -296,11 +402,19 @@ end --~ d.what = a ---~ print(d) +--~ 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" } @@ -310,91 +424,14 @@ end --~ d[#d+1] = { a=1, b=2, c=3, d="test" } --~ print(d) ---~ local s = lpdf.boolean(false) +--~ local s = lpdf.constant("xx") +--~ print(s) -- fails somehow --~ print(s()) -- fails somehow -function lpdf.checkedkey(t,key,kind) - local pn = t[key] - if pn then - local tn = type(pn) - if tn == kind then - if kind == "string" then - return pn ~= "" and pn - elseif kind == "table" then - return next(pn) and pn - else - return pn - end - elseif tn == "string" and kind == "number" then - return tonumber(pn) - end - end -end - -function lpdf.checkedvalue(value,kind) -- code not shared - if value then - local tv = type(value) - if tv == kind then - if kind == "string" then - return value ~= "" and value - elseif kind == "table" then - return next(value) and value - else - return value - end - elseif tv == "string" and kind == "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 - --- there will be more of this - -local pdfreference = lpdf.reference -local pdfdictionary = lpdf.dictionary -local pdfreserveobj = pdf.reserveobj -local pdfimmediateobj = pdf.immediateobj - -local texset, texsprint, ctxcatcodes = tex.set, tex.sprint, tex.ctxcatcodes - -local pdfobjcache = { } - -function lpdf.sharedobj(content) - local r = pdfobjcache[content] - if not r then - r = pdfreference(pdfimmediateobj(content)) - pdfobjcache[content] = r - end - return r -end - --- saves definitions later on - -backends = backends or { } -backends.pdf = backends.pdf or { - comment = "backend for directly generating pdf output", - nodeinjections = { }, - codeinjections = { }, - registrations = { }, - helpers = { }, -} +--~ local s = lpdf.boolean(false) +--~ s.value = true +--~ print(s) +--~ print(s()) -- three priority levels, default=2 @@ -409,9 +446,12 @@ local function resetpageproperties() end local function setpageproperties() - texset("global", "pdfpageresources", pageresources ()) - texset("global", "pdfpageattr", pageattributes ()) - texset("global", "pdfpagesattr", pagesattributes()) +--~ texset("global", "pdfpageresources", pageresources ()) +--~ texset("global", "pdfpageattr", pageattributes ()) +--~ texset("global", "pdfpagesattr", pagesattributes()) + pdf.pageresources = pageresources () + pdf.pageattributes = pageattributes () + pdf.pagesattributes = pagesattributes() end function lpdf.addtopageresources (k,v) pageresources [k] = v end @@ -458,16 +498,39 @@ end function lpdf.finalizedocument() if not environment.initex then run(documentfinalizers,"document") + function lpdf.finalizedocument() + logs.report("backend","serious error: the document is finalized multiple times") + function lpdf.finalizedocument() end + end end end -local c_template = "\\normalpdfcatalog{/%s %s}" -local i_template = "\\normalpdfinfo{/%s %s}" -local n_template = "\\normalpdfnames{/%s %s}" +-- some minimal tracing, handy for checking the order -function lpdf.addtocatalog(k,v) if not environment.initex then texsprint(ctxcatcodes,format(c_template,k,tostring(v))) end end -function lpdf.addtoinfo (k,v) if not environment.initex then texsprint(ctxcatcodes,format(i_template,k,tostring(v))) end end -function lpdf.addtonames (k,v) if not environment.initex then texsprint(ctxcatcodes,format(n_template,k,tostring(v))) end end +local function trace_set(what,key) + if trace_resources then + logs.report("backend", "setting key '%s' in '%s'",key,what) + end +end +local function trace_flush(what) + if trace_resources then + logs.report("backend", "flushing '%s'",what) + end +end + +lpdf.protectresources = true + +local catalog, info, names = pdfdictionary(), pdfdictionary(), pdfdictionary() + +local function flushcatalog() if not environment.initex then trace_flush("catalog") pdf.catalog = catalog() end end +local function flushinfo () if not environment.initex then trace_flush("info") pdf.info = info () end end +local function flushnames () if not environment.initex then trace_flush("names") 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 = pdfreserveobj() -- else bug in hvmd due so some internal luatex conflict local r_extgstates, d_extgstates = pdfreserveobj(), pdfdictionary() local p_extgstates = pdfreference(r_extgstates) local r_colorspaces, d_colorspaces = pdfreserveobj(), pdfdictionary() local p_colorspaces = pdfreference(r_colorspaces) @@ -479,10 +542,10 @@ local function checkcolorspaces() if next(d_colorspaces) then lpdf.addtopagereso local function checkpatterns () if next(d_patterns ) then lpdf.addtopageresources("Pattern", p_patterns ) end end local function checkshades () if next(d_shades ) then lpdf.addtopageresources("Shading", p_shades ) end end -local function flushextgstates () if next(d_extgstates ) then pdfimmediateobj(r_extgstates, tostring(d_extgstates )) end end -local function flushcolorspaces() if next(d_colorspaces) then pdfimmediateobj(r_colorspaces,tostring(d_colorspaces)) end end -local function flushpatterns () if next(d_patterns ) then pdfimmediateobj(r_patterns, tostring(d_patterns )) end end -local function flushshades () if next(d_shades ) then pdfimmediateobj(r_shades, tostring(d_shades )) end end +local function flushextgstates () if next(d_extgstates ) then trace_flush("extgstates") pdfimmediateobj(r_extgstates, tostring(d_extgstates )) end end +local function flushcolorspaces() if next(d_colorspaces) then trace_flush("colorspaces") pdfimmediateobj(r_colorspaces,tostring(d_colorspaces)) end end +local function flushpatterns () if next(d_patterns ) then trace_flush("patterns") pdfimmediateobj(r_patterns, tostring(d_patterns )) end end +local function flushshades () if next(d_shades ) then trace_flush("shades") pdfimmediateobj(r_shades, tostring(d_shades )) end end local collected = pdfdictionary { ExtGState = p_extgstates, @@ -505,15 +568,103 @@ lpdf.registerdocumentfinalizer(flushcolorspaces,3) lpdf.registerdocumentfinalizer(flushpatterns,3) lpdf.registerdocumentfinalizer(flushshades,3) +lpdf.registerdocumentfinalizer(flushcatalog,3) +lpdf.registerdocumentfinalizer(flushinfo,3) +lpdf.registerdocumentfinalizer(flushnames,3) + lpdf.registerpagefinalizer(checkextgstates,3) lpdf.registerpagefinalizer(checkcolorspaces,3) lpdf.registerpagefinalizer(checkpatterns,3) lpdf.registerpagefinalizer(checkshades,3) --- +-- in strc-bkm: lpdf.registerdocumentfinalizer(function() structure.bookmarks.place() end,1) function lpdf.rotationcm(a) local s, c = sind(a), cosd(a) texwrite(format("%s %s %s %s 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,kind) + local pn = t[key] + if pn then + local tn = type(pn) + if tn == kind then + if kind == "string" then + return pn ~= "" and pn + elseif kind == "table" then + return next(pn) and pn + else + return pn + end + elseif tn == "string" and kind == "number" then + return tonumber(pn) + end + end +end + +function lpdf.checkedvalue(value,kind) -- code not shared + if value then + local tv = type(value) + if tv == kind then + if kind == "string" then + return value ~= "" and value + elseif kind == "table" then + return next(value) and value + else + return value + end + elseif tv == "string" and kind == "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", tex.jobname) +-- lpdf.addtoinfo("ConTeXt.Url", "www.pragma-ade.com") + +-- saves definitions later on + +backends = backends or { } +backends.pdf = backends.pdf or { + comment = "backend for directly generating pdf output", + nodeinjections = { }, + codeinjections = { }, + registrations = { }, + helpers = { }, +} |