summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/lpdf-ini.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lpdf-ini.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/lpdf-ini.lua461
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 = { },
+}