diff options
97 files changed, 6231 insertions, 1545 deletions
diff --git a/metapost/context/base/metafun.mp b/metapost/context/base/metafun.mp index 98ea1980f..b828b8b9e 100644 --- a/metapost/context/base/metafun.mp +++ b/metapost/context/base/metafun.mp @@ -35,7 +35,7 @@ else : fi ; input mp-tool.mp ; -input mp-spec.mp ; +input mp-spec.mp ; % will be skipped in mkiv, some day input mp-core.mp ; input mp-page.mp ; input mp-text.mp ; @@ -47,6 +47,8 @@ input mp-step.mp ; input mp-grph.mp ; input mp-figs.mp ; +input mp-mlib.mp ; + % mp-form.mp ; input mp-grid.mp ; input mp-func.mp ; diff --git a/metapost/context/base/mp-grph.mp b/metapost/context/base/mp-grph.mp index 18c43c57b..e2e7e7529 100644 --- a/metapost/context/base/mp-grph.mp +++ b/metapost/context/base/mp-grph.mp @@ -56,23 +56,24 @@ string graphictextformat ; graphictextformat := "plain" ; string graphictextstring ; graphictextstring := "" ; string graphictextfile ; graphictextfile := "dummy.mpo" ; -def savegraphictext (expr str) = - graphictextfile := jobname & ".mpo" ; - if (graphictextstring<>"") : - write graphictextstring to graphictextfile ; - graphictextstring := "" ; - fi ; - write str to graphictextfile ; - let erasegraphictextfile = relax ; -enddef ; +def data_mpo_file = job_name & "-mp.mpo" enddef ; +def data_mpy_file = job_name & "-mp.mpy" enddef ; -def erasegraphictextfile = - graphictextfile := jobname & ".mpo" ; - write EOF to graphictextfile ; - let erasegraphictextfile = relax ; -enddef ; +% def savegraphictext (expr str) = +% if (graphictextstring<>"") : +% write graphictextstring to data_mpo_file ; +% graphictextstring := "" ; +% fi ; +% write str to data_mpo_file ; +% let erasegraphictextfile = relax ; +% enddef ; + +% def erasegraphictextfile = +% write EOF to data_mpo_file ; +% let erasegraphictextfile = relax ; +% enddef ; -extra_beginfig := extra_beginfig & " erasegraphictextfile ;" ; +% extra_beginfig := extra_beginfig & " erasegraphictextfile ;" ; def begingraphictextfig (expr n) = foundpicture := n ; scratchpicture := nullpicture ; @@ -213,7 +214,7 @@ def dofinishgraphictext text x_op_x = if (urcorner dashpart i) = origin : outline_fill := false ; fi ; endfor ; scratchpicture := nullpicture ; - readfile(jobname & ".mpy") ; + readfile(data_mpy_file) ; scratchpicture := (scratchpicture shifted -llcorner scratchpicture) scaled (1/10) ; if not d_color and not f_color : d_color := true ; fi if s_color : d_color := false ; f_color := false ; fi ; diff --git a/metapost/context/base/mp-mlib.mp b/metapost/context/base/mp-mlib.mp new file mode 100644 index 000000000..4ebe576e6 --- /dev/null +++ b/metapost/context/base/mp-mlib.mp @@ -0,0 +1,130 @@ + +if unknown mplib : endinput ; fi ; +if known context_mlib : endinput ; fi ; + +boolean context_mlib ; context_mlib := true ; + +numeric _tt_w_[], _tt_h_[], _tt_d_[] ; +numeric _tt_n_ ; _tt_n_ := 0 ; +boolean _trial_run_ ; _trial_run_ := false ; + +vardef textext(expr str) = + if _trial_run_ : + image ( + draw unitsquare + withprescript "tf" + withpostscript str ; + ) + else : + image ( + _tt_n_ := _tt_n_ + 1 ; + draw unitsquare + xscaled _tt_w_[_tt_n_] + yscaled (_tt_h_[_tt_n_] + _tt_d_[_tt_n_]) + withprescript "ts" + withpostscript decimal _tt_n_ ; + ) + fi +enddef ; + +def circular_shade (expr p, n, ca, cb) = + begingroup ; + save ab, r ; pair ab ; numeric r ; + r := (xpart lrcorner p - xpart llcorner p) ++ (ypart urcorner p - ypart lrcorner p) ; + set_circular_vector(ab,r)(p,n) ; + fill p withcircularshade(ab,ab,0,r,ca,cb) ; + if trace_shades : + drawarrow ab -- ab shifted (0,r) withpen pencircle scaled 1pt withcolor .5white ; + fi ; + endgroup ; +enddef ; +def linear_shade (expr p, n, ca, cb) = + begingroup ; + save a, b, sh ; pair a, b ; + set_linear_vector(a,b)(p,n) ; + fill p withlinearshade(a,b,ca,cb) ; + if trace_shades : + drawarrow a -- b withpen pencircle scaled 1pt withcolor .5white ; + fi ; + endgroup ; +enddef ; +def withcircularshade (expr a, b, ra, rb, ca, cb) = + withprescript + "cs" + withpostscript + "0 1 " & decimal shadefactor & " " & + colordecimals ca & " " & ddecimal (a shifted shadeoffset) & " " & decimal ra & " " & + colordecimals cb & " " & ddecimal (b shifted shadeoffset) & " " & decimal rb +enddef ; +def withlinearshade (expr a, b, ca, cb) = + withprescript + "ls" + withpostscript + "0 1 " & decimal shadefactor & " " & + colordecimals ca & " " & ddecimal (a shifted shadeoffset) & " " & + colordecimals cb & " " & ddecimal (b shifted shadeoffset) +enddef ; +string _defined_cs_pre_[], _defined_cs_post_[] ; numeric _defined_cs_ ; _defined_cs_:= 0 ; +vardef define_circular_shade (expr a, b, ra, rb, ca, cb) = + _defined_cs_ = _defined_cs_ + 1 ; + _defined_cs_pre_ [_defined_cs_] = "cs" ; + _defined_cs_post_[_defined_cs_] = "0 1 " & decimal shadefactor & " " & + colordecimals ca & " " & ddecimal (a shifted shadeoffset) & " " & decimal ra & " " & + colordecimals cb & " " & ddecimal (b shifted shadeoffset) & " " & decimal rb ; + _defined_cs_ +enddef ; +vardef define_linear_shade (expr a, b, ca, cb) = + _defined_cs_ = _defined_cs_ + 1 ; + _defined_cs_pre_ [_defined_cs_] = "ls" ; + _defined_cs_post_[_defined_cs_] = "0 1 " & decimal shadefactor & " " & + colordecimals ca & " " & ddecimal (a shifted shadeoffset) & " " + colordecimals cb & " " & ddecimal (b shifted shadeoffset) ; + _defined_cs_ +enddef ; +def withshade(expr sc) = + withprescript _defined_cs_pre_[sc] withpostscript _defined_cs_post_[sc] +enddef ; +def shadecolor(expr sc) = % obsolete + 1 withprescript _defined_cs_pre_[sc] withpostscript _defined_cs_post_[sc] +enddef ; + +% def _graphic_text_f_(expr t) text rest = +% draw unitsquare withprescript "gt" withpostscript t ; +% enddef ; +% def _graphic_text_s_(expr t) = +% begingroup ; +% save figurepicture ; picture figurepicture ; +% figurepicture := currentpicture ; currentpicture := nullpicture ; +% currentgraphictext := currentgraphictext + 1 ; +% dofinishgraphictext +% enddef ; + +vardef graphictext primary t = + save next ; + if _trial_run_ : + let next = nographictext ; + else : + let next = dographictext ; + fi + next(t) +enddef ; +def dographictext (expr t) = + begingroup ; + save figurepicture ; picture figurepicture ; + figurepicture := currentpicture ; currentpicture := nullpicture ; + currentgraphictext := currentgraphictext + 1 ; + dofinishgraphictext +enddef ; +def nographictext (expr t) text rest = + draw unitsquare withprescript "gt" withpostscript t ; +enddef ; + + +def doexternalfigure (expr filename) text transformation = + draw unitsquare transformation withprescript "fg" withpostscript filename ; +enddef ; + +extra_beginfig := extra_beginfig & "currentgraphictext := 0 ; " ; + +boolean cmykcolors ; cmykcolors := true ; +boolean spotcolors ; spotcolors := true ; diff --git a/metapost/context/base/mp-spec.mp b/metapost/context/base/mp-spec.mp index 351f9fe1c..f28e5a5d3 100644 --- a/metapost/context/base/mp-spec.mp +++ b/metapost/context/base/mp-spec.mp @@ -495,7 +495,8 @@ vardef cmyk(expr c,m,y,k) = ok := true ; % globally already defined fi ; if not ok : - save s ; string s ; s := dddecimal (c,m,y) & " " & decimal k ; +% save s ; string s ; s := dddecimal (c,m,y) & " " & decimal k ; + save s ; string s ; s := ddddecimal (c,m,y,k) ; _cmyk_counter_ := _cmyk_counter_ + 1 ; cmykcolorpattern[_cmyk_counter_/_special_div_] := s ; cmykcolorhash[c][m][y][k] := _cmyk_counter_ ; diff --git a/metapost/context/base/mp-tool.mp b/metapost/context/base/mp-tool.mp index b8e2dd668..24f2e6676 100644 --- a/metapost/context/base/mp-tool.mp +++ b/metapost/context/base/mp-tool.mp @@ -102,16 +102,59 @@ vardef ddecimal primary p = decimal xpart p & " " & decimal ypart p enddef ; -extra_endfig := extra_endfig - & "special " - & "(" - & ditto - & "%%HiResBoundingBox: " - & ditto - & "&ddecimal llcorner currentpicture" - & "&space" - & "&ddecimal urcorner currentpicture" - & ");"; +% is now built in + +% extra_endfig := extra_endfig +% & "special " +% & "(" +% & ditto +% & "%%HiResBoundingBox: " +% & ditto +% & "&ddecimal llcorner currentpicture" +% & "&space" +% & "&ddecimal urcorner currentpicture" +% & ");"; + +%D Colors: + +nocolormodel := 1 ; +greycolormodel := 3 ; +rgbcolormodel := 5 ; +cmykcolormodel := 7 ; + +let grayscale = numeric ; + +% def colorlike(expr c) text v = % colorlike(a) b, c, d ; +% forsuffixes i=v : % save i ; +% if cmykcolor c : +% cmykcolor i ; +% elseif rgbcolor c : +% rgbcolor i ; +% else : +% grayscale i ; +% fi ; +% endfor ; +% enddef ; + +vardef colorlike(text c) text v = % colorlike(a) b, c, d ; + save _p_ ; picture _p_ ; + forsuffixes i=v : + _p_ := image(draw origin withcolor c ;) ; % intercept pre and postscripts + if (colormodel _p_ = cmykcolormodel) : + cmykcolor i ; + elseif (colormodel _p_ = rgbcolormodel) : + rgbcolor i ; + else : + grayscale i ; + fi ; + endfor ; +enddef ; + +% if (unknown colormodel) : +% def colormodel = +% rgbcolormodel +% enddef ; +% fi ; %D Also handy (when we flush colors): @@ -124,7 +167,7 @@ vardef ddddecimal primary c = enddef ; vardef colordecimals primary c = - if cmykcolor c : + if cmykcolor c : decimal cyanpart c & ":" & decimal magentapart c & ":" & decimal yellowpart c & ":" & decimal blackpart c elseif rgbcolor c : decimal redpart c & ":" & decimal greenpart c & ":" & decimal bluepart c @@ -135,15 +178,12 @@ enddef ; %D We have standardized data file names: -if not known _data_prefix_ : - - string _data_prefix_ ; _data_prefix_ = "mpd-" ; - string _data_suffix_ ; _data_suffix_ = ".tmp" ; - -fi ; +def job_name = + jobname +enddef ; -def data_file = - _data_prefix_ & decimal charcode & _data_suffix_ +def data_mpd_file = + job_name & "-mp.mpd" enddef ; %D Because \METAPOST\ has a hard coded limit of 4~datafiles, @@ -157,12 +197,12 @@ boolean savingdata ; savingdata := false ; def savedata expr txt = if collapse_data : + write txt to data_mpd_file ; + else : write if savingdata : txt else : "\MPdata{" & decimal charcode & "}{" & txt & "}" fi - & "%" to jobname & _data_suffix_ ; - else : - write txt to data_file ; + & "%" to data_mpd_file ; fi ; enddef ; @@ -172,15 +212,19 @@ def startsavingdata = write "\MPdata{" & decimal charcode & "}{%" to - jobname & _data_suffix_ ; + data_mpd_file ; fi ; enddef ; def stopsavingdata = - savingdata := false ; if collapse_data : - write "}%" to jobname & _data_suffix_ ; + write "}%" to data_mpd_file ; fi ; + savingdata := false ; +enddef ; + +def finishsavingdata = + write EOF to data_mpd_file ; enddef ; %D Instead of a keystroke eating save and allocation @@ -664,6 +708,16 @@ color cyan ; cyan = (0,1,1) ; color magenta ; magenta = (1,0,1) ; color yellow ; yellow = (1,1,0) ; +def colortype(expr c) = + if cmykcolor c : cmykcolor elseif rgbcolor c : rgbcolor else : grayscale fi +enddef ; +vardef whitecolor(expr c) = + if cmykcolor c : (0,0,0,0) elseif rgbcolor c : (1,1,1) else : 1 fi +enddef ; +vardef blackcolor(expr c) = + if cmykcolor c : (0,0,0,1) elseif rgbcolor c : (0,0,0) else : 0 fi +enddef ; + %D Well, this is the dangerous and naive version: def drawfill text t = @@ -833,7 +887,18 @@ primarydef p randomized s = fi elseif pair p : p randomshifted s - elseif color p : + elseif cmykcolor p : + if color s : + (uniformdeviate cyanpart s * cyanpart p, + uniformdeviate magentapart s * magentapart p, + uniformdeviate yellowpart s * yellowpart p, + uniformdeviate blackpart s * blackpart p) + elseif pair s : + ((xpart s + uniformdeviate (ypart s - xpart s)) * p) + else : + (uniformdeviate s * p) + fi + elseif rgbcolor p : if color s : (uniformdeviate redpart s * redpart p, uniformdeviate greenpart s * greenpart p, @@ -843,6 +908,14 @@ primarydef p randomized s = else : (uniformdeviate s * p) fi + elseif color p : + if color s : + (uniformdeviate graypart s * graypart p) + elseif pair s : + ((xpart s + uniformdeviate (ypart s - xpart s)) * p) + else : + (uniformdeviate s * p) + fi else : p + uniformdeviate s fi) @@ -1152,7 +1225,7 @@ def visualizedfill expr c = enddef ; def do_visualizeddraw text t = - draworigin ; + draworigin ; drawpath _c_ t ; drawcontrollines _c_ ; drawcontrolpoints _c_ ; diff --git a/scripts/context/lua/luatools.lua b/scripts/context/lua/luatools.lua index 2d146149e..b38d0c4c1 100644 --- a/scripts/context/lua/luatools.lua +++ b/scripts/context/lua/luatools.lua @@ -1845,19 +1845,68 @@ dir = { } if lfs then do +--~ local attributes = lfs.attributes +--~ local walkdir = lfs.dir +--~ +--~ local function glob_pattern(path,patt,recurse,action) +--~ local ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe +--~ if ok and type(scanner) == "function" then +--~ if not path:find("/$") then path = path .. '/' end +--~ for name in scanner do +--~ local full = path .. name +--~ local mode = attributes(full,'mode') +--~ if mode == 'file' then +--~ if name:find(patt) then +--~ action(full) +--~ end +--~ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then +--~ glob_pattern(full,patt,recurse,action) +--~ end +--~ end +--~ end +--~ end +--~ +--~ dir.glob_pattern = glob_pattern +--~ +--~ local function glob(pattern, action) +--~ local t = { } +--~ local action = action or function(name) t[#t+1] = name end +--~ local path, patt = pattern:match("^(.*)/*%*%*/*(.-)$") +--~ local recurse = path and patt +--~ if not recurse then +--~ path, patt = pattern:match("^(.*)/(.-)$") +--~ if not (path and patt) then +--~ path, patt = '.', pattern +--~ end +--~ end +--~ patt = patt:gsub("([%.%-%+])", "%%%1") +--~ patt = patt:gsub("%*", ".*") +--~ patt = patt:gsub("%?", ".") +--~ patt = "^" .. patt .. "$" +--~ -- print('path: ' .. path .. ' | pattern: ' .. patt .. ' | recurse: ' .. tostring(recurse)) +--~ glob_pattern(path,patt,recurse,action) +--~ return t +--~ end +--~ +--~ dir.glob = glob + local attributes = lfs.attributes local walkdir = lfs.dir local function glob_pattern(path,patt,recurse,action) - local ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe + local ok, scanner + if path == "/" then + ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe + else + ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe + end if ok and type(scanner) == "function" then if not path:find("/$") then path = path .. '/' end for name in scanner do -print(name) local full = path .. name local mode = attributes(full,'mode') if mode == 'file' then - if name:find(patt) then + if full:find(patt) then action(full) end elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then @@ -1871,29 +1920,72 @@ print(name) local function glob(pattern, action) local t = { } - local action = action or function(name) table.insert(t,name) end - local path, patt = pattern:match("^(.*)/*%*%*/*(.-)$") - local recurse = path and patt - if not recurse then - path, patt = pattern:match("^(.*)/(.-)$") - if not (path and patt) then - path, patt = '.', pattern - end - end - patt = patt:gsub("([%.%-%+])", "%%%1") - patt = patt:gsub("%*", ".*") - patt = patt:gsub("%?", ".") - patt = "^" .. patt .. "$" - -- print('path: ' .. path .. ' | pattern: ' .. patt .. ' | recurse: ' .. tostring(recurse)) + local path, rest, patt, recurse + local action = action or function(name) t[#t+1] = name end + local pattern = pattern:gsub("^%*%*","./**") + local pattern = pattern:gsub("/%*/","/**/") + path, rest = pattern:match("^(/)(.-)$") + if path then + path = path + else + path, rest = pattern:match("^([^/]*)/(.-)$") + end + if rest then + patt = rest:gsub("([%.%-%+])", "%%%1") + end + patt = patt:gsub("%*", "[^/]*") + patt = patt:gsub("%?", "[^/]") + patt = patt:gsub("%[%^/%]%*%[%^/%]%*", ".*") + if path == "" then path = "." end + recurse = patt:find("%.%*/") ~= nil glob_pattern(path,patt,recurse,action) return t end + local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V + + local pattern = Ct { + [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), + [2] = C(((1-S("*?/"))^0 * P("/"))^0), + [3] = C(P(1)^0) + } + + local filter = Cs ( ( + P("**") / ".*" + + P("*") / "[^/]*" + + P("?") / "[^/]" + + P(".") / "%." + + P("+") / "%+" + + P("-") / "%-" + + P(1) + )^0 ) + + function glob(str) + local split = pattern:match(str) + if split then + local t = { } + local action = action or function(name) t[#t+1] = name end + local root, path, base = split[1], split[2], split[3] + local recurse = base:find("**") + local start = root .. path + local result = filter:match(start .. base) + -- print(str, start, result) + glob_pattern(start,result,recurse,action) + return t + else + return { } + end + end + dir.glob = glob - -- todo: speedup + --~ list = dir.glob("**/*.tif") + --~ list = dir.glob("/**/*.tif") + --~ list = dir.glob("./**/*.tif") + --~ list = dir.glob("oeps/**/*.tif") + --~ list = dir.glob("/oeps/**/*.tif") - local function globfiles(path,recurse,func,files) + local function globfiles(path,recurse,func,files) -- func == pattern or function if type(func) == "string" then local s = func -- alas, we need this indirect way func = function(name) return name:find(s) end @@ -1935,23 +2027,6 @@ print(name) --~ mkdirs(".","/a/b/c") --~ mkdirs("a","b","c") ---~ function dir.mkdirs(...) ---~ local pth, err, lst = "", false, table.concat({...},"/") ---~ for _, s in ipairs(lst:split("/")) do ---~ if pth == "" then ---~ pth = (s == "" and "/") or s ---~ else ---~ pth = pth .. "/" .. s ---~ end ---~ if s == "" then ---~ -- can be network path ---~ elseif not lfs.isdir(pth) then ---~ lfs.mkdir(pth) ---~ end ---~ end ---~ return pth, not err ---~ end - local make_indeed = true -- false if string.find(os.getenv("PATH"),";") then diff --git a/scripts/context/lua/mtx-context.lua b/scripts/context/lua/mtx-context.lua index e0aa7d086..a2ea27a9b 100644 --- a/scripts/context/lua/mtx-context.lua +++ b/scripts/context/lua/mtx-context.lua @@ -625,6 +625,10 @@ input.verbose = true input.starttiming(scripts.context) +if environment.argument("once") then + scripts.context.multipass.nofruns = 1 +end + if environment.argument("run") then scripts.context.run() elseif environment.argument("make") then diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index aa78a553e..f08c0f812 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -176,7 +176,7 @@ end --~ split = lpeg.Ct(c*(p*c)^0) --~ splitters[separator] = split --~ end ---~ return lpeg.match(split,self) -- split:match(self) +--~ return split:match(self) --~ else --~ return { } --~ end @@ -429,6 +429,8 @@ if not versions then versions = { } end versions['l-lpeg'] = 1.001 --~ lpeg.whitespace = lpeg.S(' \r\n\f\t')^1 --~ lpeg.nonwhitespace = lpeg.P(1-lpeg.whitespace)^1 +local hash = { } + function lpeg.anywhere(pattern) --slightly adapted from website return lpeg.P { lpeg.P(pattern) + 1 * lpeg.V(1) } end @@ -444,6 +446,20 @@ function lpeg.splitter(pattern, action) end +local crlf = lpeg.P("\r\n") +local cr = lpeg.P("\r") +local lf = lpeg.P("\n") +local space = lpeg.S(" \t\f\v") +local newline = crlf + cr + lf +local spacing = space^0 * newline +local content = lpeg.Cs((1-spacing)^1) * spacing^-1 * (spacing * lpeg.Cc(""))^0 + +local capture = lpeg.Ct(content^0) + +function string:splitlines() + return capture:match(self) +end + -- filename : l-table.lua -- comment : split off from luat-lib @@ -517,15 +533,6 @@ function table.prepend(t, list) end end ---~ function table.merge(t, ...) ---~ for _, list in ipairs({...}) do ---~ for k,v in pairs(list) do ---~ t[k] = v ---~ end ---~ end ---~ return t ---~ end - function table.merge(t, ...) -- first one is target t = t or {} local lst = {...} @@ -537,16 +544,6 @@ function table.merge(t, ...) -- first one is target return t end ---~ function table.merged(...) ---~ local tmp = { } ---~ for _, list in ipairs({...}) do ---~ for k,v in pairs(list) do ---~ tmp[k] = v ---~ end ---~ end ---~ return tmp ---~ end - function table.merged(...) local tmp, lst = { }, {...} for i=1,#lst do @@ -557,15 +554,6 @@ function table.merged(...) return tmp end ---~ function table.imerge(t, ...) ---~ for _, list in ipairs({...}) do ---~ for _, v in ipairs(list) do ---~ t[#t+1] = v ---~ end ---~ end ---~ return t ---~ end - function table.imerge(t, ...) local lst = {...} for i=1,#lst do @@ -577,16 +565,6 @@ function table.imerge(t, ...) return t end ---~ function table.imerged(...) ---~ local tmp = { } ---~ for _, list in ipairs({...}) do ---~ for _,v in pairs(list) do ---~ tmp[#tmp+1] = v ---~ end ---~ end ---~ return tmp ---~ end - function table.imerged(...) local tmp, lst = { }, {...} for i=1,#lst do @@ -734,7 +712,6 @@ do end if n == #t then local tt = { } - -- for _,v in ipairs(t) do for i=1,#t do local v = t[i] local tv = type(v) @@ -789,7 +766,7 @@ do local inline = compact and table.serialize_inline local first, last = nil, 0 -- #root cannot be trusted here if compact then - for k,v in ipairs(root) do -- NOT: for k=1,#root do + for k,v in ipairs(root) do -- NOT: for k=1,#root do (why) if not first then first = k end last = last + 1 end @@ -971,7 +948,8 @@ end do local function flatten(t,f,complete) - for _,v in ipairs(t) do + for i=1,#t do + local v = t[i] if type(v) == "table" then if complete or type(v[1]) == "table" then flatten(v,f,complete) @@ -1437,7 +1415,7 @@ do local one = lpeg.C(1-lpeg.S(''))^1 function number.toset(n) - return lpeg.match(one,tostring(n)) + return one:match(tostring(n)) end end @@ -1839,17 +1817,53 @@ if lfs then do else path, rest = pattern:match("^([^/]*)/(.-)$") end - patt = rest:gsub("([%.%-%+])", "%%%1") + if rest then + patt = rest:gsub("([%.%-%+])", "%%%1") + end patt = patt:gsub("%*", "[^/]*") patt = patt:gsub("%?", "[^/]") patt = patt:gsub("%[%^/%]%*%[%^/%]%*", ".*") if path == "" then path = "." end - -- print(pattern, path, patt) - recurse = patt:find("%.%*/") + recurse = patt:find("%.%*/") ~= nil glob_pattern(path,patt,recurse,action) return t end + local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V + + local pattern = Ct { + [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), + [2] = C(((1-S("*?/"))^0 * P("/"))^0), + [3] = C(P(1)^0) + } + + local filter = Cs ( ( + P("**") / ".*" + + P("*") / "[^/]*" + + P("?") / "[^/]" + + P(".") / "%." + + P("+") / "%+" + + P("-") / "%-" + + P(1) + )^0 ) + + function glob(str) + local split = pattern:match(str) + if split then + local t = { } + local action = action or function(name) t[#t+1] = name end + local root, path, base = split[1], split[2], split[3] + local recurse = base:find("**") + local start = root .. path + local result = filter:match(start .. base) + -- print(str, start, result) + glob_pattern(start,result,recurse,action) + return t + else + return { } + end + end + dir.glob = glob --~ list = dir.glob("**/*.tif") @@ -2162,6 +2176,8 @@ xml.trace_lpath = false xml.trace_print = false xml.trace_remap = false +local format, concat = string.format, table.concat + -- todo: some things per xml file, liek namespace remapping --[[ldx-- @@ -2172,7 +2188,7 @@ find based solution where we loop over an array of patterns. Less code and much cleaner.</p> --ldx]]-- -xml.xmlns = { } +xml.xmlns = xml.xmlns or { } do @@ -2312,9 +2328,9 @@ do local toclose = remove(stack) top = stack[#stack] if #stack < 1 then - errorstr = string.format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "") + errorstr = format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "") elseif toclose.tg ~= tag then -- no namespace check - errorstr = string.format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "") + errorstr = format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "") end dt = top.dt dt[#dt+1] = toclose @@ -2438,8 +2454,10 @@ do errorstr = "empty xml file" elseif not grammar:match(data) then errorstr = "invalid xml file" + else + errorstr = "" end - if errorstr then + if errorstr and errorstr ~= "" then result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={}, er = true } }, error = true } setmetatable(stack, mt) if xml.error_handler then xml.error_handler("load",errorstr) end @@ -2449,7 +2467,9 @@ do if not no_root then result = { special = true, ns = "", tg = '@rt@', dt = result.dt, at={} } setmetatable(result, mt) - for k,v in ipairs(result.dt) do + local rdt = result.dt + for k=1,#rdt do + local v = rdt[k] if type(v) == "table" and not v.special then -- always table -) result.ri = k -- rootindex break @@ -2560,138 +2580,141 @@ do local fallbackhandle = (tex and tex.sprint) or io.write - function xml.serialize(e, handle, textconverter, attributeconverter, specialconverter, nocommands) + local function serialize(e, handle, textconverter, attributeconverter, specialconverter, nocommands) if not e then - -- quit - elseif not nocommands and e.command and xml.command then - xml.command(e) - else - handle = handle or fallbackhandle - local etg = e.tg - if etg then - -- local format = string.format - if e.special then - local edt = e.dt - local spc = specialconverter and specialconverter[etg] - if spc then - local result = spc(edt[1]) - if result then - handle(result) - else - -- no need to handle any further + return + elseif not nocommands then + local ec = e.command + if ec then + local xc = xml.command + if xc then + xc(e,ec) + return + end + end + end + handle = handle or fallbackhandle + local etg = e.tg + if etg then + if e.special then + local edt = e.dt + local spc = specialconverter and specialconverter[etg] + if spc then + local result = spc(edt[1]) + if result then + handle(result) + else + -- no need to handle any further + end + elseif etg == "@pi@" then + -- handle(format("<?%s?>",edt[1])) + handle("<?" .. edt[1] .. "?>") -- maybe table.join(edt) + elseif etg == "@cm@" then + -- handle(format("<!--%s-->",edt[1])) + handle("<!--" .. edt[1] .. "-->") + elseif etg == "@cd@" then + -- handle(format("<![CDATA[%s]]>",edt[1])) + handle("<![CDATA[" .. edt[1] .. "]]>") + elseif etg == "@dd@" then + -- handle(format("<!DOCTYPE %s>",edt[1])) + handle("<!DOCTYPE " .. edt[1] .. ">") + elseif etg == "@rt@" then + serialize(edt,handle,textconverter,attributeconverter,specialconverter,nocommands) + end + else + local ens, eat, edt, ern = e.ns, e.at, e.dt, e.rn + local ats = eat and next(eat) and { } + if ats then + if attributeconverter then + for k,v in pairs(eat) do + ats[#ats+1] = format('%s=%q',k,attributeconverter(v)) + end + else + for k,v in pairs(eat) do + ats[#ats+1] = format('%s=%q',k,v) end - elseif etg == "@pi@" then - -- handle(format("<?%s?>",edt[1])) - handle("<?" .. edt[1] .. "?>") -- maybe table.join(edt) - elseif etg == "@cm@" then - -- handle(format("<!--%s-->",edt[1])) - handle("<!--" .. edt[1] .. "-->") - elseif etg == "@cd@" then - -- handle(format("<![CDATA[%s]]>",edt[1])) - handle("<![CDATA[" .. edt[1] .. "]]>") - elseif etg == "@dd@" then - -- handle(format("<!DOCTYPE %s>",edt[1])) - handle("<!DOCTYPE " .. edt[1] .. ">") - elseif etg == "@rt@" then - xml.serialize(edt,handle,textconverter,attributeconverter,specialconverter,nocommands) end - else - local ens, eat, edt, ern = e.ns, e.at, e.dt, e.rn - local ats = eat and next(eat) and { } + end + if ern and xml.trace_remap then if ats then - local format = string.format - if attributeconverter then - for k,v in pairs(eat) do - ats[#ats+1] = format('%s=%q',k,attributeconverter(v)) - end - else - for k,v in pairs(eat) do - ats[#ats+1] = format('%s=%q',k,v) - end - end + ats[#ats+1] = format("xmlns:remapped='%s'",ern) + else + ats = { format("xmlns:remapped='%s'",ern) } end - if ern and xml.trace_remap then + end + if ens ~= "" then + if edt and #edt > 0 then if ats then - ats[#ats+1] = string.format("xmlns:remapped='%s'",ern) + -- handle(format("<%s:%s %s>",ens,etg,concat(ats," "))) + handle("<" .. ens .. ":" .. etg .. " " .. concat(ats," ") .. ">") else - ats = { string.format("xmlns:remapped='%s'",ern) } + -- handle(format("<%s:%s>",ens,etg)) + handle("<" .. ens .. ":" .. etg .. ">") end - end - if ens ~= "" then - if edt and #edt > 0 then - if ats then - -- handle(format("<%s:%s %s>",ens,etg,table.concat(ats," "))) - handle("<" .. ens .. ":" .. etg .. " " .. table.concat(ats," ") .. ">") - else - -- handle(format("<%s:%s>",ens,etg)) - handle("<" .. ens .. ":" .. etg .. ">") - end - local serialize = xml.serialize - for i=1,#edt do - local e = edt[i] - if type(e) == "string" then - if textconverter then - handle(textconverter(e)) - else - handle(e) - end + for i=1,#edt do + local e = edt[i] + if type(e) == "string" then + if textconverter then + handle(textconverter(e)) else - serialize(e,handle,textconverter,attributeconverter,specialconverter,nocommands) + handle(e) end - end - -- handle(format("</%s:%s>",ens,etg)) - handle("</" .. ens .. ":" .. etg .. ">") - else - if ats then - -- handle(format("<%s:%s %s/>",ens,etg,table.concat(ats," "))) - handle("<" .. ens .. ":" .. etg .. " " .. table.concat(ats," ") .. "/>") else - -- handle(format("<%s:%s/>",ens,etg)) - handle("<" .. ens .. ":" .. "/>") + serialize(e,handle,textconverter,attributeconverter,specialconverter,nocommands) end end + -- handle(format("</%s:%s>",ens,etg)) + handle("</" .. ens .. ":" .. etg .. ">") else - if edt and #edt > 0 then - if ats then - -- handle(format("<%s %s>",etg,table.concat(ats," "))) - handle("<" .. etg .. " " .. table.concat(ats," ") .. ">") - else - -- handle(format("<%s>",etg)) - handle("<" .. etg .. ">") - end - local serialize = xml.serialize - for i=1,#edt do - serialize(edt[i],handle,textconverter,attributeconverter,specialconverter,nocommands) - end - -- handle(format("</%s>",etg)) - handle("</" .. etg .. ">") + if ats then + -- handle(format("<%s:%s %s/>",ens,etg,concat(ats," "))) + handle("<" .. ens .. ":" .. etg .. " " .. concat(ats," ") .. "/>") else - if ats then - -- handle(format("<%s %s/>",etg,table.concat(ats," "))) - handle("<" .. etg .. " " .. table.concat(ats," ") .. "/>") - else - -- handle(format("<%s/>",etg)) - handle("<" .. etg .. "/>") - end + -- handle(format("<%s:%s/>",ens,etg)) + handle("<" .. ens .. ":" .. "/>") end end - end - elseif type(e) == "string" then - if textconverter then - handle(textconverter(e)) else - handle(e) + if edt and #edt > 0 then + if ats then + -- handle(format("<%s %s>",etg,concat(ats," "))) + handle("<" .. etg .. " " .. concat(ats," ") .. ">") + else + -- handle(format("<%s>",etg)) + handle("<" .. etg .. ">") + end + for i=1,#edt do + serialize(edt[i],handle,textconverter,attributeconverter,specialconverter,nocommands) + end + -- handle(format("</%s>",etg)) + handle("</" .. etg .. ">") + else + if ats then + -- handle(format("<%s %s/>",etg,concat(ats," "))) + handle("<" .. etg .. " " .. concat(ats," ") .. "/>") + else + -- handle(format("<%s/>",etg)) + handle("<" .. etg .. "/>") + end + end end + end + elseif type(e) == "string" then + if textconverter then + handle(textconverter(e)) else - local serialize = xml.serialize - for i=1,#e do - serialize(e[i],handle,textconverter,attributeconverter,specialconverter,nocommands) - end + handle(e) + end + else + for i=1,#e do + serialize(e[i],handle,textconverter,attributeconverter,specialconverter,nocommands) end end end - function xml.checkbom(root) + xml.serialize = serialize + + function xml.checkbom(root) -- can be made faster if root.ri then local dt, found = root.dt, false for k,v in ipairs(dt) do @@ -2707,24 +2730,24 @@ do end end -end - ---[[ldx-- -<p>At the cost of some 25% runtime overhead you can first convert the tree to a string -and then handle the lot.</p> ---ldx]]-- + --[[ldx-- + <p>At the cost of some 25% runtime overhead you can first convert the tree to a string + and then handle the lot.</p> + --ldx]]-- -function xml.tostring(root) -- 25% overhead due to collecting - if root then - if type(root) == 'string' then - return root - elseif next(root) then - local result = { } - xml.serialize(root,function(s) result[#result+1] = s end) - return table.concat(result,"") + function xml.tostring(root) -- 25% overhead due to collecting + if root then + if type(root) == 'string' then + return root + elseif next(root) then + local result = { } + serialize(root,function(s) result[#result+1] = s end) + return concat(result,"") + end end -end - return "" + return "" + end + end --[[ldx-- @@ -2852,14 +2875,14 @@ do [40] = "processing instruction", } - local function make_expression(str) + local function make_expression(str) --could also be an lpeg str = str:gsub("@([a-zA-Z%-_]+)", "(a['%1'] or '')") str = str:gsub("position%(%)", "i") str = str:gsub("text%(%)", "t") str = str:gsub("!=", "~=") str = str:gsub("([^=!~<>])=([^=!~<>])", "%1==%2") str = str:gsub("([a-zA-Z%-_]+)%(", "functions.%1(") - return str, loadstring(string.format("return function(functions,i,a,t) return %s end", str))() + return str, loadstring(format("return function(functions,i,a,t) return %s end", str))() end local map = { } @@ -2945,6 +2968,9 @@ do local expression = (is_one * is_expression)/ function(...) map[#map+1] = { 31, true, ... } end local dont_expression = (is_none * is_expression)/ function(...) map[#map+1] = { 31, false, ... } end + local self_expression = ( is_expression)/ function(...) map[#map+1] = { 31, true, "", "*", ... } end + local dont_self_expression = (exclam * is_expression)/ function(...) map[#map+1] = { 31, true, "", "*", ... } end + local instruction = (instructiontag * text ) / function(...) map[#map+1] = { 40, ... } end local nothing = (empty ) / function( ) map[#map+1] = { 15 } end -- 15 ? local crap = (1-slash)^1 @@ -2970,6 +2996,7 @@ do dont_match_and_eq + dont_match_and_ne + match_and_eq + match_and_ne + dont_expression + expression + +dont_self_expression + self_expression + has_attribute + has_value + dont_match_one_of + match_one_of + dont_match + match + @@ -2981,7 +3008,7 @@ do followup = ((slash + parenttag + childtag + selftag)^0 * selector)^1, } - function compose(str) + local function compose(str) if not str or str == "" then -- wildcard return true @@ -3003,7 +3030,7 @@ do -- root return false end - elseif #map == 2 and m == 12 and map[2][1] == 20 then + elseif #map == 2 and m == 12 and map[2][1] == 20 then -- return { { 29, map[2][2], map[2][3], map[2][4], map[2][5] } } map[2][1] = 29 return { map[2] } @@ -3011,6 +3038,7 @@ do if m ~= 11 and m ~= 12 and m ~= 13 and m ~= 14 and m ~= 15 and m ~= 16 then table.insert(map, 1, { 16 }) end + -- print((table.serialize(map)):gsub("[ \n]+"," ")) return map end end @@ -3045,7 +3073,7 @@ do report(" -: wildcard\n") else if type(pattern) == "string" then - report(string.format("pattern: %s\n",pattern)) + report(format("pattern: %s\n",pattern)) end for k,v in ipairs(lp) do if #v > 1 then @@ -3058,9 +3086,9 @@ do t[#t+1] = (vv and "==") or "<>" end end - report(string.format("%2i: %s %s -> %s\n", k,v[1],actions[v[1]],table.join(t," "))) + report(format("%2i: %s %s -> %s\n", k,v[1],actions[v[1]],table.join(t," "))) else - report(string.format("%2i: %s %s\n", k,v[1],actions[v[1]])) + report(format("%2i: %s %s\n", k,v[1],actions[v[1]])) end end end @@ -3181,7 +3209,7 @@ do start, stop, step = stop, start, -1 end local idx = 0 - for k=start,stop,step do + for k=start,stop,step do -- we used to have functions for all but a case is faster local e = rootdt[k] local ns, tg = e.rn or e.ns, e.tg if tg then @@ -3256,15 +3284,19 @@ do if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end if not action[2] then matched = not matched end if matched then - matched = action[6](functions,idx,e.at,edt[1]) + matched = action[6](functions,idx,e.at or { },edt[1]) end end if matched then -- combine tg test and at test if index == #pattern then if handle(root,rootdt,root.ri or k) then return false end ---~ if wildcard and multiple then -if wildcard or multiple then - if not traverse(e,pattern,handle,reverse,index,root,true) then return false end + if wildcard then + if multiple then + if not traverse(e,pattern,handle,reverse,index,root,true) then return false end + else + -- maybe or multiple; anyhow, check on (section|title) vs just section and title in example in lxml + if not traverse(e,pattern,handle,reverse,index,root) then return false end + end end else if not traverse(e,pattern,handle,reverse,index+1,root) then return false end diff --git a/scripts/context/lua/scite-ctx.lua b/scripts/context/lua/scite-ctx.lua new file mode 100644 index 000000000..1b8329289 --- /dev/null +++ b/scripts/context/lua/scite-ctx.lua @@ -0,0 +1,843 @@ +-- version : 1.0.0 - 07/2005 (2008: lua 5.1) +-- author : Hans Hagen - PRAGMA ADE - www.pragma-ade.com +-- copyright : public domain or whatever suits +-- remark : part of the context distribution, my first lua code + +-- todo: name space for local functions + +-- loading: scite-ctx.properties + +-- # environment variable +-- # +-- # CTXSPELLPATH=t:/spell +-- # +-- # auto language detection +-- # +-- # % version =1.0 language=uk +-- # <?xml version='1.0' language='uk' ?> + +-- ext.lua.startup.script=$(SciteDefaultHome)/scite-ctx.lua +-- +-- # extension.$(file.patterns.context)=scite-ctx.lua +-- # extension.$(file.patterns.example)=scite-ctx.lua +-- +-- # ext.lua.reset=1 +-- # ext.lua.auto.reload=1 +-- # ext.lua.startup.script=t:/lua/scite-ctx.lua +-- +-- ctx.menulist.default=\ +-- wrap=wrap_text|\ +-- unwrap=unwrap_text|\ +-- sort=sort_text|\ +-- document=document_text|\ +-- quote=quote_text|\ +-- compound=compound_text|\ +-- check=check_text +-- +-- ctx.spellcheck.language=auto +-- ctx.spellcheck.wordsize=4 +-- ctx.spellcheck.wordpath=ENV(CTXSPELLPATH) +-- +-- ctx.spellcheck.wordfile.all=spell-uk.txt,spell-nl.txt +-- +-- ctx.spellcheck.wordfile.uk=spell-uk.txt +-- ctx.spellcheck.wordfile.nl=spell-nl.txt +-- ctx.spellcheck.wordsize.uk=4 +-- ctx.spellcheck.wordsize.nl=4 +-- +-- command.name.21.*=CTX Action List +-- command.subsystem.21.*=3 +-- command.21.*=show_menu $(ctx.menulist.default) +-- command.groupundo.21.*=yes +-- command.shortcut.21.*=Shift+F11 +-- +-- command.name.22.*=CTX Check Text +-- command.subsystem.22.*=3 +-- command.22.*=check_text +-- command.groupundo.22.*=yes +-- command.shortcut.22.*=Ctrl+L +-- +-- command.name.23.*=CTX Wrap Text +-- command.subsystem.23.*=3 +-- command.23.*=wrap_text +-- command.groupundo.23.*=yes +-- command.shortcut.23.*=Ctrl+M +-- +-- # command.21.*=check_text +-- # command.21.*=dofile e:\context\lua\scite-ctx.lua + +-- generic functions + +local crlf = "\n" + +function traceln(str) + trace(str .. crlf) + io.flush() +end + +function table.found(tab, str) + local l, r, p + if #str == 0 then + return false + else + l, r = 1, #tab + while l <= r do + p = math.floor((l+r)/2) + if str < tab[p] then + r = p - 1 + elseif str > tab[p] then + l = p + 1 + else + return true + end + end + return false + end +end + +function string:grab(delimiter) + local list = {} + for snippet in self:gmatch(delimiter) do + list[#list+1] = snippet + end + return list +end + +function string:is_empty() + return not self:find("%S") +end + +function string:expand() + return (self:gsub("ENV%((%w+)%)", os.envvar)) +end + +function string:strip() + return (self:gsub("^%s*(.-)%s*$", "%1")) +end + +do + + local lower, gsub, sub = string.lower, string.gsub, string.sub + + function table.alphasort(list,i) + if i and i > 0 then + local function alphacmp(a,b) + return lower(gsub(sub(a,i),'0',' ')) < lower(gsub(sub(b,i),'0',' ')) + end + table.sort(list,alphacmp) + else + local function alphacmp(a,b) + return a:lower() < b:lower() + end + table.sort(list,alphacmp) + end + end + +end + +function io.exists(filename) + local ok, result, message = pcall(io.open,filename) + if result then + io.close(result) + return true + else + return false + end +end + +function os.envvar(str) + local s = os.getenv(str) + if s ~= '' then + return s + end + s = os.getenv(str:upper()) + if s ~= '' then + return s + end + s = os.getenv(str:lower()) + if s ~= '' then + return s + end +end + +-- support functions, maybe editor namespace + +-- function column_of_position(position) +-- local line = editor:LineFromPosition(position) +-- local oldposition = editor.CurrentPos +-- local column = 0 +-- editor:GotoPos(position) +-- while editor.CurrentPos ~= 0 and line == editor:LineFromPosition(editor.CurrentPos) do +-- editor:CharLeft() +-- column = column + 1 +-- end +-- editor:GotoPos(oldposition) +-- if line > 0 then +-- return column -1 +-- else +-- return column +-- end +-- end + +-- function line_of_position(position) +-- return editor:LineFromPosition(position) +-- end + +function extend_to_start() + local selectionstart = editor.SelectionStart + local selectionend = editor.SelectionEnd + local line = editor:LineFromPosition(selectionstart) + if line > 0 then + while line == editor:LineFromPosition(selectionstart-1) do + selectionstart = selectionstart - 1 + editor:SetSel(selectionstart,selectionend) + end + else + selectionstart = 0 + end + editor:SetSel(selectionstart,selectionend) + return selectionstart +end + +function extend_to_end() -- editor:LineEndExtend() does not work + local selectionstart = editor.SelectionStart + local selectionend = editor.SelectionEnd + local line = editor:LineFromPosition(selectionend) + while line == editor:LineFromPosition(selectionend+1) do + selectionend = selectionend + 1 + editor:SetSel(selectionstart,selectionend) + end + editor:SetSel(selectionstart,selectionend) + return selectionend +end + +function getfiletype() + local firstline = editor:GetLine(0) + if editor.Lexer == SCLEX_TEX then + return 'tex' + elseif editor.Lexer == SCLEX_XML then + return 'xml' + elseif firstline:find("^%%") then + return 'tex' + elseif firstline:find("^<%?xml") then + return 'xml' + else + return 'unknown' + end +end + +-- inspired by LuaExt's scite_Files + +function get_dir_list(mask) + local f + if props['PLAT_GTK'] and props['PLAT_GTK'] ~= "" then + f = io.popen('ls -1 ' .. mask) + else + mask = mask:gsub('/','\\') + local tmpfile = 'scite-ctx.tmp' + local cmd = 'dir /b "' .. mask .. '" > ' .. tmpfile + os.execute(cmd) + f = io.open(tmpfile) + end + local files = {} + if not f then -- path check added + return files + end + for line in f:lines() do + files[#files+1] = line + end + f:close() + return files +end + +-- banner + +do + + print("loading scite-ctx.lua definition file\n") + print("- see scite-ctx.properties for configuring info\n") + print("- ctx.spellcheck.wordpath set to " .. props['ctx.spellcheck.wordpath']) + if (props['ctx.spellcheck.wordpath']:lower()):find("ctxspellpath") then + if os.getenv('ctxspellpath') then + print("- ctxspellpath set to " .. os.getenv('CTXSPELLPATH')) + else + print("- 'ctxspellpath is not set") + end + print("- ctx.spellcheck.wordpath expands to " .. string.expand(props['ctx.spellcheck.wordpath'])) + end + print("\n- ctx.wraptext.length is set to " .. props['ctx.wraptext.length']) + if props['ctx.helpinfo'] ~= '' then + print("\n- key bindings:\n") + print(((string.strip(props['ctx.helpinfo'])):gsub("%s*\|%s*","\n"))) + end + print("\n- recognized first lines:\n") + print("xml <?xml version='1.0' language='nl'") + print("tex % language=nl") + +end + +-- text functions + +-- written while listening to Talk Talk + +local magicstring = string.rep("<ctx-crlf/>", 2) + +function wrap_text() + + -- We always go to the end of a line, so in fact some of + -- the variables set next are not needed. + + local length = props["ctx.wraptext.length"] + + if length == '' then length = 80 else length = tonumber(length) end + + local startposition = editor.SelectionStart + local endposition = editor.SelectionEnd + + if startposition == endposition then return end + + editor:LineEndExtend() + + startposition = editor.SelectionStart + endposition = editor.SelectionEnd + + -- local startline = line_of_position(startposition) + -- local endline = line_of_position(endposition) + -- local startcolumn = column_of_position(startposition) + -- local endcolumn = column_of_position(endposition) + -- + -- editor:SetSel(startposition,endposition) + + local startline = props['SelectionStartLine'] + local endline = props['SelectionEndLine'] + local startcolumn = props['SelectionStartColumn'] - 1 + local endcolumn = props['SelectionEndColumn'] - 1 + + local replacement = { } + local templine = '' + local indentation = string.rep(' ',startcolumn) + local selection = editor:GetSelText() + + selection = selection:gsub("[\n\r][\n\r]","\n") + selection = selection:gsub("\n\n+",' ' .. magicstring .. ' ') + selection = selection:gsub("^%s",'') + + for snippet in selection:gmatch("%S+") do + if snippet == magicstring then + replacement[#replacement+1] = templine + replacement[#replacement+1] = "" + templine = '' + elseif #templine + #snippet > length then + replacement[#replacement+1] = templine + templine = indentation .. snippet + elseif #templine == 0 then + templine = indentation .. snippet + else + templine = templine .. ' ' .. snippet + end + end + + replacement[#replacement+1] = templine + replacement[1] = replacement[1]:gsub("^%s+",'') + + if endcolumn == 0 then + replacement[#replacement+1] = "" + end + + editor:ReplaceSel(table.concat(replacement,"\n")) + +end + +function unwrap_text() + + local startposition = editor.SelectionStart + local endposition = editor.SelectionEnd + + if startposition == endposition then return end + + editor:HomeExtend() + editor:LineEndExtend() + + startposition = editor.SelectionStart + endposition = editor.SelectionEnd + + local magicstring = string.rep("<multiplelines/>", 2) + local selection = string.gsub(editor:GetSelText(),"[\n\r][\n\r]+", ' ' .. magicstring .. ' ') + local replacement = '' + + for snippet in selection:gmatch("%S+") do + if snippet == magicstring then + replacement = replacement .. "\n" + else + replacement = replacement .. snippet .. "\n" + end + end + + if endcolumn == 0 then replacement = replacement .. "\n" end + + editor:ReplaceSel(replacement) + +end + +function sort_text() + + local startposition = editor.SelectionStart + local endposition = editor.SelectionEnd + + if startposition == endposition then return end + + -- local startcolumn = column_of_position(startposition) + -- local endcolumn = column_of_position(endposition) + -- + -- editor:SetSel(startposition,endposition) + + local startline = props['SelectionStartLine'] + local endline = props['SelectionEndLine'] + local startcolumn = props['SelectionStartColumn'] - 1 + local endcolumn = props['SelectionEndColumn'] - 1 + + startposition = extend_to_start() + endposition = extend_to_end() + + local selection = string.gsub(editor:GetSelText(), "%s*$", '') + + list = string.grab(selection,"[^\n\r]+") + table.alphasort(list, startcolumn) + local replacement = table.concat(list, "\n") + + editor:GotoPos(startposition) + editor:SetSel(startposition,endposition) + + if endcolumn == 0 then replacement = replacement .. "\n" end + + editor:ReplaceSel(replacement) + +end + +function document_text() + + local startposition = editor.SelectionStart + local endposition = editor.SelectionEnd + + if startposition == endposition then return end + + startposition = extend_to_start() + endposition = extend_to_end() + + editor:SetSel(startposition,endposition) + + local filetype = getfiletype() + + local replacement = '' + for i = editor:LineFromPosition(startposition), editor:LineFromPosition(endposition) do + local str = editor:GetLine(i) + if filetype == 'xml' then + if str:find("^<%!%-%- .* %-%->%s*$") then + replacement = replacement .. str:gsub("^<%!%-%- (.*) %-%->(%s*)$","%1\n") + elseif not str:is_empty() then + replacement = replacement .. '<!-- ' .. str:gsub("(%s*)$",'') .. " -->\n" + else + replacement = replacement .. str + end + else + if str:find("^%%D%s+$") then + replacement = replacement .. "\n" + elseif str:find("^%%D ") then + replacement = replacement .. str:gsub("^%%D ",'') + else + replacement = replacement .. '%D ' .. str + end + end + end + + editor:ReplaceSel(replacement:gsub("[\n\r]$",'')) + +end + +function quote_text() + + local filetype, leftquotation, rightquotation = getfiletype(), '', '' + + if filetype == 'xml' then + leftquotation, rightquotation = "<quotation>", "</quotation>" + leftquote, rightquote = "<quotation>", "</quote>" + else + leftquotation, rightquotation = "\\quotation {", "}" + leftquote, rightquote = "\\quote {", "}" + end + + local replacement = editor:GetSelText() + replacement = replacement.gsub("\`\`(.-)\'\'", leftquotation .. "%1" .. rightquotation) + replacement = replacement.gsub("\"(.-)\"", leftquotation .. "%1" .. rightquotation) + replacement = replacement.gsub("\`(.-)\'", leftquote .. "%1" .. rightquote ) + replacement = replacement.gsub("\'(.-)\'", leftquote .. "%1" .. rightquote ) + editor:ReplaceSel(replacement) + +end + +function compound_text() + + local filetype = getfiletype() + + if filetype == 'xml' then + editor:ReplaceSel(string.gsub(editor:GetSelText(),"(>[^<%-][^<%-]+)([-\/])(%w%w+)","%1<compound token='%2'/>%3")) + else + editor:ReplaceSel(string.gsub(editor:GetSelText(),"([^\|])([-\/]+)([^\|])","%1|%2|%3")) + end + +end + +-- written while listening to Alanis Morissette's acoustic +-- Jagged Little Pill and Tori Amos' Beekeeper after +-- reinstalling on my good old ATH-7 + +local language = props["ctx.spellcheck.language"] +local wordsize = props["ctx.spellcheck.wordsize"] +local wordpath = props["ctx.spellcheck.wordpath"] + +if language == '' then language = 'uk' end +if wordsize == '' then wordsize = 4 else wordsize = tonumber(wordsize) end + +local wordfile = "" +local wordlist = {} +local worddone = 0 + +-- we use wordlist as a hash so that we can add entries without the +-- need to sort and also use a fast (built in) search + +-- function kpsewhich_file(filename,filetype,progname) +-- local progflag, typeflag = '', '' +-- local tempname = os.tmpname() +-- if progname then +-- progflag = " --progname=" .. progname .. " " +-- end +-- if filetype then +-- typeflag = " --format=" .. filetype .. " " +-- end +-- local command = "kpsewhich" .. progflag .. typeflag .. " " .. filename .. " > " .. tempname +-- os.execute(command) +-- for line in io.lines(tempname) do +-- return string.gsub(line, "\s*$", '') +-- end +-- end + +function check_text() + + local dlanguage = props["ctx.spellcheck.language"] + local dwordsize = props["ctx.spellcheck.wordsize"] + local dwordpath = props["ctx.spellcheck.wordpath"] + + if dlanguage ~= '' then dlanguage = tostring(language) end + if dwordsize ~= '' then dwordsize = tonumber(wordsize) end + + local firstline, skipfirst = editor:GetLine(0), false + local filetype, wordskip, wordgood = getfiletype(), '', '' + + if filetype == 'tex' then + wordskip = "\\" + elseif filetype == 'xml' then + wordskip = "<" + wordgood = ">" + end + + if props["ctx.spellcheck.language"] == 'auto' then + if filetype == 'tex' then + -- % version =1.0 language=uk + firstline = firstline:gsub("^%%%s*",'') + firstline = firstline:gsub("%s*$",'') + for key, val in firstline:gmatch("(%w+)=(%w+)") do + if key == "language" then + language = val + traceln("auto document language " .. "'" .. language .. "' (tex)") + end + end + skipfirst = true + elseif filetype == 'xml' then + -- <?xml version='1.0' language='uk' ?> + firstline = firstline:gsub("^%<%?xml%s*", '') + firstline = firstline:gsub("%s*%?%>%s*$", '') + for key, val in firstline:gmatch("(%w+)=[\"\'](.-)[\"\']") do + if key == "language" then + language = val + traceln("auto document language " .. "'" .. language .. "' (xml)") + end + end + skipfirst = true + end + end + + local fname = props["ctx.spellcheck.wordfile." .. language] + local fsize = props["ctx.spellcheck.wordsize." .. language] + + if fsize ~= '' then wordsize = tonumber(fsize) end + + if fname ~= '' and fname ~= wordfile then + wordfile, worddone, wordlist = fname, 0, {} + for filename in wordfile:gmatch("[^%,]+") do + if wordpath ~= '' then + filename = string.expand(wordpath) .. '/' .. filename + end + if io.exists(filename) then + traceln("loading " .. filename) + for line in io.lines(filename) do + if not line:find("^[\%\#\-]") then + str = line:gsub("%s*$", '') + rawset(wordlist,str,true) + worddone = worddone + 1 + end + end + else + traceln("unknown file '" .. filename .."'") + end + end + traceln(worddone .. " words loaded") + end + + reset_text() + + if worddone == 0 then + traceln("no (valid) language or wordfile specified") + else + traceln("start checking") + if wordskip ~= '' then + traceln("ignoring " .. wordskip .. "..." .. wordgood) + end + local i, j, lastpos, startpos, endpos, snippet, len, first = 0, 0, -1, 0, 0, '', 0, 0 + local ok, skip, ch = false, false, '' + if skipfirst then first = #firstline end + for k = first, editor.TextLength do + ch = editor:textrange(k,k+1) + if wordgood ~= '' and ch == wordgood then + skip = false + elseif ch == wordskip then + skip = true + end + if ch:find("%w") and not ch:find("%d") then + if not skip then + if ok then + endpos = k + else + startpos = k + endpos = k + ok = true + end + end + elseif ok and not skip then + len = endpos - startpos + 1 + if len >= wordsize then + snippet = editor:textrange(startpos,endpos+1) + i = i + 1 + if wordlist[snippet] or wordlist[snippet:lower()] then -- table.found(wordlist,snippet) + j = j + 1 + else + editor:StartStyling(startpos,INDICS_MASK) + editor:SetStyling(len,INDIC2_MASK) -- INDIC0_MASK+2 + end + end + ok = false + elseif wordgood == '' then + skip = (ch == wordskip) + end + end + traceln(i .. " words checked, " .. (i-j) .. " errors") + end + +end + +function reset_text() + editor:StartStyling(0,INDICS_MASK) + editor:SetStyling(editor.TextLength,INDIC_PLAIN) +end + +-- menu + +local menuactions = {} +local menufunctions = {} + +function UserListShow(menutrigger, menulist) + local menuentries = {} + local list = string.grab(menulist,"[^%|]+") + menuactions = {} + for i=1, #list do + if list[i] ~= '' then + for key, val in list[i]:gmatch("%s*(.+)=(.+)%s*") do + menuentries[#menuentries+1] = key + menuactions[key] = val + end + end + end + local menustring = table.concat(menuentries,'|') + if menustring == "" then + traceln("There are no templates defined for this file type.") + else + editor.AutoCSeparator = string.byte('|') + editor:UserListShow(menutrigger,menustring) + editor.AutoCSeparator = string.byte(' ') + end +end + +function OnUserListSelection(trigger,choice) + if menufunctions[trigger] and menuactions[choice] then + return menufunctions[trigger](menuactions[choice]) + else + return false + end +end + +-- main menu + +local menutrigger = 12 + +function show_menu(menulist) + UserListShow(menutrigger, menulist) +end + +function process_menu(action) + if not action:find("%(%)$") then + assert(loadstring(action .. "()"))() + else + assert(loadstring(action))() + end +end + +menufunctions[12] = process_menu + +-- templates + +local templatetrigger = 13 + +local ctx_template_paths = { "./ctx-templates", "../ctx-templates", "../../ctx-templates" } +local ctx_auto_templates = false +local ctx_template_list = "" + +local ctx_path_list = {} +local ctx_path_done = {} +local ctx_path_name = {} + +function ctx_list_loaded(path) + return ctx_path_list[path] and #ctx_path_list[path] > 0 +end + +function insert_template(templatelist) + if props["ctx.template.scan"] == "yes" then + local path = props["FileDir"] + local rescan = props["ctx.template.rescan"] == "yes" + local suffix = props["ctx.template.suffix." .. props["FileExt"]] -- alas, no suffix expansion here + local current = path .. "+" .. props["FileExt"] + if rescan then + print("re-scanning enabled") + end + ctx_template_list = "" + if not ctx_path_done[path] or rescan then + local pattern = "*.*" + for i, pathname in ipairs(ctx_template_paths) do + print("scanning " .. path:gsub("\\","/") .. "/" .. pathname) + ctx_path_name[path] = pathname + ctx_path_list[path] = get_dir_list(pathname .. "/" .. pattern) + if ctx_list_loaded(path) then + print("finished locating template files") + break + end + end + if ctx_list_loaded(path) then + print(#ctx_path_list[path] .. " template files found") + else + print("no template files found") + end + end + if ctx_list_loaded(path) then + ctx_template_list = "" + local pattern = "%." .. suffix .. "$" + local n = 0 + for j, filename in ipairs(ctx_path_list[path]) do + if filename:find(pattern) then + n = n + 1 + local menuname = filename:gsub("%..-$","") + if ctx_template_list ~= "" then + ctx_template_list = ctx_template_list .. "|" + end + ctx_template_list = ctx_template_list .. menuname .. "=" .. ctx_path_name[path] .. "/" .. filename + end + end + if not ctx_path_done[path] then + print(n .. " suitable template files found") + end + end + ctx_path_done[path] = true + if ctx_template_list == "" then + ctx_auto_templates = false + else + ctx_auto_templates = true + templatelist = ctx_template_list + end + else + ctx_auto_templates = false + end + if templatelist ~= "" then + UserListShow(templatetrigger, templatelist) + end +end + + +-- ctx.template.[whatever].[filetype] +-- ctx.template.[whatever].data.[filetype] +-- ctx.template.[whatever].file.[filetype] +-- ctx.template.[whatever].list.[filetype] + +function process_template_one(action) + local text = nil + if ctx_auto_templates then + local f = io.open(action,"r") + if f then + text = string.gsub(f:read("*all"),"\n$","") + f:close() + else + print("unable to auto load template file " .. text) + text = nil + end + end + if not text or text == "" then + text = props["ctx.template." .. action .. ".file"] + if not text or text == "" then + text = props["ctx.template." .. action .. ".data"] + if not text or text == "" then + text = props["ctx.template." .. action] + end + else + local f = io.open(text,"r") + if f then + text = string.gsub(f:read("*all"),"\n$","") + f:close() + else + print("unable to load template file " .. text) + text = nil + end + end + end + if text then + text = text:gsub("\\n","\n") + local pos = text:find("%?") + text = text:gsub("%?","") + editor:insert(editor.CurrentPos,text) + if pos then + editor.CurrentPos = editor.CurrentPos + pos - 1 + editor.SelectionStart = editor.CurrentPos + editor.SelectionEnd = editor.CurrentPos + editor:GotoPos(editor.CurrentPos) + end + end +end + +menufunctions[13] = process_template_one +menufunctions[14] = process_template_two + +-- command.name.26.*=Open Logfile +-- command.subsystem.26.*=3 +-- command.26.*=open_log +-- command.save.before.26.*=2 +-- command.groupundo.26.*=yes +-- command.shortcut.26.*=Ctrl+E + +function open_log() + scite.Open(props['FileName'] .. ".log") +end diff --git a/scripts/context/perl/makempy.pl b/scripts/context/perl/makempy.pl index f263dd425..7cba2e1a6 100644 --- a/scripts/context/perl/makempy.pl +++ b/scripts/context/perl/makempy.pl @@ -289,7 +289,7 @@ sub construct_mpy_file { error("unable to open $mpyfile file") } print MPY "% mpochecksum : $mpochecksum\n" ; my $copying = my $n = 0 ; - while (<TMP>) + while (<TMP>) # a simple sub is faster { if (s/beginfig/begingraphictextfig/o) { print MPY $_ ; $copying = 1 ; ++$n } elsif (s/endfig/endgraphictextfig/o) diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua index 7b379151a..a2e747902 100644 --- a/tex/context/base/attr-ini.lua +++ b/tex/context/base/attr-ini.lua @@ -17,6 +17,8 @@ if not modules then modules = { } end modules ['attr-ini'] = { nodes = nodes or { } +local format, concat = string.format, table.concat + -- This is not the most ideal place, but it will do. Maybe we need to move -- attributes to node-att.lua. @@ -222,7 +224,7 @@ do end end else - texio.write_nl(string.format("undefined attribute %s",name)) + texio.write_nl(format("undefined attribute %s",name)) end end if done then @@ -482,9 +484,6 @@ function states.collect(str) end function states.flush() ---~ for _, c in ipairs(states.collected) do ---~ tex.sprint(tex.ctxcatcodes,c) ---~ end local collected = states.collected if #collected > 0 then for i=1,#collected do @@ -495,7 +494,7 @@ function states.flush() end function states.check() - texio.write_nl(table.concat(states.collected,"\n")) + texio.write_nl(concat(states.collected,"\n")) end -- @@ -529,7 +528,7 @@ colors.triggering = true -- colors.strings = colors.strings or { } -- -- if environment.initex then --- colors.strings[color] = "return colors." .. colorspace .. "(" .. table.concat({...},",") .. ")" +-- colors.strings[color] = "return colors." .. colorspace .. "(" .. concat({...},",") .. ")" -- end -- -- input.storage.register(true,"colors/data", colors.strings, "colors.data") -- evaluated @@ -557,10 +556,8 @@ colors.model = "all" do - local min = math.min - local max = math.max - local format = string.format - local concat = table.concat + local min = math.min + local max = math.max local function rgbdata(r,g,b) -- dodo: backends.pdf.rgbdata return backends.pdf.literal(format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b)) @@ -644,7 +641,7 @@ do if not v then local gray = graydata(0) d = { gray, gray, gray, gray } - logs.report("attributes",string.format("unable to revive color %s",n or "?")) + logs.report("attributes",format("unable to revive color %s",n or "?")) else local kind, gray, rgb, cmyk = v[1], graydata(v[2]), rgbdata(v[3],v[4],v[5]), cmykdata(v[6],v[7],v[8],v[9]) if kind == 2 then @@ -679,7 +676,7 @@ function colors.setmodel(attribute,name) end function colors.register(attribute, name, colorspace, ...) -- passing 9 vars is faster - local stamp = string.format(colors.stamps[colorspace], ...) + local stamp = format(colors.stamps[colorspace], ...) local color = colors.registered[stamp] if not color then color = #colors.values+1 @@ -722,18 +719,18 @@ input.storage.register(false, "transparencies/registered", transparencies.regist input.storage.register(false, "transparencies/values", transparencies.values, "transparencies.values") function transparencies.reference(n) - return backends.pdf.literal(string.format("/Tr%s gs",n)) + return backends.pdf.literal(format("/Tr%s gs",n)) end function transparencies.register(name,a,t) - local stamp = string.format(transparencies.template,a,t) + local stamp = format(transparencies.template,a,t) local n = transparencies.registered[stamp] if not n then n = #transparencies.data+1 transparencies.data[n] = transparencies.reference(n) transparencies.values[n] = { a, t } transparencies.registered[stamp] = n - states.collect(string.format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,a,t)) -- too many, but experimental anyway + states.collect(format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,a,t)) -- too many, but experimental anyway end return transparencies.registered[stamp] end @@ -744,10 +741,10 @@ function transparencies.reviver(n) local v = transparencies.values[n] if not v then d = transparencies.reference(0) - logs.report("attributes",string.format("unable to revive transparency %s",n or "?")) + logs.report("attributes",format("unable to revive transparency %s",n or "?")) else d = transparencies.reference(n) - states.collect(string.format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,v[1],v[2])) + states.collect(format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,v[1],v[2])) end transparencies.data[n] = d end @@ -777,8 +774,8 @@ overprints = overprints or { } overprints.data = overprints.data or { } overprints.enabled = false -overprints.data[1] = backends.pdf.literal(string.format("/GSoverprint gs")) -overprints.data[2] = backends.pdf.literal(string.format("/GSknockout gs")) +overprints.data[1] = backends.pdf.literal(format("/GSoverprint gs")) +overprints.data[2] = backends.pdf.literal(format("/GSknockout gs")) overprints.none = overprints.data[1] @@ -805,8 +802,8 @@ negatives = netatives or { } negatives.data = negatives.data or { } negatives.enabled = false -negatives.data[1] = backends.pdf.literal(string.format("/GSpositive gs")) -negatives.data[2] = backends.pdf.literal(string.format("/GSnegative gs")) +negatives.data[1] = backends.pdf.literal(format("/GSpositive gs")) +negatives.data[2] = backends.pdf.literal(format("/GSnegative gs")) negatives.none = negatives.data[1] @@ -839,7 +836,7 @@ input.storage.register(false, "effects/registered", effects.registered, "effects input.storage.register(false, "effects/data", effects.data, "effects.data") function effects.register(effect,stretch,rulethickness) - local stamp = string.format(effects.stamp,effect,stretch,rulethickness) + local stamp = format(effects.stamp,effect,stretch,rulethickness) local n = effects.registered[stamp] if not n then n = #effects.data+1 @@ -862,7 +859,7 @@ function effects.reference(effect,stretch,rulethickness) -- always, no zero test (removed) rulethickness = number.dimenfactors["bp"]*rulethickness effect = backends.pdf.effects[effect] or backends.pdf.effects['normal'] - return backends.pdf.literal(string.format("%s Tc %s w %s Tr",stretch,rulethickness,effect)) -- watch order + return backends.pdf.literal(format("%s Tc %s w %s Tr",stretch,rulethickness,effect)) -- watch order end effects.none = effects.reference(0,0,0) -- faster: backends.pdf.literal("0 Tc 0 w 0 Tr") diff --git a/tex/context/base/char-def.lua b/tex/context/base/char-def.lua index 59f8ed4ac..b4ca759bf 100644 --- a/tex/context/base/char-def.lua +++ b/tex/context/base/char-def.lua @@ -226,6 +226,7 @@ characters.data={ ["adobename"]="quotedbl", ["category"]="po", ["cjkwd"]="na", + ["contextname"]="quotedbl", ["description"]="QUOTATION MARK", ["linebreak"]="qu", ["unicodeslot"]=0x0022, -- " @@ -792,6 +793,8 @@ characters.data={ ["cjkwd"]="na", ["contextname"]="textasciicircum", ["description"]="CIRCUMFLEX ACCENT", + ["mathclass"]="accent", + ["mathname"]="widehat", ["linebreak"]="al", ["unicodeslot"]=0x005E, -- ^ }, @@ -1088,6 +1091,7 @@ characters.data={ ["adobename"]="bar", ["category"]="sm", ["cjkwd"]="na", + ["contextname"]="textbar", ["description"]="VERTICAL LINE", ["linebreak"]="ba", ["mathclass"]="binary", @@ -1112,6 +1116,8 @@ characters.data={ ["contextname"]="textasciitilde", ["description"]="TILDE", ["linebreak"]="al", + ["mathclass"]="accent", + ["mathname"]="widetilde", ["unicodeslot"]=0x007E, -- ~ }, { @@ -8129,7 +8135,7 @@ characters.data={ ["description"]="GREEK SMALL LETTER EPSILON", ["linebreak"]="al", ["mathclass"]="variable", - ["mathname"]="varepsilon", + ["mathname"]="epsilon", ["uccode"]=0x0395, ["unicodeslot"]=0x03B5, -- ε }, @@ -8475,6 +8481,7 @@ characters.data={ ["description"]="GREEK PHI SYMBOL", ["linebreak"]="al", ["mathclass"]="variable", + ["mathname"]="phi", ["specials"]={ "compat", 0x03C6 }, ["uccode"]=0x03A6, ["unicodeslot"]=0x03D5, -- ϕ @@ -8717,6 +8724,8 @@ characters.data={ ["contextname"]="greekepsilonalt", ["description"]="GREEK LUNATE EPSILON SYMBOL", ["linebreak"]="al", + ["mathclass"]="variable", + ["mathname"]="varepsilon", ["specials"]={ "compat", 0x03B5 }, ["uccode"]=0x0395, ["unicodeslot"]=0x03F5, -- ϵ @@ -45705,6 +45714,8 @@ characters.data={ ["cjkwd"]="a", ["description"]="N-ARY PRODUCT", ["linebreak"]="ai", + ["mathclass"]="limop", + ["mathname"]="prod", ["unicodeslot"]=0x220F, -- ∏ }, [0x2210]={ @@ -45719,6 +45730,8 @@ characters.data={ ["cjkwd"]="a", ["description"]="N-ARY SUMMATION", ["linebreak"]="ai", + ["mathclass"]="limop", + ["mathname"]="sum", ["unicodeslot"]=0x2211, -- ∑ }, [0x2212]={ @@ -45903,6 +45916,8 @@ characters.data={ ["cjkwd"]="a", ["description"]="INTEGRAL", ["linebreak"]="ai", + ["mathclass"]="limop", + ["mathname"]="intop", ["unicodeslot"]=0x222B, -- ∫ }, [0x222C]={ diff --git a/tex/context/base/char-def.tex b/tex/context/base/char-def.tex index 1c0b061dc..39137241d 100644 --- a/tex/context/base/char-def.tex +++ b/tex/context/base/char-def.tex @@ -29,8 +29,9 @@ %D The codes are stored in the format, so we don't need to reinitialize %D them (unless of course we have adapted the table). +\ctxlua{characters.setcodes()} + % \startruntimeluacode - \ctxlua{characters.setcodes()} % \ctxlua{characters.setpdfunicodes()}% pdftounicode mappings can only be done runtime % \stopruntimeluacode diff --git a/tex/context/base/char-ini.lua b/tex/context/base/char-ini.lua index 4750e929a..f44eb8aca 100644 --- a/tex/context/base/char-ini.lua +++ b/tex/context/base/char-ini.lua @@ -9,9 +9,7 @@ if not modules then modules = { } end modules ['char-ini'] = { utf = utf or unicode.utf tex = tex or { } -function tex.ctxprint(...) - tex.sprint(tex.ctxcatcodes,...) -end +local format = string.format --[[ldx-- <p>This module implements some methods and creates additional datastructured @@ -133,9 +131,9 @@ function characters.context.show(n) local d = characters.data[n] if d then local function entry(label,name) - tex.ctxprint(string.format("\\NC %s\\NC %s\\NC\\NR",label,characters.valid(d[name]))) + tex.sprint(tex.ctxcatcodes,format("\\NC %s\\NC %s\\NC\\NR",label,characters.valid(d[name]))) end - tex.ctxprint("\\starttabulate[|Tl|Tl|]") + tex.sprint(tex.ctxcatcodes,"\\starttabulate[|Tl|Tl|]") entry("unicode index" , "unicodeslot") entry("context name" , "contextname") entry("adobe name" , "adobename") @@ -144,7 +142,7 @@ function characters.context.show(n) entry("uppercase code", "uccode") entry("lowercase code", "lccode") entry("specials" , "specials") - tex.ctxprint("\\stoptabulate ") + tex.sprint(tex.ctxcatcodes,"\\stoptabulate ") end end @@ -154,7 +152,7 @@ use the table. After all, we have this information available anyway.</p> --ldx]]-- function characters.makeactive(n,name) - tex.sprint(string.format("\\catcode%s=13\\unexpanded\\def %s{\\%s}",n,utf.char(n),name)) + tex.sprint(tex.ctxcatcodes,format("\\catcode%s=13\\unexpanded\\def %s{\\%s}",n,utf.char(n),name)) end function tex.uprint(n) @@ -226,14 +224,14 @@ end --ldx]]-- function characters.setcodes() - local flush, tc = tex.sprint, tex.ctxcatcodes + local flush, tc, format = tex.sprint, tex.ctxcatcodes, string.format for code, chr in pairs(characters.data) do local cc = chr.category if cc == 'll' or cc == 'lu' or cc == 'lt' then local lc, uc = chr.lccode, chr.uccode if not lc then chr.lccode, lc = code, code end if not uc then chr.uccode, uc = code, code end - flush(tc, '\\setcclcuc '.. code .. ' ' .. lc .. ' ' .. uc .. ' ') + flush(tc, format("\\setcclcuc %i %i %i ",code,lc,uc)) end end end @@ -298,7 +296,7 @@ to the checking.</p> --ldx]]-- function characters.hexindex(n) - return string.format("%04X", characters.valid(characters.data[characters.number(n)].unicodeslot)) + return format("%04X", characters.valid(characters.data[characters.number(n)].unicodeslot)) end function characters.contextname(n) @@ -391,7 +389,6 @@ characters.pdftex.make_pdf_to_unicodetable("pdfr-def.tex") characters.pdftex = characters.pdftex or { } function characters.pdftex.make_pdf_to_unicodetable(filename) ---~ local sf = string.format --~ f = io.open(filename,'w') --~ if f then --~ f:write("% This file is generated with Luatex using the\n") @@ -400,7 +397,7 @@ function characters.pdftex.make_pdf_to_unicodetable(filename) --~ f:write("\\ifx\\pdfglyphtounicode\\undefined\\endinput\\fi\n") -- just to be sure --~ for _, v in pairs(characters.data) do --~ if v.adobename then ---~ f:write(sf("\\pdfglyphtounicode{%s}{%04X}", v.adobename, v.unicodeslot)) +--~ f:write(format("\\pdfglyphtounicode{%s}{%04X}", v.adobename, v.unicodeslot)) --~ end --~ end --~ f:write("%\n") diff --git a/tex/context/base/char-utf.tex b/tex/context/base/char-utf.tex index 68036bf4c..2e7156962 100644 --- a/tex/context/base/char-utf.tex +++ b/tex/context/base/char-utf.tex @@ -35,15 +35,17 @@ }% \to \everyjob -%D This is a hack, and only meant for special situations. We don't -%D support this in for instance verbatim. The active characters map -%D onto the \CONTEXT\ names and font handling etc. is up to the user. - -\registerctxluafile{char-act}{1.001} - -\def\enableactiveutf {\ctxlua{characters.active.enable()}} -\def\disableactiveutf{\ctxlua{characters.active.disable()}} -\def\testactiveutf #1{\ctxlua{characters.active.test("#1")}} +% %D This is a hack, and only meant for special situations. We don't +% %D support this in for instance verbatim. The active characters map +% %D onto the \CONTEXT\ names and font handling etc. is up to the user. +% +% %D This feature is obsolete. +% +% \registerctxluafile{char-act}{1.001} +% +% \def\enableactiveutf {\ctxlua{characters.active.enable()}} +% \def\disableactiveutf{\ctxlua{characters.active.disable()}} +% \def\testactiveutf #1{\ctxlua{characters.active.test("#1")}} %D Usage: %D diff --git a/tex/context/base/colo-new.lua b/tex/context/base/colo-new.lua index d039e47c7..fb1457070 100644 --- a/tex/context/base/colo-new.lua +++ b/tex/context/base/colo-new.lua @@ -22,160 +22,152 @@ backends = backends or { } backends.pdf = backends.pdf or { } backend = backends.pdf -do +local texsprint, format, concat = tex.sprint, string.format, table.concat - -- maybe combine spotcolorname +local s_template_g = "\\dodoPDFregistergrayspotcolor{%s}{%s}{%s}{%s}{%s}" -- n f d p s (p can go away) +local s_template_r = "\\dodoPDFregisterrgbspotcolor {%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p r g b +local s_template_c = "\\dodoPDFregistercmykspotcolor{%s}{%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p c m y k - local s_template_g = "\\dodoPDFregistergrayspotcolor{%s}{%s}{%s}{%s}{%s}" -- n f d p s (p can go away) - local s_template_r = "\\dodoPDFregisterrgbspotcolor {%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p r g b - local s_template_c = "\\dodoPDFregistercmykspotcolor{%s}{%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p c m y k +function backends.pdf.registergrayspotcolor(n,f,d,p,s) states.collect(s_template_g:format(n,f,d,p,s)) end +function backends.pdf.registerrgbspotcolor (n,f,d,p,r,g,b) states.collect(s_template_r:format(n,f,d,p,r,g,b)) end +function backends.pdf.registercmykspotcolor(n,f,d,p,c,m,y,k) states.collect(s_template_c:format(n,f,d,p,c,m,y,k)) end - function backends.pdf.registergrayspotcolor(n,f,d,p,s) states.collect(s_template_g:format(n,f,d,p,s)) end - function backends.pdf.registerrgbspotcolor (n,f,d,p,r,g,b) states.collect(s_template_r:format(n,f,d,p,r,g,b)) end - function backends.pdf.registercmykspotcolor(n,f,d,p,c,m,y,k) states.collect(s_template_c:format(n,f,d,p,c,m,y,k)) end +local m_template_g = "\\doPDFregistergrayindexcolor{%s}{%s}{%s}{%s}{%s}" -- n f d p s (p can go away) +local m_template_r = "\\doPDFregisterrgbindexcolor {%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p r g b +local m_template_c = "\\doPDFregistercmykindexcolor{%s}{%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p c m y k - local m_template_g = "\\doPDFregistergrayindexcolor{%s}{%s}{%s}{%s}{%s}" -- n f d p s (p can go away) - local m_template_r = "\\doPDFregisterrgbindexcolor {%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p r g b - local m_template_c = "\\doPDFregistercmykindexcolor{%s}{%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p c m y k +function backends.pdf.registergrayindexcolor(n,f,d,p,s) states.collect(m_template_g:format(n,f,d,p,s)) end +function backends.pdf.registerrgbindexcolor (n,f,d,p,r,g,b) states.collect(m_template_r:format(n,f,d,p,r,g,b)) end +function backends.pdf.registercmykindexcolor(n,f,d,p,c,m,y,k) states.collect(m_template_c:format(n,f,d,p,c,m,y,k)) end - function backends.pdf.registergrayindexcolor(n,f,d,p,s) states.collect(m_template_g:format(n,f,d,p,s)) end - function backends.pdf.registerrgbindexcolor (n,f,d,p,r,g,b) states.collect(m_template_r:format(n,f,d,p,r,g,b)) end - function backends.pdf.registercmykindexcolor(n,f,d,p,c,m,y,k) states.collect(m_template_c:format(n,f,d,p,c,m,y,k)) end +local s_template_e = "\\doPDFregisterspotcolorname{%s}{%s}" -- name, e - local s_template_e = "\\doPDFregisterspotcolorname{%s}{%s}" -- name, e - - function backends.pdf.registerspotcolorname(name,e) - if e and e ~= "" then - tex.sprint(tex.ctxcatcodes,string.format(s_template_e,name,e)) -- todo in new backend: e:gsub(" ","#20") - end +function backends.pdf.registerspotcolorname(name,e) + if e and e ~= "" then + texsprint(tex.ctxcatcodes,format(s_template_e,name,e)) -- todo in new backend: e:gsub(" ","#20") end - end ctx = ctx or { } ctx.aux = ctx.aux or { } -do - - local format, sprint = string.format, tex.sprint - - local a_l_c_template = "\\setevalue{(ca:%s)}{%s}" .. - "\\setevalue{(cs:%s)}{\\dosetattribute{color}{%s}}" - local a_g_c_template = "\\setxvalue{(ca:%s)}{%s}" .. - "\\setxvalue{(cs:%s)}{\\dosetattribute{color}{%s}}" - local f_l_c_template = "\\setvalue {(ca:%s)}{\\doinheritca{%s}}" .. - "\\setvalue {(cs:%s)}{\\doinheritcs{%s}}" - local f_g_c_template = "\\setgvalue{(ca:%s)}{\\doinheritca{%s}}" .. - "\\setgvalue{(cs:%s)}{\\doinheritcs{%s}}" - local r_l_c_template = "\\letbeundefined{(ca:%s)}" .. - "\\letbeundefined{(cs:%s)}" - local r_g_c_template = "\\global\\letbeundefined{(ca:%s)}" .. - "\\global\\letbeundefined{(cs:%s)}" - - local a_l_t_template = "\\setevalue{(ta:%s)}{%s}" .. - "\\setevalue{(ts:%s)}{\\dosetattribute{transparency}{%s}}" - local a_g_t_template = "\\setxvalue{(ta:%s)}{%s}" .. - "\\setxvalue{(ts:%s)}{\\dosetattribute{transparency}{%s}}" - local f_l_t_template = "\\setvalue {(ta:%s)}{\\doinheritta{%s}}" .. - "\\setvalue {(ts:%s)}{\\doinheritts{%s}}" - local f_g_t_template = "\\setgvalue{(ta:%s)}{\\doinheritta{%s}}" .. - "\\setgvalue{(ts:%s)}{\\doinheritts{%s}}" - local r_l_t_template = "\\letbeundefined{(ta:%s)}" .. - "\\letbeundefined{(ts:%s)}" - local r_g_t_template = "\\global\\letbeundefined{(ta:%s)}" .. - "\\global\\letbeundefined{(ts:%s)}" - - function ctx.aux.definecolor(name, ca, global) - if ca and ca > 0 then - if global then - sprint(tex.ctxcatcodes,a_g_c_template:format(name, ca, name, ca)) - else - sprint(tex.ctxcatcodes,a_l_c_template:format(name, ca, name, ca)) - end +local a_l_c_template = "\\setevalue{(ca:%s)}{%s}" .. + "\\setevalue{(cs:%s)}{\\dosetattribute{color}{%s}}" +local a_g_c_template = "\\setxvalue{(ca:%s)}{%s}" .. + "\\setxvalue{(cs:%s)}{\\dosetattribute{color}{%s}}" +local f_l_c_template = "\\setvalue {(ca:%s)}{\\doinheritca{%s}}" .. + "\\setvalue {(cs:%s)}{\\doinheritcs{%s}}" +local f_g_c_template = "\\setgvalue{(ca:%s)}{\\doinheritca{%s}}" .. + "\\setgvalue{(cs:%s)}{\\doinheritcs{%s}}" +local r_l_c_template = "\\letbeundefined{(ca:%s)}" .. + "\\letbeundefined{(cs:%s)}" +local r_g_c_template = "\\global\\letbeundefined{(ca:%s)}" .. + "\\global\\letbeundefined{(cs:%s)}" + +local a_l_t_template = "\\setevalue{(ta:%s)}{%s}" .. + "\\setevalue{(ts:%s)}{\\dosetattribute{transparency}{%s}}" +local a_g_t_template = "\\setxvalue{(ta:%s)}{%s}" .. + "\\setxvalue{(ts:%s)}{\\dosetattribute{transparency}{%s}}" +local f_l_t_template = "\\setvalue {(ta:%s)}{\\doinheritta{%s}}" .. + "\\setvalue {(ts:%s)}{\\doinheritts{%s}}" +local f_g_t_template = "\\setgvalue{(ta:%s)}{\\doinheritta{%s}}" .. + "\\setgvalue{(ts:%s)}{\\doinheritts{%s}}" +local r_l_t_template = "\\letbeundefined{(ta:%s)}" .. + "\\letbeundefined{(ts:%s)}" +local r_g_t_template = "\\global\\letbeundefined{(ta:%s)}" .. + "\\global\\letbeundefined{(ts:%s)}" + +function ctx.aux.definecolor(name, ca, global) + if ca and ca > 0 then + if global then + texsprint(tex.ctxcatcodes,a_g_c_template:format(name, ca, name, ca)) else - if global then - sprint(tex.ctxcatcodes,r_g_c_template:format(name, name)) - else - sprint(tex.ctxcatcodes,r_l_c_template:format(name, name)) - end + texsprint(tex.ctxcatcodes,a_l_c_template:format(name, ca, name, ca)) + end + else + if global then + texsprint(tex.ctxcatcodes,r_g_c_template:format(name, name)) + else + texsprint(tex.ctxcatcodes,r_l_c_template:format(name, name)) end end - function ctx.aux.inheritcolor(name, ca, global) - if ca and ca ~= "" then - if global then - sprint(tex.ctxcatcodes,f_g_c_template:format(name, ca, name, ca)) - else - sprint(tex.ctxcatcodes,f_l_c_template:format(name, ca, name, ca)) - end +end +function ctx.aux.inheritcolor(name, ca, global) + if ca and ca ~= "" then + if global then + texsprint(tex.ctxcatcodes,f_g_c_template:format(name, ca, name, ca)) else - if global then - sprint(tex.ctxcatcodes,r_g_c_template:format(name, name)) - else - sprint(tex.ctxcatcodes,r_l_c_template:format(name, name)) - end + texsprint(tex.ctxcatcodes,f_l_c_template:format(name, ca, name, ca)) + end + else + if global then + texsprint(tex.ctxcatcodes,r_g_c_template:format(name, name)) + else + texsprint(tex.ctxcatcodes,r_l_c_template:format(name, name)) end end - function ctx.aux.definetransparent(name, ta, global) - if ta and ta > 0 then - if global then - sprint(tex.ctxcatcodes,a_g_t_template:format(name, ta, name, ta)) - else - sprint(tex.ctxcatcodes,a_l_t_template:format(name, ta, name, ta)) - end +end +function ctx.aux.definetransparent(name, ta, global) + if ta and ta > 0 then + if global then + texsprint(tex.ctxcatcodes,a_g_t_template:format(name, ta, name, ta)) else - if global then - sprint(tex.ctxcatcodes,r_g_t_template:format(name, name)) - else - sprint(tex.ctxcatcodes,r_l_t_template:format(name, name)) - end + texsprint(tex.ctxcatcodes,a_l_t_template:format(name, ta, name, ta)) + end + else + if global then + texsprint(tex.ctxcatcodes,r_g_t_template:format(name, name)) + else + texsprint(tex.ctxcatcodes,r_l_t_template:format(name, name)) end end - function ctx.aux.inherittransparent(name, ta, global) - if ta and ta ~= "" then - if global then - sprint(tex.ctxcatcodes,f_g_t_template:format(name, ta, name, ta)) - else - sprint(tex.ctxcatcodes,f_l_t_template:format(name, ta, name, ta)) - end +end +function ctx.aux.inherittransparent(name, ta, global) + if ta and ta ~= "" then + if global then + texsprint(tex.ctxcatcodes,f_g_t_template:format(name, ta, name, ta)) else - if global then - sprint(tex.ctxcatcodes,r_g_t_template:format(name, name)) - else - sprint(tex.ctxcatcodes,r_l_t_template:format(name, name)) - end + texsprint(tex.ctxcatcodes,f_l_t_template:format(name, ta, name, ta)) + end + else + if global then + texsprint(tex.ctxcatcodes,r_g_t_template:format(name, name)) + else + texsprint(tex.ctxcatcodes,r_l_t_template:format(name, name)) end end +end - local transparent = { - none = 0, - normal = 1, - multiply = 2, - screen = 3, - overlay = 4, - softlight = 5, - hardlight = 6, - colordodge = 7, - colorburn = 8, - darken = 9, - lighten = 10, - difference = 11, - exclusion = 12, - } - - -- By coupling we are downward compatible. When we decouple we need to do more tricky - -- housekeeping (e.g. persist color independent transparencies when color bound ones - -- are nil. - - ctx.couplecolors = true - - function ctx.definetransparency(name,n) - transparent[name] = n - end +local transparent = { + none = 0, + normal = 1, + multiply = 2, + screen = 3, + overlay = 4, + softlight = 5, + hardlight = 6, + colordodge = 7, + colorburn = 8, + darken = 9, + lighten = 10, + difference = 11, + exclusion = 12, +} + +-- By coupling we are downward compatible. When we decouple we need to do more tricky +-- housekeeping (e.g. persist color independent transparencies when color bound ones +-- are nil. + +ctx.couplecolors = true - local registered = { } +function ctx.definetransparency(name,n) + transparent[name] = n +end + +local registered = { } - local function registerspotcolor(parent,name,parentnumber,e,f,d,p) -if not registered[parentnumber] then +local function registerspotcolor(parent,name,parentnumber,e,f,d,p) + if not registered[parentnumber] then local v = colors.values[parentnumber] if v then local kind = v[1] @@ -188,12 +180,12 @@ if not registered[parentnumber] then end backends.pdf.registerspotcolorname(parent,e) end - registered[parentnumber] = true -end + registered[parentnumber] = true end +end - local function registermultitonecolor(parent,name,parentnumber,e,f,d,p) -- same as spot but different template -if not registered[parentnumber] then +local function registermultitonecolor(parent,name,parentnumber,e,f,d,p) -- same as spot but different template + if not registered[parentnumber] then local v = colors.values[parentnumber] if v then local kind = v[1] @@ -205,359 +197,351 @@ if not registered[parentnumber] then backend.registercmykindexcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) end end - registered[parentnumber] = true -end + registered[parentnumber] = true end +end - function ctx.definesimplegray(name,s) - return colors.register('color',name,'gray',s) -- we still need to get rid of 'color' - end +function ctx.definesimplegray(name,s) + return colors.register('color',name,'gray',s) -- we still need to get rid of 'color' +end - function ctx.defineprocesscolor(name,str,global,freeze) -- still inconsistent color vs transparent - local t = str:split_settings() - if t then - if t.h then - local r, g, b =string.match(t.h .. "000000","(..)(..)(..)") - ctx.aux.definecolor(name, colors.register('color',name,'rgb',(tonumber(r,16) or 0)/256,(tonumber(g,16) or 0)/256,(tonumber(b,16) or 0)/256 ), global) - elseif t.r or t.g or t.b then - ctx.aux.definecolor(name, colors.register('color',name,'rgb', tonumber(t.r) or 0, tonumber(t.g) or 0, tonumber(t.b) or 0 ), global) - elseif t.c or t.m or t.y or t.k then - ctx.aux.definecolor(name, colors.register('color',name,'cmyk',tonumber(t.c) or 0, tonumber(t.m) or 0, tonumber(t.y) or 0, tonumber(t.k) or 0), global) - else - ctx.aux.definecolor(name, colors.register('color',name,'gray',tonumber(t.s) or 0), global) - end - if t.a and t.t then - ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) - elseif ctx.couplecolors then ---~ ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up - ctx.aux.definetransparent(name, 0, global) -- can be sped up - end - elseif freeze then - local ca = attributes.list[attributes.numbers['color']] [str] - local ta = attributes.list[attributes.numbers['transparency']][str] - if ca then - ctx.aux.definecolor(name, ca, global) - end - if ta then - ctx.aux.definetransparent(name, ta, global) - end +function ctx.defineprocesscolor(name,str,global,freeze) -- still inconsistent color vs transparent + local t = str:split_settings() + if t then + if t.h then + local r, g, b =string.match(t.h .. "000000","(..)(..)(..)") + ctx.aux.definecolor(name, colors.register('color',name,'rgb',(tonumber(r,16) or 0)/256,(tonumber(g,16) or 0)/256,(tonumber(b,16) or 0)/256 ), global) + elseif t.r or t.g or t.b then + ctx.aux.definecolor(name, colors.register('color',name,'rgb', tonumber(t.r) or 0, tonumber(t.g) or 0, tonumber(t.b) or 0 ), global) + elseif t.c or t.m or t.y or t.k then + ctx.aux.definecolor(name, colors.register('color',name,'cmyk',tonumber(t.c) or 0, tonumber(t.m) or 0, tonumber(t.y) or 0, tonumber(t.k) or 0), global) else - ctx.aux.inheritcolor(name, str, global) - ctx.aux.inherittransparent(name, str, global) + ctx.aux.definecolor(name, colors.register('color',name,'gray',tonumber(t.s) or 0), global) + end + if t.a and t.t then + ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) + elseif ctx.couplecolors then +--~ ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up + ctx.aux.definetransparent(name, 0, global) -- can be sped up + end + elseif freeze then + local ca = attributes.list[attributes.numbers['color']] [str] + local ta = attributes.list[attributes.numbers['transparency']][str] + if ca then + ctx.aux.definecolor(name, ca, global) end + if ta then + ctx.aux.definetransparent(name, ta, global) + end + else + ctx.aux.inheritcolor(name, str, global) + ctx.aux.inherittransparent(name, str, global) end +end - function ctx.definespotcolor(name,parent,str,global) - if parent == "" or parent:find("=") then - ctx.registerspotcolor(name, parent) - elseif name ~= parent then - local cp = attributes.list[attributes.numbers['color']][parent] - if cp then - local t = str:split_settings() - if t then - t.p = tonumber(t.p) or 1 - registerspotcolor(parent, name, cp, t.e, 1, "", t.p) -- p not really needed, only diagnostics - if name and name ~= "" then - ctx.aux.definecolor(name, colors.register('color',name,'spot', parent, 1, "", t.p), true) - if t.a and t.t then - ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) - elseif ctx.couplecolors then - --~ ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up - ctx.aux.definetransparent(name, 0, global) -- can be sped up - end +function ctx.definespotcolor(name,parent,str,global) + if parent == "" or parent:find("=") then + ctx.registerspotcolor(name, parent) + elseif name ~= parent then + local cp = attributes.list[attributes.numbers['color']][parent] + if cp then + local t = str:split_settings() + if t then + t.p = tonumber(t.p) or 1 + registerspotcolor(parent, name, cp, t.e, 1, "", t.p) -- p not really needed, only diagnostics + if name and name ~= "" then + ctx.aux.definecolor(name, colors.register('color',name,'spot', parent, 1, "", t.p), true) + if t.a and t.t then + ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) + elseif ctx.couplecolors then + --~ ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up + ctx.aux.definetransparent(name, 0, global) -- can be sped up end end end end end +end + +function ctx.registerspotcolor(parent, str) + local cp = attributes.list[attributes.numbers['color']][parent] + if cp then + local e = "" + if str then + local t = str:split_settings() + e = (t and t.e) or "" + end + registerspotcolor(parent, "dummy", cp, e, 1, "", 1) -- p not really needed, only diagnostics + end +end - function ctx.registerspotcolor(parent, str) +function ctx.definemultitonecolor(name,multispec,colorspec,selfspec) + local dd, pp, nn = { }, { }, { } + for k,v in multispec:gmatch("(%a+)=([^%,]*)") do + dd[#dd+1] = k + pp[#pp+1] = v + nn[#nn+1] = k + nn[#nn+1] = format("%1.3g",tonumber(v)) + end +--~ v = tonumber(v) * p + local nof = #dd + if nof > 0 then + dd, pp, nn = concat(dd,','), concat(pp,','), concat(nn,'_') + local parent = (nn:lower()):gsub("[^%d%a%.]+","_") + ctx.defineprocesscolor(parent,colorspec..","..selfspec,true,true) local cp = attributes.list[attributes.numbers['color']][parent] if cp then - local e = "" - if str then - local t = str:split_settings() - e = (t and t.e) or "" + registerspotcolor (parent, name, cp, "", nof, dd, pp) + registermultitonecolor(parent, name, cp, "", nof, dd, pp) + ctx.aux.definecolor(name, colors.register('color', name, 'spot', parent, nof, dd, pp), true) + local t = selfspec:split_settings() + if t and t.a and t.t then + ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) + elseif ctx.couplecolors then + -- ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up + ctx.aux.definetransparent(name, 0, global) -- can be sped up end - registerspotcolor(parent, "dummy", cp, e, 1, "", 1) -- p not really needed, only diagnostics end end +end - function ctx.definemultitonecolor(name,multispec,colorspec,selfspec) - local dd, pp, nn = { }, { }, { } - for k,v in multispec:gmatch("(%a+)=([^%,]*)") do - dd[#dd+1] = k - pp[#pp+1] = v - nn[#nn+1] = k - nn[#nn+1] = format("%1.3g",tonumber(v)) - end - --~ v = tonumber(v) * p - local nof = #dd - if nof > 0 then - dd, pp, nn = table.concat(dd,','), table.concat(pp,','), table.concat(nn,'_') - local parent = (nn:lower()):gsub("[^%d%a%.]+","_") - ctx.defineprocesscolor(parent,colorspec..","..selfspec,true,true) - local cp = attributes.list[attributes.numbers['color']][parent] - if cp then - registerspotcolor (parent, name, cp, "", nof, dd, pp) - registermultitonecolor(parent, name, cp, "", nof, dd, pp) - ctx.aux.definecolor(name, colors.register('color', name, 'spot', parent, nof, dd, pp), true) - local t = selfspec:split_settings() - if t and t.a and t.t then - ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) - elseif ctx.couplecolors then - -- ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up - ctx.aux.definetransparent(name, 0, global) -- can be sped up - end - end +function ctx.mpcolor(model,ca,ta,default) + local cv = colors.value(ca) -- faster when direct colors.values[ca] + if cv then + local tv = transparencies.value(ta) + if model == 1 then + model = cv[1] end - end - - function ctx.mpcolor(model,ca,ta,default) - local cv = colors.value(ca) -- faster when direct colors.values[ca] - if cv then - local tv = transparencies.value(ta) - if model == 1 then - model = cv[1] - end - if tv then - if model == 2 then - return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5]) - elseif model == 3 then - return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5]) - elseif model == 4 then - return format("transparent(%s,%s,cmyk(%s,%s,%s,%s))",tv[1],tv[2],cv[6],cv[7],cv[8],cv[9]) - else - return format("transparent(%s,%s,multitonecolor(\"%s\",%s,\"%s\",\"%s\"))",tv[1],tv[2],cv[10],cv[11],cv[12],cv[13]) - end + if tv then + if model == 2 then + return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5]) + elseif model == 3 then + return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5]) + elseif model == 4 then + return format("transparent(%s,%s,cmyk(%s,%s,%s,%s))",tv[1],tv[2],cv[6],cv[7],cv[8],cv[9]) else - if model == 2 then - return format("(%s,%s,%s)",cv[3],cv[4],cv[5]) - elseif model == 3 then - return format("(%s,%s,%s)",cv[3],cv[4],cv[5]) - elseif model == 4 then - return format("cmyk(%s,%s,%s,%s)",cv[6],cv[7],cv[8],cv[9]) - else - return format("multitonecolor(\"%s\",%s,\"%s\",\"%s\")",cv[10],cv[11],cv[12],cv[13]) - end + return format("transparent(%s,%s,multitonecolor(\"%s\",%s,\"%s\",\"%s\"))",tv[1],tv[2],cv[10],cv[11],cv[12],cv[13]) end else - default = default or 0 -- rgb ! - return format("(%s,%s,%s)",default,default,default) - end - end - - function ctx.formatcolor(ca,separator) - local cv = colors.value(ca) - if cv then - local model = cv[1] if model == 2 then - return tostring(cv[2]) + return format("(%s,%s,%s)",cv[3],cv[4],cv[5]) elseif model == 3 then - return table.concat(cv,separator,3,5) + return format("(%s,%s,%s)",cv[3],cv[4],cv[5]) elseif model == 4 then - return table.concat(cv,separator,6,9) + return format("cmyk(%s,%s,%s,%s)",cv[6],cv[7],cv[8],cv[9]) else - return tostring(cv[13]) + return format("multitonecolor(\"%s\",%s,\"%s\",\"%s\")",cv[10],cv[11],cv[12],cv[13]) end - else - return tostring(0) end + else + default = default or 0 -- rgb ! + return format("(%s,%s,%s)",default,default,default) end +end - function ctx.formatgray(ca,separator) - local cv = colors.value(ca) - if cv then +function ctx.formatcolor(ca,separator) + local cv = colors.value(ca) + if cv then + local model = cv[1] + if model == 2 then return tostring(cv[2]) + elseif model == 3 then + return concat(cv,separator,3,5) + elseif model == 4 then + return concat(cv,separator,6,9) else - return tostring(0) + return tostring(cv[13]) end + else + return tostring(0) end +end - function ctx.colorcomponents(ca) - local cv = colors.value(ca) - if cv then - local model = cv[1] - if model == 2 then - return format("s=%1.3f",cv[2]) - elseif model == 3 then - return format("r=%1.3f g=%1.3f b=%1.3f",cv[3],cv[4],cv[5]) - elseif model == 4 then - return format("c=%1.3f m=%1.3f y=%1.3f k=%1.3f",cv[6],cv[7],cv[8],cv[9]) - elseif type(cv[13]) == "string" then - return format("p=%s",cv[13]) - else - return format("p=%1.3f",cv[13]) - end - else - return "" - end +function ctx.formatgray(ca,separator) + local cv = colors.value(ca) + if cv then + return tostring(cv[2]) + else + return tostring(0) end +end - function ctx.transparencycomponents(ta) - local tv = transparencies.value(ta) - if tv then - return format("a=%1.3f t=%1.3f",tv[1],tv[2]) +function ctx.colorcomponents(ca) + local cv = colors.value(ca) + if cv then + local model = cv[1] + if model == 2 then + return format("s=%1.3f",cv[2]) + elseif model == 3 then + return format("r=%1.3f g=%1.3f b=%1.3f",cv[3],cv[4],cv[5]) + elseif model == 4 then + return format("c=%1.3f m=%1.3f y=%1.3f k=%1.3f",cv[6],cv[7],cv[8],cv[9]) + elseif type(cv[13]) == "string" then + return format("p=%s",cv[13]) else - return "" + return format("p=%1.3f",cv[13]) end + else + return "" end +end - function ctx.pdfcolor(model,ca,default) -- todo: use gray when no color - local cv = colors.value(ca) - if cv then - if model == 1 then - model = cv[1] - end - if model == 2 then - local s = cv[2] - return format("%s g %s G",s,s) - elseif model == 3 then - local r, g, b = cv[3], cv[4], cv[5] - return format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b) - elseif model == 4 then - local c, m, y, k = cv[6],cv[7],cv[8],cv[9] - return format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k) - else - local n,f,d,p = cv[10],cv[11],cv[12],cv[13] - if type(p) == "string" then - p = p:gsub(","," ") -- brr misuse of spot - end - return format("/%s cs /%s CS %s SCN %s scn",n,n,p,p) - end - else - return format("%s g %s G",default or 0) - end +function ctx.transparencycomponents(ta) + local tv = transparencies.value(ta) + if tv then + return format("a=%1.3f t=%1.3f",tv[1],tv[2]) + else + return "" end +end - function ctx.pdfcolorvalue(model,ca,default) - local cv = colors.value(ca) - if cv then - if model == 1 then - model = cv[1] - end - if model == 2 then - return format("%s",cv[2]) - elseif model == 3 then - return format("%s %s %s",cv[3],cv[4],cv[5]) - elseif model == 4 then - return format("%s %s %s %s",cv[6],cv[7],cv[8],cv[9]) - else - return format("%s",cv[13]) - end +function ctx.pdfcolor(model,ca,default) -- todo: use gray when no color + local cv = colors.value(ca) + if cv then + if model == 1 then + model = cv[1] + end + if model == 2 then + local s = cv[2] + return format("%s g %s G",s,s) + elseif model == 3 then + local r, g, b = cv[3], cv[4], cv[5] + return format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b) + elseif model == 4 then + local c, m, y, k = cv[6],cv[7],cv[8],cv[9] + return format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k) else - return format("%s",default or 0) + local n,f,d,p = cv[10],cv[11],cv[12],cv[13] + if type(p) == "string" then + p = p:gsub(","," ") -- brr misuse of spot + end + return format("/%s cs /%s CS %s SCN %s scn",n,n,p,p) end + else + return format("%s g %s G",default or 0,default or 0) end +end - function ctx.fdfcolor(model,ca,default) - local cv = colors.value(ca) - if cv then - if model == 1 then - model = cv[1] - end - if model == 2 then - return format("[%s]",cv[2]) - elseif model == 3 then - return format("[%s %s %s]",cv[3],cv[4],cv[5]) - elseif model == 4 then - return format("[%s %s %s %s]",cv[6],cv[7],cv[8],cv[9]) - elseif model == 4 then - return format("[%s]",cv[13]) - end +function ctx.pdfcolorvalue(model,ca,default) + local cv = colors.value(ca) + if cv then + if model == 1 then + model = cv[1] + end + if model == 2 then + return format("%s",cv[2]) + elseif model == 3 then + return format("%s %s %s",cv[3],cv[4],cv[5]) + elseif model == 4 then + return format("%s %s %s %s",cv[6],cv[7],cv[8],cv[9]) else - return format("[%s]",default or 0) + return format("%s",cv[13]) end + else + return format("%s",default or 0) end +end - function ctx.pdfcolorspace(model,ca) - local cv = colors.value(ca) - if cv then - if model == 1 then - model = cv[1] - end - if model == 2 then - return "DeviceGray" - elseif model == 3 then - return "DeviceRGB" - elseif model == 4 then - return "DeviceCMYK" - end +function ctx.fdfcolor(model,ca,default) + local cv = colors.value(ca) + if cv then + if model == 1 then + model = cv[1] end - return "DeviceGRAY" - end - - function ctx.spotcolorname(ca,default) - local cv, v = colors.value(ca), "unknown" - if cv and cv[1] == 5 then - v = cv[10] + if model == 2 then + return format("[%s]",cv[2]) + elseif model == 3 then + return format("[%s %s %s]",cv[3],cv[4],cv[5]) + elseif model == 4 then + return format("[%s %s %s %s]",cv[6],cv[7],cv[8],cv[9]) + elseif model == 4 then + return format("[%s]",cv[13]) end - return tostring(v) + else + return format("[%s]",default or 0) end +end - function ctx.spotcolorvalue(ca,default) - local cv, v = colors.value(ca), 0 - if cv and cv[1] == 5 then - v = cv[13] +function ctx.pdfcolorspace(model,ca) + local cv = colors.value(ca) + if cv then + if model == 1 then + model = cv[1] + end + if model == 2 then + return "DeviceGray" + elseif model == 3 then + return "DeviceRGB" + elseif model == 4 then + return "DeviceCMYK" end - return tostring(v) end + return "DeviceGRAY" +end - -- unfortunately we have \cs's here but this will go anyway once we have mplib and such - - function ctx.resolvempgraycolor(csa,csb,model,s) - local ca = colors.register('color',nil,'gray',s) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) - end - function ctx.resolvemprgbcolor(csa,csb,model,r,g,b) - local ca = colors.register('color',nil,'rgb',r,g,b) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) - end - function ctx.resolvempcmykcolor(csa,csb,model,c,m,y,k) - local ca = colors.register('color',nil,'cmyk',c,m,y,k) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) +function ctx.spotcolorname(ca,default) + local cv, v = colors.value(ca), "unknown" + if cv and cv[1] == 5 then + v = cv[10] end - function ctx.resolvempspotcolor(csa,csb,model,n,f,d,p) - local ca = colors.register('color',nil,'spot',n,f,d,p) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) + return tostring(v) +end + +function ctx.spotcolorvalue(ca,default) + local cv, v = colors.value(ca), 0 + if cv and cv[1] == 5 then + v = cv[13] end + return tostring(v) +end + +-- unfortunately we have \cs's here but this will go anyway once we have mplib and such +function ctx.resolvempgraycolor(csa,csb,model,s) + local ca = colors.register('color',nil,'gray',s) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) +end +function ctx.resolvemprgbcolor(csa,csb,model,r,g,b) + local ca = colors.register('color',nil,'rgb',r,g,b) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) +end +function ctx.resolvempcmykcolor(csa,csb,model,c,m,y,k) + local ca = colors.register('color',nil,'cmyk',c,m,y,k) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) +end +function ctx.resolvempspotcolor(csa,csb,model,n,f,d,p) + local ca = colors.register('color',nil,'spot',n,f,d,p) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) end -- literals needed to inject code in the mp stream, we cannot use attributes there -- since literals may have qQ's, much may go away once we have mplib code in place -do +local intransparency = false - local format, sprint = string.format, tex.sprint - - local intransparency = false - - function ctx.pdfrgbliteral(model,r,g,b) - sprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'rgb',r,g,b)))) - end - function ctx.pdfcmykliteral(model,c,m,y,k) - sprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'cmyk',c,m,y,k)))) - end - function ctx.pdfgrayliteral(model,s) - sprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'gray',s)))) - end - function ctx.pdfspotliteral(model,n,f,d,p) - sprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'spot',n,f,d,p)))) -- incorrect - end - function ctx.pdftransparencyliteral(a,t) - intransparency = true - sprint(tex.ctxcatcodes,format("\\pdfliteral{/Tr%s gs}",transparencies.register(nil,a,t))) - end - function ctx.pdffinishtransparency() - if intransparency then - intransparency = false - sprint(tex.ctxcatcodes,"\\pdfliteral{/Tr0 gs}") -- we happen to know this -) - end +function ctx.pdfrgbliteral(model,r,g,b) + texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'rgb',r,g,b)))) +end +function ctx.pdfcmykliteral(model,c,m,y,k) + texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'cmyk',c,m,y,k)))) +end +function ctx.pdfgrayliteral(model,s) + texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'gray',s)))) +end +function ctx.pdfspotliteral(model,n,f,d,p) + texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'spot',n,f,d,p)))) -- incorrect +end +function ctx.pdftransparencyliteral(a,t) + intransparency = true + texsprint(tex.ctxcatcodes,format("\\pdfliteral{/Tr%s gs}",transparencies.register(nil,a,t))) +end +function ctx.pdffinishtransparency() + if intransparency then + intransparency = false + texsprint(tex.ctxcatcodes,"\\pdfliteral{/Tr0 gs}") -- we happen to know this -) end - end diff --git a/tex/context/base/colo-new.tex b/tex/context/base/colo-new.tex index 417c54a52..04b2ef716 100644 --- a/tex/context/base/colo-new.tex +++ b/tex/context/base/colo-new.tex @@ -191,6 +191,10 @@ \newif\ifconverttoGRAY \newif\ifweightGRAY \weightGRAYtrue +\newif\ifconvertMPcolors +\newif\ifreduceMPcolors +\newif\ifforcegrayMPcolors + %D The last boolean controls reduction of \cap{CMYK} to %D \cap{CMY} colors. When set to true, the black component %D is added to the other ones. diff --git a/tex/context/base/cont-new.tex b/tex/context/base/cont-new.tex index be7393632..070b6e214 100644 --- a/tex/context/base/cont-new.tex +++ b/tex/context/base/cont-new.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2008.03.11 23:55} +\newcontextversion{2008.04.11 00:07} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/context.tex b/tex/context/base/context.tex index 6b126af9c..324c874e2 100644 --- a/tex/context/base/context.tex +++ b/tex/context/base/context.tex @@ -42,7 +42,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2008.03.11 23:55} +\edef\contextversion{2008.04.11 00:07} %D For those who want to use this: @@ -194,7 +194,7 @@ \loadcorefile{mult-com.tex} \loadmkivfile{luat-ini.tex} -% \loadmkivfile{luat-lib.tex} +%loadmkivfile{luat-lib.tex} \loadmkivfile{luat-lmx.tex} \loadmkivfile{luat-uni.tex} @@ -224,11 +224,12 @@ \loadcorefile{supp-mrk.tex} \loadcorefile{supp-vis.tex} \loadcorefile{supp-fun.tex} -\loadcorefile{supp-eps.tex} +%loadcorefile{supp-eps.tex} \loadcorefile{supp-spe.tex} \loadcorefile{supp-ran.tex} -\loadcorefile{supp-mps.tex} -\loadcorefile{supp-tpi.tex} +%loadcorefile{supp-mps.tex} +\loadmkiifile{supp-mps.tex} +\loadmkiifile{supp-tpi.tex} \loadcorefile{supp-mat.tex} \loadcorefile{supp-ali.tex} \loadcorefile{supp-num.tex} @@ -435,10 +436,15 @@ %D Like languages, fonts, encodings and symbols, \METAPOST\ %D support is also organized in its own class of modules. -\loadcorefile{meta-ini.tex} +\loadmkivfile{mlib-ctx.tex} +\loadmkivfile{mlib-pdf.tex} +\loadmkivfile{mlib-pps.tex} + +\loadmarkfile{meta-ini} +\loadmarkfile{meta-tex} + \loadcorefile{meta-pdf.tex} \loadcorefile{meta-pag.tex} -\loadcorefile{meta-tex.tex} %D Special page handling (maybe even later) @@ -475,7 +481,7 @@ \loadcorefile{core-box.tex} \loadcorefile{page-app.tex} -\loadcorefile{meta-fig.tex} +\loadmarkfile{meta-fig} %D Language specific spacing. diff --git a/tex/context/base/core-buf.lua b/tex/context/base/core-buf.lua index 384a5608e..33ca8ebbf 100644 --- a/tex/context/base/core-buf.lua +++ b/tex/context/base/core-buf.lua @@ -7,6 +7,8 @@ -- ctx lua reference model / hooks and such -- to be optimized +-- redefine buffers.get + if not versions then versions = { } end versions['core-buf'] = 1.001 if unicode and not utf then utf = unicode.utf8 end @@ -17,6 +19,10 @@ buffers.hooks = { } buffers.flags = { } buffers.commands = { } +-- if needed we can make 'm local + +local concat, texsprint, texprint = table.concat, tex.sprint, tex.print + function buffers.erase(name) buffers.data[name] = nil end @@ -52,7 +58,8 @@ function buffers.grab(name,begintag,endtag,data) buffers.data[name] = buffers.data[name]:gsub("[\010\013]$","") if buffers.flags.store_as_table then -- todo: specific splitter, do we really want to erase the spaces? - buffers.data[name] = string.split(buffers.data[name]," *[\010\013]") + --~ buffers.data[name] = string.split(buffers.data[name]," *[\010\013]") + buffers.data[name] = buffers.data[name]:splitlines() end end cs.testcase(more) @@ -78,39 +85,36 @@ buffers.commands.empty_line_command = "\\doverbatimemptyline" function buffers.verbatimbreak(n,m) if buffers.flags.optimize_verbatim then if (n==2) or (n==m) then - tex.sprint(buffers.commands.no_break) + texsprint(buffers.commands.no_break) else - tex.sprint(buffers.commands.do_break) + texsprint(buffers.commands.do_break) end end end function buffers.type(name) - if buffers.data[name] then - if type(buffers.data[name]) == "string" then - -- todo: use linestepper (no need for a table) - local lines = string.split(buffers.data[name]," *[\010\013]") - local line, n, m = 0, 0, #lines - for _,str in ipairs(lines) do - n, line = buffers.typeline(str, n, m, line) - end - else - local line, n, m = 0, 0, #buffers.data[name] - for _,str in ipairs(buffers.data[name]) do - n, line = buffers.typeline(str, n, m, line) - end + local lines = buffers.data[name] + local action = buffers.typeline + if lines then + if type(lines) == "string" then + lines = lines:splitlines() -- lines:split(" *[\010\013]") + end + local line, n, m = 0, 0, #lines + for i=1,m do + n, line = action(lines[i], n, m, line) end end end function buffers.typefile(name) local t = input.openfile(name) + local action = buffers.typeline if t then local line, n, m = 0, 0, t.noflines while true do str = t.reader(t) if str then - n, line = buffers.typeline(str, n, m, line) + n, line = action(str, n, m, line) else break end @@ -148,31 +152,57 @@ end -- todo, use more locals function buffers.get(name) - if buffers.data[name] then - if type(buffers.data[name]) == "table" then - for _,v in ipairs(buffers.data[name]) do - tex.print(v) + local b = buffers.data[name] + if b then + if type(b) == "table" then + for i=1,#b do + texprint(b[i]) end else - string.piecewise(buffers.data[name], " *[\010\013]", tex.print) + string.piecewise(b, " *[\010\013]", texprint) -- hm, can be faster + end + end +end + +function buffers.content(name) -- no print + local b = buffers.data[name] + if b then + if type(b) == "table" then + return concat(b," ") + else + return b + end + else + return "" + end +end + +function buffers.collect(names) -- no print + local t = { } + for i=1,#names do + local c = buffers.content(names[i]) + if c ~= "" then + t[#t+1] = c end end + return concat(t," ") end function buffers.inspect(name) - if buffers.data[name] then - if type(buffers.data[name]) == "table" then - for _,v in ipairs(buffers.data[name]) do + local b = buffers.data[name] + if b then + if type(b) == "table" then + for _,v in ipairs(b) do if v == "" then - tex.sprint(tex.ctxcatcodes,"[crlf]\\par ") + texsprint(tex.ctxcatcodes,"[crlf]\\par ") else - tex.sprint(tex.ctxcatcodes,(buffers.data[name]:gsub("(.)",function(c) + texsprint(tex.ctxcatcodes,(b:gsub("(.)",function(c) return " [" .. string.byte(c) .. "] " end)) .. "\\par") end end else - tex.sprint(tex.ctxcatcodes,(buffers.data[name]:gsub("(.)",function(c) + texsprint(tex.ctxcatcodes,(b:gsub("(.)",function(c) return " [" .. string.byte(c) .. "] " end))) end @@ -258,19 +288,19 @@ end -- defaults function buffers.visualizers.default.flush_line(str) - tex.sprint(tex.ctxcatcodes,buffers.escaped(str)) + texsprint(tex.ctxcatcodes,buffers.escaped(str)) end function buffers.visualizers.default.begin_of_line(n) - tex.sprint(tex.ctxcatcodes, buffers.commands.begin_of_line_command .. "{" .. n .. "}") + texsprint(tex.ctxcatcodes, buffers.commands.begin_of_line_command .. "{" .. n .. "}") end function buffers.visualizers.default.end_of_line() - tex.sprint(tex.ctxcatcodes,buffers.commands.end_of_line_command) + texsprint(tex.ctxcatcodes,buffers.commands.end_of_line_command) end function buffers.visualizers.default.empty_line() - tex.sprint(tex.ctxcatcodes,buffers.commands.empty_line_command) + texsprint(tex.ctxcatcodes,buffers.commands.empty_line_command) end function buffers.visualizers.default.line(str) @@ -315,7 +345,7 @@ function buffers.visualizers.flush_nested(str, enable) -- no utf, kind of obsole end end result = result .. "\\char" .. sb(ss(str,i,i)) .. " " .. string.rep("}",nested) - tex.sprint(tex.ctxcatcodes,result) + texsprint(tex.ctxcatcodes,result) end -- handy helpers @@ -363,9 +393,9 @@ end function buffers.flush_result(result,nested) if nested then - tex.sprint(tex.ctxcatcodes,buffers.replace_nested(table.concat(result,""))) + texsprint(tex.ctxcatcodes,buffers.replace_nested(concat(result,""))) else - tex.sprint(tex.ctxcatcodes,table.concat(result,"")) + texsprint(tex.ctxcatcodes,concat(result,"")) end end diff --git a/tex/context/base/core-con.lua b/tex/context/base/core-con.lua index b6af6be9a..be00accde 100644 --- a/tex/context/base/core-con.lua +++ b/tex/context/base/core-con.lua @@ -121,7 +121,7 @@ function converters.Character(n) converters.chr (n,64) end function converters.characters(n) converters.chrs(n,96) end function converters.Characters(n) converters.chrs(n,64) end -function converters.weekday(year,month,day) +function converters.weekday(day,month,year) tex.sprint(os.date("%w",os.time{year=year,month=month,day=day})+1) end diff --git a/tex/context/base/core-fig.tex b/tex/context/base/core-fig.tex index f701c0c9f..5eed8fbf1 100644 --- a/tex/context/base/core-fig.tex +++ b/tex/context/base/core-fig.tex @@ -485,10 +485,6 @@ \externalfigure[\typesetfilename.pdf][#2,#4]} \appendtoks \setupexternalfigures[\c!option=\v!empty] \to \everyfastmode -\appendtoks \runMPgraphicsfalse \to \everyfastmode -\appendtoks \insertMPgraphicsfalse \to \everyfastmode - -\appendtoks \flushMPgraphics \to \everygoodbye % \everylastshipout \setupexternalfigures [\c!option=, diff --git a/tex/context/base/core-fnt.tex b/tex/context/base/core-fnt.tex index dea5d3571..920f1288b 100644 --- a/tex/context/base/core-fnt.tex +++ b/tex/context/base/core-fnt.tex @@ -436,9 +436,9 @@ %D \showsetup{setupunderbar} %D %D The alternatives show up as -%D {\setupunderbar [alternativevariant=a]\underbar{alternative a}}, -%D {\setupunderbar [alternativevariant=b]\underbar{alternative b}}, -%D {\setupunderbar [alternativevariant=c]\underbar{alternative c}} +%D {\setupunderbar [alternative=a]\underbar{alternative a}}, +%D {\setupunderbar [alternative=b]\underbar{alternative b}}, +%D {\setupunderbar [alternative=c]\underbar{alternative c}} %D and %D {\setupunderbar [rulethickness=1pt]\underbar{1pt width}}, %D {\setupunderbar [rulethickness=2pt]\underbar{2pt width}}, diff --git a/tex/context/base/core-grd.tex b/tex/context/base/core-grd.tex index 864da8021..575ec4c2d 100644 --- a/tex/context/base/core-grd.tex +++ b/tex/context/base/core-grd.tex @@ -100,6 +100,7 @@ \wd0\hsize \dp0\strutdp \nointerlineskip + \forgetall \ruledvbox{\box0}% \egroup \prevdepth\strutdp}% @@ -704,6 +705,7 @@ \botbaselinecorrection \vss}% \setbox\nextbox\hbox{\lower\strutdp\flushnextbox}% + \forgeteverypar % new per 3/4/2008, prevents duplicate pos nodes resulting in extra whitespace \noindent\snaptogrid\vbox{\flushnextbox}% \egroup} \vbox % was \hbox diff --git a/tex/context/base/core-inc.mkiv b/tex/context/base/core-inc.mkiv index 2d6075247..24a78e657 100644 --- a/tex/context/base/core-inc.mkiv +++ b/tex/context/base/core-inc.mkiv @@ -180,7 +180,7 @@ \doifseparatingcolorselse {\let\@@efforegroundcolor\empty \doifelsenothing\@@efsplit - {\charde\splitexternalfigure\zerocount} + {\chardef\splitexternalfigure\zerocount} {\doifcolorchannelelse\@@efsplit {\let\@@efobject\v!no % why? \chardef\splitexternalfigure\plusone} diff --git a/tex/context/base/core-job.lua b/tex/context/base/core-job.lua index 2493719e8..b963227a5 100644 --- a/tex/context/base/core-job.lua +++ b/tex/context/base/core-job.lua @@ -21,10 +21,14 @@ function commands.doifnot(b) end cs.testcase = commands.doifelse -function commands. def(cs,value) tex.sprint(tex.ctxcatcodes, "\\def\\"..cs.."{"..value.."}") end -function commands.edef(cs,value) tex.sprint(tex.ctxcatcodes,"\\edef\\"..cs.."{"..value.."}") end -function commands.gdef(cs,value) tex.sprint(tex.ctxcatcodes,"\\gdef\\"..cs.."{"..value.."}") end -function commands.xdef(cs,value) tex.sprint(tex.ctxcatcodes,"\\xdef\\"..cs.."{"..value.."}") end +local format = string.format + +function commands. def(cs,value) tex.sprint(tex.ctxcatcodes,format( "\\def\\%s{%s}",cs,value)) end +function commands.edef(cs,value) tex.sprint(tex.ctxcatcodes,format("\\edef\\%s{%s}",cs,value)) end +function commands.gdef(cs,value) tex.sprint(tex.ctxcatcodes,format("\\gdef\\%s{%s}",cs,value)) end +function commands.xdef(cs,value) tex.sprint(tex.ctxcatcodes,format("\\xdef\\%s{%s}",cs,value)) end + +function commands.cs(cs,args) tex.sprint(tex.ctxcatcodes,format("\\csname %s\\endcsname %s",cs,args or"")) end -- main code @@ -140,4 +144,3 @@ function commands.loadexamodes(filename) commands.writestatus("examodes","no mode file " .. filename) -- todo: message system end end - diff --git a/tex/context/base/core-mis.tex b/tex/context/base/core-mis.tex index 8459caab2..48bf85259 100644 --- a/tex/context/base/core-mis.tex +++ b/tex/context/base/core-mis.tex @@ -2801,12 +2801,33 @@ %D \commalistsentence[aap,noot,mies] %D \commalistsentence[aap,noot] %D \commalistsentence[aap] +%D \commalistsentence[a,b,c] +%D \commalistsentence[a,b,c][{ \& },{ and }] +%D \commalistsentence[a,b,c][+,-] %D \stoptyping \let\handlecommalistsentence\firstofoneargument -\def\commalistsentence[#1]% +\def\commalistsentenceone{and-1} +\def\commalistsentencetwo{and-2} + +\def\commalistsentence + {\dodoubleempty\docommalistsentence} + +\def\docommalistsentence[#1][#2]% {\bgroup + \getfromcommalist[#2][1]% + \ifx\commalistelement\empty + \def\@@commalistsentenceone{\labeltext\commalistsentenceone}% + \else + \let\@@commalistsentenceone\commalistelement + \fi + \getfromcommalist[#2][2]% + \ifx\commalistelement\empty + \def\@@commalistsentencetwo{\labeltext\commalistsentencetwo}% + \else + \let\@@commalistsentencetwo\commalistelement + \fi \getcommalistsize[#1]% \ifcase\commalistsize\relax \def\serializedcommalist{#1}% @@ -2819,9 +2840,9 @@ \scratchtoks{\handlecommalistsentence{##1}}% \else \ifnum\scratchcounter=\commalistsize - \appendtoks\labeltext{and-2}\handlecommalistsentence{##1}\to\scratchtoks + \appendtoks\@@commalistsentencetwo\handlecommalistsentence{##1}\to\scratchtoks \else - \appendtoks\labeltext{and-1}\handlecommalistsentence{##1}\to\scratchtoks + \appendtoks\@@commalistsentenceone\handlecommalistsentence{##1}\to\scratchtoks \fi \fi}% \processcommacommand[#1]\docommand @@ -2830,6 +2851,8 @@ \serializedcommalist \egroup} +\def\commacommandsentence[#1]{\@EA\commalistsentence\@EA[#1]} + \ifx\textcomma\undefined \def\textcomma{,} \fi \setuplabeltext [\s!nl] [and-1=\textcomma\ , and-2= en ] diff --git a/tex/context/base/core-pgr.tex b/tex/context/base/core-pgr.tex index 65f1da061..145599cc3 100644 --- a/tex/context/base/core-pgr.tex +++ b/tex/context/base/core-pgr.tex @@ -278,15 +278,17 @@ \prepareMPvariables{#2}% \prepareMPpositionvariables \enableincludeMPgraphics - \ifcollectMPpositiongraphics + \ifcollectMPpositiongraphics % no longer needed in mkiv \expanded{\startMPdrawing#3\noexpand\stopMPdrawing}% \global\MPdrawingdonetrue + \else\ifx\startMPgraphic\undefined + \startMPcode#3\stopMPcode \else \startMPgraphic#3\stopMPgraphic \loadMPgraphic{\MPgraphicfile.\the\currentMPgraphic}{}% \deallocateMPslot\currentMPgraphic \placeMPgraphic - \fi + \fi\fi \egroup} % Now we need a adapted action handler: @@ -582,6 +584,14 @@ \iftracepositions show_multi_pars \else draw_multi_pars \fi ; \stopuseMPgraphic +\startuseMPgraphic{mpos:par:sideline}{linecolor,lineoffset} + for i=1 upto nofmultipars : + fill leftboundary multipars[i] + shifted (-\MPvar{lineoffset},0) + rightenlarged 1mm withcolor \MPvar{linecolor} ; + endfor ; +\stopuseMPgraphic + \startMPpositionmethod{mpos:par:columnset} \edef\MPparcounter{\MPv\MPbself{1}{0}}% \startMPpositiongraphic{mpos:par}% @@ -741,6 +751,7 @@ dashtype=\textbackgroundparameter{dash}, % to be internationalized gridcolor=\textbackgroundparameter\c!framecolor, linecolor=\textbackgroundparameter\c!framecolor, + lineoffset=\textbackgroundparameter\c!frameoffset, fillcolor=\textbackgroundparameter\c!backgroundcolor, filloffset=\textbackgroundparameter\c!backgroundoffset, gridwidth=\textbackgroundparameter\c!rulethickness, @@ -949,7 +960,7 @@ [\c!state,\c!location,\c!alternative,\c!mp,\c!method, \c!background,\c!backgroundcolor,\c!corner,\c!level, \c!backgroundoffset,\c!before,\c!after,\c!align,dash, % dash not yet internationalized - \c!radius,\c!frame,\c!framecolor,\c!rulethickness,\c!voffset, + \c!radius,\c!frame,\c!framecolor,\c!rulethickness,\c!voffset,\c!frameoffset, \c!leftoffset,\c!rightoffset,\c!topoffset,\c!bottomoffset]% \getparameters[\??td#1][#2]% \doifvalue{\??td#1\c!state}\v!start\checktextbackgrounds @@ -1669,9 +1680,21 @@ [before={\starttextbackground[strikethrough]}, after=\stoptextbackground] +\definetextbackground + [sideline] + [mp=mpos:par:sideline, + location=paragraph, + framecolor=red, + frameoffset=5mm] + +\definestartstop [sideline] + [before={\starttextbackground[sideline]}, + after=\stoptextbackground] + \starttext \startunderline \input tufte \stopunderline \blank \startoverstrike \input tufte \stopoverstrike \blank \startexlines \input tufte \stopexlines \blank \startstrikethrough \input tufte \stopstrikethrough \blank + \startsideline \input tufte \stopsideline \blank \stoptext diff --git a/tex/context/base/core-spa.lua b/tex/context/base/core-spa.lua index 22b392d4d..501ea28f2 100644 --- a/tex/context/base/core-spa.lua +++ b/tex/context/base/core-spa.lua @@ -791,7 +791,7 @@ do if not prev or prev.id ~= glyph then return upper(start) else - return start + return start, false end end @@ -806,7 +806,7 @@ do if (not prev or prev.id ~= glyph) and next and next.id == glyph then return upper(start) else - return start + return start, false end end diff --git a/tex/context/base/core-tab.tex b/tex/context/base/core-tab.tex index 89e6a556d..2e843eae8 100644 --- a/tex/context/base/core-tab.tex +++ b/tex/context/base/core-tab.tex @@ -1640,7 +1640,7 @@ \bgroup \catcode`\|=\@@other \gdef\@@otherbar {|} - \catcode`\"=\@@other \gdef\@@otherquite {"} + \catcode`\"=\@@other \gdef\@@otherquote {"} \catcode`\|=\@@active \gdef\@@useotherbar {\let|\@@otherbar} \catcode`\"=\@@active \gdef\@@useotherquote{\let"\@@otherquote} \egroup diff --git a/tex/context/base/core-tbl.tex b/tex/context/base/core-tbl.tex index 8081ff62a..55f40ecc7 100644 --- a/tex/context/base/core-tbl.tex +++ b/tex/context/base/core-tbl.tex @@ -1000,7 +1000,7 @@ &##\tabskip\zeropoint}% \fi \tabulatewidth\zeropoint -% |#1X|\relax + % |#1X|\relax \nexttabulate #1X|\relax \scratchcounter\tabulatecolumns \multiply\scratchcounter3% @@ -1029,7 +1029,7 @@ \def\xeskip {\par\egroup\egroup \global\let\tabulatehook\dotabulatehook}% -% \let|\savedbar + % \let|\savedbar \global\let\tabulatehook\dotabulatehook \doifvalue{\??tt\currenttabulate\c!indenting}\v!no\forgetparindent \ifinsidefloat diff --git a/tex/context/base/core-var.tex b/tex/context/base/core-var.tex index 6b6583a8b..61a8c87e7 100644 --- a/tex/context/base/core-var.tex +++ b/tex/context/base/core-var.tex @@ -232,7 +232,7 @@ {\docheckforallmodes\donothing\dostopallmodes{#1}} \long\def\dostartnotallmodes[#1]% - {\docheckforallmodes\dostopallmodes\donothing{#1}} + {\docheckforallmodes\dostopnotallmodes\donothing{#1}} \let\stopallmodes \donothing \let\stopnotallmodes\donothing @@ -380,6 +380,8 @@ %D This boolean can be used to bypass certain %D initializations. +\ifx\protectionlevel\undefined \newcount\protectionlevel \fi + \newif\ifproductionrun \appendtoks \productionruntrue \to \everydump \appendtoks \ifcase\protectionlevel\else\reportunprotection\fi \to \everydump diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index f5e08ca6d..92f545e15 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -1030,7 +1030,7 @@ function fonts.otf.enhance.analyze(data,filename) end do - -- original string parsr: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap + -- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap -- -- 18964 18964 (leader) -- 0 /.notdef @@ -1048,21 +1048,21 @@ do local tonumber = tonumber - function do_one(a,b) + local function do_one(a,b) unicodes[tonumber(a)] = tonumber(b,16) end - function do_range(a,b,c) + local function do_range(a,b,c) c = tonumber(c,16) for i=tonumber(a),tonumber(b) do unicodes[i] = c c = c + 1 end end - function do_name(a,b) + local function do_name(a,b) names[tonumber(a)] = b end - grammar = lpeg.P { "start", + local grammar = lpeg.P { "start", start = number * spaces * number * lpeg.V("series"), series = (spaces * (lpeg.V("one") + lpeg.V("range") + lpeg.V("named")) )^1, one = (number * spaces * number) / do_one, diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua index 88a85652e..1b0ed65d5 100644 --- a/tex/context/base/font-tfm.lua +++ b/tex/context/base/font-tfm.lua @@ -431,7 +431,7 @@ do local characters = tfmdata.characters local unicodes = afmdata.luatex.unicodes local function remap(pattern,name) - local p = lpeg.match(pattern,name) + local p = pattern:match(name) if p then local oldchr, newchr = unicodes[p], unicodes[name] if oldchr and newchr then @@ -459,7 +459,7 @@ do local characters = tfmdata.characters local unicodes = afmdata.luatex.unicodes local function remap(pattern,name) - local p = lpeg.match(pattern,name) + local p = pattern:match(name) if p then local oldchr, newchr = unicodes[p], unicodes[name] if oldchr and newchr then diff --git a/tex/context/base/l-aux.lua b/tex/context/base/l-aux.lua index fdcc88e46..f81d807cd 100644 --- a/tex/context/base/l-aux.lua +++ b/tex/context/base/l-aux.lua @@ -10,7 +10,7 @@ do local hash = { } - local function set(key,value) + local function set(key,value) -- using Carg is slower here hash[key] = value end @@ -31,7 +31,7 @@ do function aux.settings_to_hash(str) if str and str ~= "" then hash = { } - lpeg.match(pattern,str) + pattern:match(str) return hash else return { } @@ -39,17 +39,24 @@ do end local seperator = comma * space^0 - local value = lbrace * lpeg.C(nobrace^0) * rbrace + lpeg.C((1-seperator)^0) + local value = lpeg.P(lbrace * lpeg.C((nobrace + nested)^0) * rbrace) + lpeg.C((nested + (1-comma))^0) local pattern = lpeg.Ct(value*(seperator*value)^0) -- "aap, {noot}, mies" : outer {} removes, leading spaces ignored function aux.settings_to_array(str) - return lpeg.match(pattern,str) + return pattern:match(str) + end + + local function set(t,v) + t[#t+1] = v end + local value = lpeg.P(lpeg.Carg(1)*value) / set + local pattern = value*(seperator*value)^0 * lpeg.Carg(1) + function aux.add_settings_to_array(t,str) - return table.merge(t, lpeg.match(pattern,str)) + return pattern:match(str, nil, t) end end diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua index 0cc913d13..0600d72fa 100644 --- a/tex/context/base/l-dir.lua +++ b/tex/context/base/l-dir.lua @@ -97,17 +97,53 @@ if lfs then do else path, rest = pattern:match("^([^/]*)/(.-)$") end - patt = rest:gsub("([%.%-%+])", "%%%1") + if rest then + patt = rest:gsub("([%.%-%+])", "%%%1") + end patt = patt:gsub("%*", "[^/]*") patt = patt:gsub("%?", "[^/]") patt = patt:gsub("%[%^/%]%*%[%^/%]%*", ".*") if path == "" then path = "." end - -- print(pattern, path, patt) - recurse = patt:find("%.%*/") + recurse = patt:find("%.%*/") ~= nil glob_pattern(path,patt,recurse,action) return t end + local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V + + local pattern = Ct { + [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), + [2] = C(((1-S("*?/"))^0 * P("/"))^0), + [3] = C(P(1)^0) + } + + local filter = Cs ( ( + P("**") / ".*" + + P("*") / "[^/]*" + + P("?") / "[^/]" + + P(".") / "%." + + P("+") / "%+" + + P("-") / "%-" + + P(1) + )^0 ) + + function glob(str) + local split = pattern:match(str) + if split then + local t = { } + local action = action or function(name) t[#t+1] = name end + local root, path, base = split[1], split[2], split[3] + local recurse = base:find("**") + local start = root .. path + local result = filter:match(start .. base) + -- print(str, start, result) + glob_pattern(start,result,recurse,action) + return t + else + return { } + end + end + dir.glob = glob --~ list = dir.glob("**/*.tif") diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua index 56893aaf2..1645912dc 100644 --- a/tex/context/base/l-lpeg.lua +++ b/tex/context/base/l-lpeg.lua @@ -24,6 +24,8 @@ if not versions then versions = { } end versions['l-lpeg'] = 1.001 --~ lpeg.whitespace = lpeg.S(' \r\n\f\t')^1 --~ lpeg.nonwhitespace = lpeg.P(1-lpeg.whitespace)^1 +local hash = { } + function lpeg.anywhere(pattern) --slightly adapted from website return lpeg.P { lpeg.P(pattern) + 1 * lpeg.V(1) } end @@ -38,3 +40,17 @@ function lpeg.splitter(pattern, action) return (((1-lpeg.P(pattern))^1)/action+1)^0 end + +local crlf = lpeg.P("\r\n") +local cr = lpeg.P("\r") +local lf = lpeg.P("\n") +local space = lpeg.S(" \t\f\v") +local newline = crlf + cr + lf +local spacing = space^0 * newline +local content = lpeg.Cs((1-spacing)^1) * spacing^-1 * (spacing * lpeg.Cc(""))^0 + +local capture = lpeg.Ct(content^0) + +function string:splitlines() + return capture:match(self) +end diff --git a/tex/context/base/l-number.lua b/tex/context/base/l-number.lua index 35b58bda3..180b4c544 100644 --- a/tex/context/base/l-number.lua +++ b/tex/context/base/l-number.lua @@ -40,7 +40,7 @@ do local one = lpeg.C(1-lpeg.S(''))^1 function number.toset(n) - return lpeg.match(one,tostring(n)) + return one:match(tostring(n)) end end diff --git a/tex/context/base/l-string.lua b/tex/context/base/l-string.lua index b8059c0c9..0b2fac3a0 100644 --- a/tex/context/base/l-string.lua +++ b/tex/context/base/l-string.lua @@ -132,7 +132,7 @@ end --~ split = lpeg.Ct(c*(p*c)^0) --~ splitters[separator] = split --~ end ---~ return lpeg.match(split,self) -- split:match(self) +--~ return split:match(self) --~ else --~ return { } --~ end diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua index 99c492f1f..ad2f11001 100644 --- a/tex/context/base/l-table.lua +++ b/tex/context/base/l-table.lua @@ -70,15 +70,6 @@ function table.prepend(t, list) end end ---~ function table.merge(t, ...) ---~ for _, list in ipairs({...}) do ---~ for k,v in pairs(list) do ---~ t[k] = v ---~ end ---~ end ---~ return t ---~ end - function table.merge(t, ...) -- first one is target t = t or {} local lst = {...} @@ -90,16 +81,6 @@ function table.merge(t, ...) -- first one is target return t end ---~ function table.merged(...) ---~ local tmp = { } ---~ for _, list in ipairs({...}) do ---~ for k,v in pairs(list) do ---~ tmp[k] = v ---~ end ---~ end ---~ return tmp ---~ end - function table.merged(...) local tmp, lst = { }, {...} for i=1,#lst do @@ -110,15 +91,6 @@ function table.merged(...) return tmp end ---~ function table.imerge(t, ...) ---~ for _, list in ipairs({...}) do ---~ for _, v in ipairs(list) do ---~ t[#t+1] = v ---~ end ---~ end ---~ return t ---~ end - function table.imerge(t, ...) local lst = {...} for i=1,#lst do @@ -130,16 +102,6 @@ function table.imerge(t, ...) return t end ---~ function table.imerged(...) ---~ local tmp = { } ---~ for _, list in ipairs({...}) do ---~ for _,v in pairs(list) do ---~ tmp[#tmp+1] = v ---~ end ---~ end ---~ return tmp ---~ end - function table.imerged(...) local tmp, lst = { }, {...} for i=1,#lst do @@ -287,7 +249,6 @@ do end if n == #t then local tt = { } - -- for _,v in ipairs(t) do for i=1,#t do local v = t[i] local tv = type(v) @@ -342,7 +303,7 @@ do local inline = compact and table.serialize_inline local first, last = nil, 0 -- #root cannot be trusted here if compact then - for k,v in ipairs(root) do -- NOT: for k=1,#root do + for k,v in ipairs(root) do -- NOT: for k=1,#root do (why) if not first then first = k end last = last + 1 end @@ -524,7 +485,8 @@ end do local function flatten(t,f,complete) - for _,v in ipairs(t) do + for i=1,#t do + local v = t[i] if type(v) == "table" then if complete or type(v[1]) == "table" then flatten(v,f,complete) diff --git a/tex/context/base/l-xml-edu.lua b/tex/context/base/l-xml-edu.lua index adfc55e68..5aba46043 100644 --- a/tex/context/base/l-xml-edu.lua +++ b/tex/context/base/l-xml-edu.lua @@ -54,7 +54,7 @@ if false then for _,v in ipairs(doctype_patterns) do a = a:gsub(v, function(d) text[#text+1] = d or "" - return string.format("<@dd@>%s</@dd@>",#text) + return string.format("<@dt@>%s</@dt@>",#text) end) end end @@ -179,7 +179,7 @@ if false then local t = { ns = "", tg = '@rt@', dt = stack[1].dt } setmetatable(t, mt) for k,v in ipairs(t.dt) do - if type(v) == "table" and v.tg ~= "@pi@" and v.tg ~= "@dd@" and v.tg ~= "@cm@" then + if type(v) == "table" and v.tg ~= "@pi@" and v.tg ~= "@dt@" and v.tg ~= "@cm@" then t.ri = k -- rootindex break end diff --git a/tex/context/base/l-xml.lua b/tex/context/base/l-xml.lua index 703a880ed..06f527cad 100644 --- a/tex/context/base/l-xml.lua +++ b/tex/context/base/l-xml.lua @@ -39,6 +39,10 @@ xml.trace_lpath = false xml.trace_print = false xml.trace_remap = false +local format, concat = string.format, table.concat + +--~ local pairs, next, type = pairs, next, type + -- todo: some things per xml file, liek namespace remapping --[[ldx-- @@ -49,7 +53,7 @@ find based solution where we loop over an array of patterns. Less code and much cleaner.</p> --ldx]]-- -xml.xmlns = { } +xml.xmlns = xml.xmlns or { } do @@ -142,9 +146,13 @@ local x = xml.convert(somestring) element.</p> --ldx]]-- +xml.strip_cm_and_dt = false -- an extra global flag, in case we have many includes + do - local remove, nsremap = table.remove, xml.xmlns + -- not just one big nested table capture (lpeg overflow) + + local remove, nsremap, resolvens = table.remove, xml.xmlns, xml.resolvens local stack, top, dt, at, xmlns, errorstr = {}, {}, {}, {}, {}, nil @@ -154,6 +162,7 @@ do return "" end + local strip = false local cleanup = false function xml.set_text_cleanup(fnc) @@ -162,7 +171,7 @@ do local function add_attribute(namespace,tag,value) if tag == "xmlns" then - xmlns[#xmlns+1] = xml.resolvens(value) + xmlns[#xmlns+1] = resolvens(value) at[tag] = value elseif namespace == "xmlns" then xml.checkns(tag,value) @@ -189,9 +198,9 @@ do local toclose = remove(stack) top = stack[#stack] if #stack < 1 then - errorstr = string.format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "") + errorstr = format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "") elseif toclose.tg ~= tag then -- no namespace check - errorstr = string.format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "") + errorstr = format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "") end dt = top.dt dt[#dt+1] = toclose @@ -220,93 +229,106 @@ do dt[#dt+1] = text end end + --~ local function add_special(what, spacing, text) + --~ if #spacing > 0 then + --~ dt[#dt+1] = spacing + --~ end + --~ top = stack[#stack] -- hm, left over 1 + --~ setmetatable(top, mt) -- hm, left over 2 + --~ dt[#dt+1] = { special=true, ns="", tg=what, dt={text} } + --~ end local function add_special(what, spacing, text) if #spacing > 0 then dt[#dt+1] = spacing end - top = stack[#stack] - setmetatable(top, mt) - dt[#dt+1] = { special=true, ns="", tg=what, dt={text} } + if strip and (what == "@cm@" or what == "@dt@") then + -- forget it + else + dt[#dt+1] = { special=true, ns="", tg=what, dt={text} } + end end local function set_message(txt) errorstr = "garbage at the end of the file: " .. txt:gsub("([ \n\r\t]*)","") end - local space = lpeg.S(' \r\n\t') - local open = lpeg.P('<') - local close = lpeg.P('>') - local squote = lpeg.S("'") - local dquote = lpeg.S('"') - local equal = lpeg.P('=') - local slash = lpeg.P('/') - local colon = lpeg.P(':') - local valid = lpeg.R('az', 'AZ', '09') + lpeg.S('_-.') - local name_yes = lpeg.C(valid^1) * colon * lpeg.C(valid^1) - local name_nop = lpeg.C(lpeg.P(true)) * lpeg.C(valid^1) + local P, S, R, C, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V + + local space = S(' \r\n\t') + local open = P('<') + local close = P('>') + local squote = S("'") + local dquote = S('"') + local equal = P('=') + local slash = P('/') + local colon = P(':') + local valid = R('az', 'AZ', '09') + S('_-.') + local name_yes = C(valid^1) * colon * C(valid^1) + local name_nop = C(P(true)) * C(valid^1) local name = name_yes + name_nop - local utfbom = lpeg.P('\000\000\254\255') + lpeg.P('\255\254\000\000') + - lpeg.P('\255\254') + lpeg.P('\254\255') + lpeg.P('\239\187\191') -- no capture + local utfbom = P('\000\000\254\255') + P('\255\254\000\000') + + P('\255\254') + P('\254\255') + P('\239\187\191') -- no capture - local spacing = lpeg.C(space^0) - local justtext = lpeg.C((1-open)^1) + local spacing = C(space^0) + local justtext = C((1-open)^1) local somespace = space^1 local optionalspace = space^0 - local value = (squote * lpeg.C((1 - squote)^0) * squote) + (dquote * lpeg.C((1 - dquote)^0) * dquote) + local value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) local attribute = (somespace * name * optionalspace * equal * optionalspace * value) / add_attribute local attributes = attribute^0 local text = justtext / add_text - local balanced = lpeg.P { "[" * ((1 - lpeg.S"[]") + lpeg.V(1))^0 * "]" } -- taken from lpeg manual, () example + local balanced = P { "[" * ((1 - S"[]") + V(1))^0 * "]" } -- taken from lpeg manual, () example local emptyelement = (spacing * open * name * attributes * optionalspace * slash * close) / add_empty local beginelement = (spacing * open * name * attributes * optionalspace * close) / add_begin local endelement = (spacing * open * slash * name * optionalspace * close) / add_end - local begincomment = open * lpeg.P("!--") - local endcomment = lpeg.P("--") * close - local begininstruction = open * lpeg.P("?") - local endinstruction = lpeg.P("?") * close - local begincdata = open * lpeg.P("![CDATA[") - local endcdata = lpeg.P("]]") * close + local begincomment = open * P("!--") + local endcomment = P("--") * close + local begininstruction = open * P("?") + local endinstruction = P("?") * close + local begincdata = open * P("![CDATA[") + local endcdata = P("]]") * close - local someinstruction = lpeg.C((1 - endinstruction)^0) - local somecomment = lpeg.C((1 - endcomment )^0) - local somecdata = lpeg.C((1 - endcdata )^0) + local someinstruction = C((1 - endinstruction)^0) + local somecomment = C((1 - endcomment )^0) + local somecdata = C((1 - endcdata )^0) - local begindoctype = open * lpeg.P("!DOCTYPE") + local begindoctype = open * P("!DOCTYPE") local enddoctype = close - local publicdoctype = lpeg.P("PUBLIC") * somespace * value * somespace * value * somespace * balanced^0 - local systemdoctype = lpeg.P("SYSTEM") * somespace * value * somespace * balanced^0 + local publicdoctype = P("PUBLIC") * somespace * value * somespace * value * somespace * balanced^0 + local systemdoctype = P("SYSTEM") * somespace * value * somespace * balanced^0 local simpledoctype = (1-close)^1 * balanced^0 - local somedoctype = lpeg.C((somespace * lpeg.P(publicdoctype + systemdoctype + simpledoctype) * optionalspace)^0) + local somedoctype = C((somespace * P(publicdoctype + systemdoctype + simpledoctype) * optionalspace)^0) local instruction = (spacing * begininstruction * someinstruction * endinstruction) / function(...) add_special("@pi@",...) end local comment = (spacing * begincomment * somecomment * endcomment ) / function(...) add_special("@cm@",...) end local cdata = (spacing * begincdata * somecdata * endcdata ) / function(...) add_special("@cd@",...) end - local doctype = (spacing * begindoctype * somedoctype * enddoctype ) / function(...) add_special("@dd@",...) end + local doctype = (spacing * begindoctype * somedoctype * enddoctype ) / function(...) add_special("@dt@",...) end -- nicer but slower: -- -- local instruction = (lpeg.Cc("@pi@") * spacing * begininstruction * someinstruction * endinstruction) / add_special -- local comment = (lpeg.Cc("@cm@") * spacing * begincomment * somecomment * endcomment ) / add_special -- local cdata = (lpeg.Cc("@cd@") * spacing * begincdata * somecdata * endcdata ) / add_special - -- local doctype = (lpeg.Cc("@dd@") * spacing * begindoctype * somedoctype * enddoctype ) / add_special + -- local doctype = (lpeg.Cc("@dt@") * spacing * begindoctype * somedoctype * enddoctype ) / add_special local trailer = space^0 * (justtext/set_message)^0 - -- comment + emptyelement + text + cdata + instruction + lpeg.V("parent"), -- 6.5 seconds on 40 MB database file - -- text + comment + emptyelement + cdata + instruction + lpeg.V("parent"), -- 5.8 - -- text + lpeg.V("parent") + emptyelement + comment + cdata + instruction, -- 5.5 + -- comment + emptyelement + text + cdata + instruction + V("parent"), -- 6.5 seconds on 40 MB database file + -- text + comment + emptyelement + cdata + instruction + V("parent"), -- 5.8 + -- text + V("parent") + emptyelement + comment + cdata + instruction, -- 5.5 - local grammar = lpeg.P { "preamble", - preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * lpeg.V("parent") * trailer, - parent = beginelement * lpeg.V("children")^0 * endelement, - children = text + lpeg.V("parent") + emptyelement + comment + cdata + instruction, + local grammar = P { "preamble", + preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer, + parent = beginelement * V("children")^0 * endelement, + children = text + V("parent") + emptyelement + comment + cdata + instruction, } - function xml.convert(data, no_root) + function xml.convert(data, no_root, strip_cm_and_dt) + strip = strip_cm_and_dt or xml.strip_cm_and_dt stack, top, at, xmlns, errorstr, result = {}, {}, {}, {}, nil, nil stack[#stack+1] = top top.dt = { } @@ -315,8 +337,10 @@ do errorstr = "empty xml file" elseif not grammar:match(data) then errorstr = "invalid xml file" + else + errorstr = "" end - if errorstr then + if errorstr and errorstr ~= "" then result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={}, er = true } }, error = true } setmetatable(stack, mt) if xml.error_handler then xml.error_handler("load",errorstr) end @@ -326,7 +350,9 @@ do if not no_root then result = { special = true, ns = "", tg = '@rt@', dt = result.dt, at={} } setmetatable(result, mt) - for k,v in ipairs(result.dt) do + local rdt = result.dt + for k=1,#rdt do + local v = rdt[k] if type(v) == "table" and not v.special then -- always table -) result.ri = k -- rootindex break @@ -403,24 +429,30 @@ generic table copier. Since we know what we're dealing with we can speed up things a bit. The second argument is not to be used!</p> --ldx]]-- -function xml.copy(old,tables) - if old then - tables = tables or { } - local new = { } - if not tables[old] then - tables[old] = new - end - for k,v in pairs(old) do - new[k] = (type(v) == "table" and (tables[v] or xml.copy(v, tables))) or v - end - local mt = getmetatable(old) - if mt then - setmetatable(new,mt) +do + + function copy(old,tables) + if old then + tables = tables or { } + local new = { } + if not tables[old] then + tables[old] = new + end + for k,v in pairs(old) do + new[k] = (type(v) == "table" and (tables[v] or copy(v, tables))) or v + end + local mt = getmetatable(old) + if mt then + setmetatable(new,mt) + end + return new + else + return { } end - return new - else - return { } end + + xml.copy = copy + end --[[ldx-- @@ -437,138 +469,141 @@ do local fallbackhandle = (tex and tex.sprint) or io.write - function xml.serialize(e, handle, textconverter, attributeconverter, specialconverter, nocommands) + local function serialize(e, handle, textconverter, attributeconverter, specialconverter, nocommands) if not e then - -- quit - elseif not nocommands and e.command and xml.command then - xml.command(e) - else - handle = handle or fallbackhandle - local etg = e.tg - if etg then - -- local format = string.format - if e.special then - local edt = e.dt - local spc = specialconverter and specialconverter[etg] - if spc then - local result = spc(edt[1]) - if result then - handle(result) - else - -- no need to handle any further + return + elseif not nocommands then + local ec = e.command + if ec ~= nil then -- we can have all kind of types + local xc = xml.command + if xc then + xc(e,ec) + return + end + end + end + handle = handle or fallbackhandle + local etg = e.tg + if etg then + if e.special then + local edt = e.dt + local spc = specialconverter and specialconverter[etg] + if spc then + local result = spc(edt[1]) + if result then + handle(result) + else + -- no need to handle any further + end + elseif etg == "@pi@" then + -- handle(format("<?%s?>",edt[1])) + handle("<?" .. edt[1] .. "?>") + elseif etg == "@cm@" then + -- handle(format("<!--%s-->",edt[1])) + handle("<!--" .. edt[1] .. "-->") + elseif etg == "@cd@" then + -- handle(format("<![CDATA[%s]]>",edt[1])) + handle("<![CDATA[" .. edt[1] .. "]]>") + elseif etg == "@dt@" then + -- handle(format("<!DOCTYPE %s>",edt[1])) + handle("<!DOCTYPE " .. edt[1] .. ">") + elseif etg == "@rt@" then + serialize(edt,handle,textconverter,attributeconverter,specialconverter,nocommands) + end + else + local ens, eat, edt, ern = e.ns, e.at, e.dt, e.rn + local ats = eat and next(eat) and { } -- type test maybe faster + if ats then + if attributeconverter then + for k,v in pairs(eat) do + ats[#ats+1] = format('%s=%q',k,attributeconverter(v)) + end + else + for k,v in pairs(eat) do + ats[#ats+1] = format('%s=%q',k,v) end - elseif etg == "@pi@" then - -- handle(format("<?%s?>",edt[1])) - handle("<?" .. edt[1] .. "?>") -- maybe table.join(edt) - elseif etg == "@cm@" then - -- handle(format("<!--%s-->",edt[1])) - handle("<!--" .. edt[1] .. "-->") - elseif etg == "@cd@" then - -- handle(format("<![CDATA[%s]]>",edt[1])) - handle("<![CDATA[" .. edt[1] .. "]]>") - elseif etg == "@dd@" then - -- handle(format("<!DOCTYPE %s>",edt[1])) - handle("<!DOCTYPE " .. edt[1] .. ">") - elseif etg == "@rt@" then - xml.serialize(edt,handle,textconverter,attributeconverter,specialconverter,nocommands) end - else - local ens, eat, edt, ern = e.ns, e.at, e.dt, e.rn - local ats = eat and next(eat) and { } + end + if ern and xml.trace_remap then if ats then - local format = string.format - if attributeconverter then - for k,v in pairs(eat) do - ats[#ats+1] = format('%s=%q',k,attributeconverter(v)) - end - else - for k,v in pairs(eat) do - ats[#ats+1] = format('%s=%q',k,v) - end - end + ats[#ats+1] = format("xmlns:remapped='%s'",ern) + else + ats = { format("xmlns:remapped='%s'",ern) } end - if ern and xml.trace_remap then + end + if ens ~= "" then + if edt and #edt > 0 then if ats then - ats[#ats+1] = string.format("xmlns:remapped='%s'",ern) + -- handle(format("<%s:%s %s>",ens,etg,concat(ats," "))) + handle("<" .. ens .. ":" .. etg .. " " .. concat(ats," ") .. ">") else - ats = { string.format("xmlns:remapped='%s'",ern) } + -- handle(format("<%s:%s>",ens,etg)) + handle("<" .. ens .. ":" .. etg .. ">") end - end - if ens ~= "" then - if edt and #edt > 0 then - if ats then - -- handle(format("<%s:%s %s>",ens,etg,table.concat(ats," "))) - handle("<" .. ens .. ":" .. etg .. " " .. table.concat(ats," ") .. ">") - else - -- handle(format("<%s:%s>",ens,etg)) - handle("<" .. ens .. ":" .. etg .. ">") - end - local serialize = xml.serialize - for i=1,#edt do - local e = edt[i] - if type(e) == "string" then - if textconverter then - handle(textconverter(e)) - else - handle(e) - end + for i=1,#edt do + local e = edt[i] + if type(e) == "string" then + if textconverter then + handle(textconverter(e)) else - serialize(e,handle,textconverter,attributeconverter,specialconverter,nocommands) + handle(e) end - end - -- handle(format("</%s:%s>",ens,etg)) - handle("</" .. ens .. ":" .. etg .. ">") - else - if ats then - -- handle(format("<%s:%s %s/>",ens,etg,table.concat(ats," "))) - handle("<" .. ens .. ":" .. etg .. " " .. table.concat(ats," ") .. "/>") else - -- handle(format("<%s:%s/>",ens,etg)) - handle("<" .. ens .. ":" .. "/>") + serialize(e,handle,textconverter,attributeconverter,specialconverter,nocommands) end end + -- handle(format("</%s:%s>",ens,etg)) + handle("</" .. ens .. ":" .. etg .. ">") else - if edt and #edt > 0 then - if ats then - -- handle(format("<%s %s>",etg,table.concat(ats," "))) - handle("<" .. etg .. " " .. table.concat(ats," ") .. ">") - else - -- handle(format("<%s>",etg)) - handle("<" .. etg .. ">") - end - local serialize = xml.serialize - for i=1,#edt do - serialize(edt[i],handle,textconverter,attributeconverter,specialconverter,nocommands) - end - -- handle(format("</%s>",etg)) - handle("</" .. etg .. ">") + if ats then + -- handle(format("<%s:%s %s/>",ens,etg,concat(ats," "))) + handle("<" .. ens .. ":" .. etg .. " " .. concat(ats," ") .. "/>") else - if ats then - -- handle(format("<%s %s/>",etg,table.concat(ats," "))) - handle("<" .. etg .. " " .. table.concat(ats," ") .. "/>") - else - -- handle(format("<%s/>",etg)) - handle("<" .. etg .. "/>") - end + -- handle(format("<%s:%s/>",ens,etg)) + handle("<" .. ens .. ":" .. "/>") end end - end - elseif type(e) == "string" then - if textconverter then - handle(textconverter(e)) else - handle(e) + if edt and #edt > 0 then + if ats then + -- handle(format("<%s %s>",etg,concat(ats," "))) + handle("<" .. etg .. " " .. concat(ats," ") .. ">") + else + -- handle(format("<%s>",etg)) + handle("<" .. etg .. ">") + end + for i=1,#edt do + serialize(edt[i],handle,textconverter,attributeconverter,specialconverter,nocommands) + end + -- handle(format("</%s>",etg)) + handle("</" .. etg .. ">") + else + if ats then + -- handle(format("<%s %s/>",etg,concat(ats," "))) + handle("<" .. etg .. " " .. concat(ats," ") .. "/>") + else + -- handle(format("<%s/>",etg)) + handle("<" .. etg .. "/>") + end + end end + end + elseif type(e) == "string" then + if textconverter then + handle(textconverter(e)) else - local serialize = xml.serialize - for i=1,#e do - serialize(e[i],handle,textconverter,attributeconverter,specialconverter,nocommands) - end + handle(e) + end + else + for i=1,#e do + serialize(e[i],handle,textconverter,attributeconverter,specialconverter,nocommands) end end end - function xml.checkbom(root) + xml.serialize = serialize + + function xml.checkbom(root) -- can be made faster if root.ri then local dt, found = root.dt, false for k,v in ipairs(dt) do @@ -584,24 +619,24 @@ do end end -end - ---[[ldx-- -<p>At the cost of some 25% runtime overhead you can first convert the tree to a string -and then handle the lot.</p> ---ldx]]-- + --[[ldx-- + <p>At the cost of some 25% runtime overhead you can first convert the tree to a string + and then handle the lot.</p> + --ldx]]-- -function xml.tostring(root) -- 25% overhead due to collecting - if root then - if type(root) == 'string' then - return root - elseif next(root) then - local result = { } - xml.serialize(root,function(s) result[#result+1] = s end) - return table.concat(result,"") + function xml.tostring(root) -- 25% overhead due to collecting + if root then + if type(root) == 'string' then + return root + elseif next(root) then -- next is faster than type (and >0 test) + local result = { } + serialize(root,function(s) result[#result+1] = s end) + return concat(result,"") + end end -end - return "" + return "" + end + end --[[ldx-- @@ -729,48 +764,50 @@ do [40] = "processing instruction", } - local function make_expression(str) + local function make_expression(str) --could also be an lpeg str = str:gsub("@([a-zA-Z%-_]+)", "(a['%1'] or '')") str = str:gsub("position%(%)", "i") str = str:gsub("text%(%)", "t") str = str:gsub("!=", "~=") str = str:gsub("([^=!~<>])=([^=!~<>])", "%1==%2") str = str:gsub("([a-zA-Z%-_]+)%(", "functions.%1(") - return str, loadstring(string.format("return function(functions,i,a,t) return %s end", str))() + return str, loadstring(format("return function(functions,i,a,t) return %s end", str))() end local map = { } - local space = lpeg.S(' \r\n\t') - local squote = lpeg.S("'") - local dquote = lpeg.S('"') - local lparent = lpeg.P('(') - local rparent = lpeg.P(')') - local atsign = lpeg.P('@') - local lbracket = lpeg.P('[') - local rbracket = lpeg.P(']') - local exclam = lpeg.P('!') - local period = lpeg.P('.') - local eq = lpeg.P('==') + lpeg.P('=') - local ne = lpeg.P('<>') + lpeg.P('!=') - local star = lpeg.P('*') - local slash = lpeg.P('/') - local colon = lpeg.P(':') - local bar = lpeg.P('|') - local hat = lpeg.P('^') - local valid = lpeg.R('az', 'AZ', '09') + lpeg.S('_-') - local name_yes = lpeg.C(valid^1) * colon * lpeg.C(valid^1 + star) -- permits ns:* - local name_nop = lpeg.C(lpeg.P(true)) * lpeg.C(valid^1) + local P, S, R, C, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V + + local space = S(' \r\n\t') + local squote = S("'") + local dquote = S('"') + local lparent = P('(') + local rparent = P(')') + local atsign = P('@') + local lbracket = P('[') + local rbracket = P(']') + local exclam = P('!') + local period = P('.') + local eq = P('==') + P('=') + local ne = P('<>') + P('!=') + local star = P('*') + local slash = P('/') + local colon = P(':') + local bar = P('|') + local hat = P('^') + local valid = R('az', 'AZ', '09') + S('_-') + local name_yes = C(valid^1) * colon * C(valid^1 + star) -- permits ns:* + local name_nop = C(P(true)) * C(valid^1) local name = name_yes + name_nop - local number = lpeg.C((lpeg.S('+-')^0 * lpeg.R('09')^1)) / tonumber + local number = C((S('+-')^0 * R('09')^1)) / tonumber local names = (bar^0 * name)^1 local morenames = name * (bar^0 * name)^1 - local instructiontag = lpeg.P('pi::') - local spacing = lpeg.C(space^0) + local instructiontag = P('pi::') + local spacing = C(space^0) local somespace = space^1 local optionalspace = space^0 - local text = lpeg.C(valid^0) - local value = (squote * lpeg.C((1 - squote)^0) * squote) + (dquote * lpeg.C((1 - dquote)^0) * dquote) + local text = C(valid^0) + local value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) local empty = 1-slash local is_eq = lbracket * atsign * name * eq * value * rbracket @@ -780,9 +817,9 @@ do local is_number = lbracket * number * rbracket local nobracket = 1-(lbracket+rbracket) -- must be improved - local is_expression = lbracket * lpeg.C(((lpeg.C(nobracket^1))/make_expression)) * rbracket + local is_expression = lbracket * C(((C(nobracket^1))/make_expression)) * rbracket - local is_expression = lbracket * (lpeg.C(nobracket^1))/make_expression * rbracket + local is_expression = lbracket * (C(nobracket^1))/make_expression * rbracket local is_one = name local is_none = exclam * name @@ -822,18 +859,21 @@ do local expression = (is_one * is_expression)/ function(...) map[#map+1] = { 31, true, ... } end local dont_expression = (is_none * is_expression)/ function(...) map[#map+1] = { 31, false, ... } end + local self_expression = ( is_expression)/ function(...) map[#map+1] = { 31, true, "", "*", ... } end + local dont_self_expression = (exclam * is_expression)/ function(...) map[#map+1] = { 31, true, "", "*", ... } end + local instruction = (instructiontag * text ) / function(...) map[#map+1] = { 40, ... } end local nothing = (empty ) / function( ) map[#map+1] = { 15 } end -- 15 ? local crap = (1-slash)^1 -- a few ugly goodies: - local docroottag = lpeg.P('^^') / function( ) map[#map+1] = { 12 } end - local subroottag = lpeg.P('^') / function( ) map[#map+1] = { 13 } end - local roottag = lpeg.P('root::') / function( ) map[#map+1] = { 12 } end - local parenttag = lpeg.P('parent::') / function( ) map[#map+1] = { 11 } end - local childtag = lpeg.P('child::') - local selftag = lpeg.P('self::') + local docroottag = P('^^') / function( ) map[#map+1] = { 12 } end + local subroottag = P('^') / function( ) map[#map+1] = { 13 } end + local roottag = P('root::') / function( ) map[#map+1] = { 12 } end + local parenttag = P('parent::') / function( ) map[#map+1] = { 11 } end + local childtag = P('child::') + local selftag = P('self::') -- there will be more and order will be optimized @@ -847,18 +887,19 @@ do dont_match_and_eq + dont_match_and_ne + match_and_eq + match_and_ne + dont_expression + expression + + dont_self_expression + self_expression + has_attribute + has_value + dont_match_one_of + match_one_of + dont_match + match + crap + empty ) - local grammar = lpeg.P { "startup", - startup = (initial + documentroot + subtreeroot + roottag + docroottag + subroottag)^0 * lpeg.V("followup"), + local grammar = P { "startup", + startup = (initial + documentroot + subtreeroot + roottag + docroottag + subroottag)^0 * V("followup"), followup = ((slash + parenttag + childtag + selftag)^0 * selector)^1, } - function compose(str) + local function compose(str) if not str or str == "" then -- wildcard return true @@ -880,7 +921,7 @@ do -- root return false end - elseif #map == 2 and m == 12 and map[2][1] == 20 then + elseif #map == 2 and m == 12 and map[2][1] == 20 then -- return { { 29, map[2][2], map[2][3], map[2][4], map[2][5] } } map[2][1] = 29 return { map[2] } @@ -888,6 +929,7 @@ do if m ~= 11 and m ~= 12 and m ~= 13 and m ~= 14 and m ~= 15 and m ~= 16 then table.insert(map, 1, { 16 }) end + -- print((table.serialize(map)):gsub("[ \n]+"," ")) return map end end @@ -922,7 +964,7 @@ do report(" -: wildcard\n") else if type(pattern) == "string" then - report(string.format("pattern: %s\n",pattern)) + report(format("pattern: %s\n",pattern)) end for k,v in ipairs(lp) do if #v > 1 then @@ -935,9 +977,9 @@ do t[#t+1] = (vv and "==") or "<>" end end - report(string.format("%2i: %s %s -> %s\n", k,v[1],actions[v[1]],table.join(t," "))) + report(format("%2i: %s %s -> %s\n", k,v[1],actions[v[1]],concat(t," "))) else - report(string.format("%2i: %s %s\n", k,v[1],actions[v[1]])) + report(format("%2i: %s %s\n", k,v[1],actions[v[1]])) end end end @@ -1058,7 +1100,7 @@ do start, stop, step = stop, start, -1 end local idx = 0 - for k=start,stop,step do + for k=start,stop,step do -- we used to have functions for all but a case is faster local e = rootdt[k] local ns, tg = e.rn or e.ns, e.tg if tg then @@ -1133,7 +1175,7 @@ do if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end if not action[2] then matched = not matched end if matched then - matched = action[6](functions,idx,e.at,edt[1]) + matched = action[6](functions,idx,e.at or { },edt[1]) end end if matched then -- combine tg test and at test @@ -1225,53 +1267,25 @@ do xml.filters = { } - --[[ldx-- - <p>For splitting the filter function from the path specification, we can - use string matching or lpeg matching. Here the difference in speed is - neglectable but the lpeg variant is more robust.</p> - --ldx]]-- - - -- function xml.filter(root,pattern) - -- local pat, fun, arg = pattern:match("^(.+)/(.-)%((.*)%)$") - -- if fun then - -- return (xml.filters[fun] or xml.filters.default)(root,pat,arg) - -- else - -- pat, arg = pattern:match("^(.+)/@(.-)$") - -- if arg then - -- return xml.filters.attributes(root,pat,arg) - -- else - -- return xml.filters.default(root,pattern) - -- end - -- end - -- end - - -- not faster but hipper ... although ... i can't get rid of the trailing / in the path - - local name = (lpeg.R("az","AZ")+lpeg.R("_-"))^1 - local path = lpeg.C(((1-lpeg.P('/'))^0 * lpeg.P('/'))^1) - local argument = lpeg.P { "(" * lpeg.C(((1 - lpeg.S("()")) + lpeg.V(1))^0) * ")" } - local action = lpeg.Cc(1) * path * lpeg.C(name) * argument - local attribute = lpeg.Cc(2) * path * lpeg.P('@') * lpeg.C(name) - - local parser = action + attribute - - function xml.filter(root,pattern) - local kind, a, b, c = parser:match(pattern) - if kind == 1 then - return (xml.filters[b] or xml.filters.default)(root,a,c) - elseif kind == 2 then - return xml.filters.attributes(root,a,b) - else - return xml.filters.default(root,pattern) - end - end - function xml.filters.default(root,pattern) local rt, dt, dk traverse(root, lpath(pattern), function(r,d,k) rt,dt,dk = r,d,k return true end) return dt and dt[dk], rt, dt, dk end - + function xml.filters.attributes(root,pattern,arguments) + local rt, dt, dk + traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end) + local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at) + if ekat then + if arguments then + return ekat[arguments] or "", rt, dt, dk + else + return ekat, rt, dt, dk + end + else + return { }, rt, dt, dk + end + end function xml.filters.reverse(root,pattern) local rt, dt, dk traverse(root, lpath(pattern), function(r,d,k) rt,dt,dk = r,d,k return true end, 'reverse') @@ -1329,27 +1343,13 @@ do end return nil, nil, nil, nil end - function xml.filters.attributes(root,pattern,arguments) - local rt, dt, dk - traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end) - local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at) - if ekat then - if arguments then - return ekat[arguments] or "", rt, dt, dk - else - return ekat, rt, dt, dk - end - else - return { }, rt, dt, dk - end - end function xml.filters.attribute(root,pattern,arguments) local rt, dt, dk traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end) local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at) return (ekat and (ekat[arguments] or ekat[arguments:gsub("^([\"\'])(.*)%1$","%2")])) or "" end - function xml.filters.text(root,pattern,arguments) -- ?? why index + function xml.filters.text(root,pattern,arguments) -- ?? why index, tostring slow local dtk, rt, dt, dk = xml.filters.index(root,pattern,arguments) if dtk then local dtkdt = dtk.dt @@ -1366,6 +1366,66 @@ do end --[[ldx-- + <p>For splitting the filter function from the path specification, we can + use string matching or lpeg matching. Here the difference in speed is + neglectable but the lpeg variant is more robust.</p> + --ldx]]-- + + -- not faster but hipper ... although ... i can't get rid of the trailing / in the path + + local P, S, R, C, V, Cc = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc + + local name = (R("az","AZ")+R("_-"))^1 + local path = C(((1-P('/'))^0 * P('/'))^1) + local argument = P { "(" * C(((1 - S("()")) + V(1))^0) * ")" } + local action = Cc(1) * path * C(name) * argument + local attribute = Cc(2) * path * P('@') * C(name) + + local parser = action + attribute + + local filters = xml.filters + local attribute_filter = xml.filters.attributes + local default_filter = xml.filters.default + + function xml.filter(root,pattern) + local kind, a, b, c = parser:match(pattern) + if kind == 1 then + return (filters[b] or default_filter)(root,a,c) + elseif kind == 2 then + return attribute_filter(root,a,b) + else + return default_filter(root,pattern) + end + end + + --~ slightly faster, but first we need a proper test file + --~ + --~ local hash = { } + --~ + --~ function xml.filter(root,pattern) + --~ local h = hash[pattern] + --~ if not h then + --~ local kind, a, b, c = parser:match(pattern) + --~ if kind == 1 then + --~ h = { kind, filters[b] or default_filter, a, b, c } + --~ elseif kind == 2 then + --~ h = { kind, attribute_filter, a, b, c } + --~ else + --~ h = { kind, default_filter, a, b, c } + --~ end + --~ hash[pattern] = h + --~ end + --~ local kind = h[1] + --~ if kind == 1 then + --~ return h[2](root,h[2],h[4]) + --~ elseif kind == 2 then + --~ return h[2](root,h[2],h[3]) + --~ else + --~ return h[2](root,pattern) + --~ end + --~ end + + --[[ldx-- <p>The following functions collect elements and texts.</p> --ldx]]-- @@ -1435,12 +1495,14 @@ do <p>We use the function variants in the filters.</p> --ldx]]-- + local wrap, yield = coroutine.wrap, coroutine.yield + function xml.elements(root,pattern,reverse) - return coroutine.wrap(function() traverse(root, lpath(pattern), coroutine.yield, reverse) end) + return wrap(function() traverse(root, lpath(pattern), yield, reverse) end) end function xml.elements_only(root,pattern,reverse) - return coroutine.wrap(function() traverse(root, lpath(pattern), function(r,d,k) coroutine.yield(d[k]) end, reverse) end) + return wrap(function() traverse(root, lpath(pattern), function(r,d,k) yield(d[k]) end, reverse) end) end function xml.each_element(root, pattern, handle, reverse) @@ -1466,7 +1528,7 @@ do local ek = d[k] local a = ek.at or { } handle(a) - if next(a) then + if next(a) then -- next is faster than type (and >0 test) ek.at = a else ek.at = nil @@ -1786,6 +1848,8 @@ end do + local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs + -- 100 * 2500 * "oeps< oeps> oeps&" : gsub:lpeg|lpeg|lpeg -- -- 1021:0335:0287:0247 @@ -1794,23 +1858,23 @@ do -- -- 1559:0257:0288:0190 (last one suggested by roberto) - -- escaped = lpeg.Cs((lpeg.S("<&>") / xml.escapes + 1)^0) - -- escaped = lpeg.Cs((lpeg.S("<")/"<" + lpeg.S(">")/">" + lpeg.S("&")/"&" + 1)^0) - local normal = (1 - lpeg.S("<&>"))^0 - local special = lpeg.P("<")/"<" + lpeg.P(">")/">" + lpeg.P("&")/"&" - local escaped = lpeg.Cs(normal * (special * normal)^0) + -- escaped = Cs((S("<&>") / xml.escapes + 1)^0) + -- escaped = Cs((S("<")/"<" + S(">")/">" + S("&")/"&" + 1)^0) + local normal = (1 - S("<&>"))^0 + local special = P("<")/"<" + P(">")/">" + P("&")/"&" + local escaped = Cs(normal * (special * normal)^0) -- 100 * 1000 * "oeps< oeps> oeps&" : gsub:lpeg == 0153:0280:0151:0080 (last one by roberto) - -- unescaped = lpeg.Cs((lpeg.S("<")/"<" + lpeg.S(">")/">" + lpeg.S("&")/"&" + 1)^0) - -- unescaped = lpeg.Cs((((lpeg.P("&")/"") * (lpeg.P("lt")/"<" + lpeg.P("gt")/">" + lpeg.P("amp")/"&") * (lpeg.P(";")/"")) + 1)^0) - local normal = (1 - lpeg.S"&")^0 - local special = lpeg.P("<")/"<" + lpeg.P(">")/">" + lpeg.P("&")/"&" - local unescaped = lpeg.Cs(normal * (special * normal)^0) + -- unescaped = Cs((S("<")/"<" + S(">")/">" + S("&")/"&" + 1)^0) + -- unescaped = Cs((((P("&")/"") * (P("lt")/"<" + P("gt")/">" + P("amp")/"&") * (P(";")/"")) + 1)^0) + local normal = (1 - S"&")^0 + local special = P("<")/"<" + P(">")/">" + P("&")/"&" + local unescaped = Cs(normal * (special * normal)^0) -- 100 * 5000 * "oeps <oeps bla='oeps' foo='bar'> oeps </oeps> oeps " : gsub:lpeg == 623:501 msec (short tags, less difference) - local cleansed = lpeg.Cs(((lpeg.P("<") * (1-lpeg.P(">"))^0 * lpeg.P(">"))/"" + 1)^0) + local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0) function xml.escaped (str) return escaped :match(str) end function xml.unescaped(str) return unescaped:match(str) end @@ -1825,9 +1889,9 @@ function xml.join(t,separator,lastseparator) result[k] = xml.tostring(v) end if lastseparator then - return table.join(result,separator or "",1,#result-1) .. (lastseparator or "") .. result[#result] + return concat(result,separator or "",1,#result-1) .. (lastseparator or "") .. result[#result] else - return table.join(result,separator) + return concat(result,separator) end else return "" @@ -1846,13 +1910,17 @@ do if unicode and unicode.utf8 then xml.entities = xml.entities or { } -- xml.entities.handler == function + function xml.entities.handler(e) + return format("[s]",e) + end + local char = unicode.utf8.char local function toutf(s) return char(tonumber(s,16)) end - function xml.utfize(root) + function utfize(root) local d = root.dt for k=1,#d do local dk = d[k] @@ -1862,22 +1930,26 @@ do if unicode and unicode.utf8 then d[k] = dk:gsub("&#x(.-);",toutf) end else - xml.utfize(dk) + utfize(dk) end end end + xml.utfize = utfize + local entities = xml.entities - local function resolve(e) - local ee = entities[e] - if ee then - return ee - elseif e:find("#x") then + local function resolve(e) -- hex encoded always first, just to avoid mkii fallbacks + if e:find("#x") then return char(tonumber(e:sub(3),16)) else - local h = entities.handler - return (h and h(e)) or "&" .. e .. ";" + local ee = entities[e] + if ee then + return ee + else + local h = xml.entities.handler + return (h and h(e)) or "&" .. e .. ";" + end end end @@ -1890,7 +1962,7 @@ do if unicode and unicode.utf8 then d[k] = dk:gsub("&(.-);",resolve) end else - xml.utfize(dk) + utfize(dk) end end end @@ -1903,7 +1975,7 @@ do if unicode and unicode.utf8 then end end - function xml.resolve_text_entities(str) + function xml.resolve_text_entities(str) -- maybe an lpeg. maybe resolve inline if str:find("&") then return (str:gsub("&(.-);",resolve)) else @@ -1919,11 +1991,11 @@ do if unicode and unicode.utf8 then end end +end end + -- xml.set_text_cleanup(xml.show_text_entities) -- xml.set_text_cleanup(xml.resolve_text_entities) -end end - --~ xml.lshow("/../../../a/(b|c)[@d='e']/f") --~ xml.lshow("/../../../a/!(b|c)[@d='e']/f") --~ xml.lshow("/../../../a/!b[@d!='e']/f") diff --git a/tex/context/base/lang-ini.lua b/tex/context/base/lang-ini.lua index bc568ebff..83e9b899f 100644 --- a/tex/context/base/lang-ini.lua +++ b/tex/context/base/lang-ini.lua @@ -283,12 +283,12 @@ languages.words.colors = { do - spacing = lpeg.S(" \n\r\t") - markup = lpeg.S("-=") - lbrace = lpeg.P("{") - rbrace = lpeg.P("}") - disc = (lbrace * (1-rbrace)^0 * rbrace)^1 -- or just 3 times, time this - word = lpeg.Cs((markup/"" + disc/"" + (1-spacing))^1) + local spacing = lpeg.S(" \n\r\t") + local markup = lpeg.S("-=") + local lbrace = lpeg.P("{") + local rbrace = lpeg.P("}") + local disc = (lbrace * (1-rbrace)^0 * rbrace)^1 -- or just 3 times, time this + local word = lpeg.Cs((markup/"" + disc/"" + (1-spacing))^1) function languages.words.load(tag, filename) local filename = input.find_file(texmf.instance,filename,'other text file') or "" @@ -325,7 +325,7 @@ do local bynode = node.traverse local bychar = string.utfcharacters - function mark_words(head,found) -- can be optimized + local function mark_words(head,found) -- can be optimized local cd = characters.data local uc = utf.char local current, start, str, language, n = head, nil, "", nil, 0 diff --git a/tex/context/base/luat-env.tex b/tex/context/base/luat-env.tex index 8fdfff295..0b6f24663 100644 --- a/tex/context/base/luat-env.tex +++ b/tex/context/base/luat-env.tex @@ -96,9 +96,10 @@ environment.formatname = "\contextformat" % tex.formatname environment.initex = \ifproductionrun false \else true \fi % tex.formatname == "" environment.version = "\contextversion" --- dofile(input.find_file(texmf.instance,"luat-env.lua","tex")) \stopruntimectxluacode +% no longer needed: dofile(input.find_file(texmf.instance,"luat-env.lua","tex")) + \chardef\ctxluaexecutionmode \zerocount % private % we start at 500, below this, we store predefined data (dumps) diff --git a/tex/context/base/luat-inp.lua b/tex/context/base/luat-inp.lua index 131b8b06b..9ab21000c 100644 --- a/tex/context/base/luat-inp.lua +++ b/tex/context/base/luat-inp.lua @@ -1245,6 +1245,8 @@ end -- work that well; the parsing is ok, but dealing with the resulting -- table is a pain because we need to work inside-out recursively +-- get rid of piecewise here, just a gmatch is ok + function input.aux.splitpathexpr(str, t, validate) -- no need for optimization, only called a few times, we can use lpeg for the sub t = t or { } @@ -1252,7 +1254,7 @@ function input.aux.splitpathexpr(str, t, validate) while true do local done = false while true do - ok = false + local ok = false str = str:gsub("([^{},]+){([^{}]-)}", function(a,b) local t = { } b:piecewise(",", function(s) t[#t+1] = a .. s end) @@ -1262,7 +1264,7 @@ function input.aux.splitpathexpr(str, t, validate) if not ok then break end end while true do - ok = false + local ok = false str = str:gsub("{([^{}]-)}([^{},]+)", function(a,b) local t = { } a:piecewise(",", function(s) t[#t+1] = s .. b end) @@ -1272,7 +1274,7 @@ function input.aux.splitpathexpr(str, t, validate) if not ok then break end end while true do - ok = false + local ok = false str = str:gsub("([,{]){([^{}]+)}([,}])", function(a,b,c) ok, done = true, true return a .. b .. c @@ -1282,7 +1284,7 @@ function input.aux.splitpathexpr(str, t, validate) if not done then break end end while true do - ok = false + local ok = false str = str:gsub("{([^{}]-)}{([^{}]-)}", function(a,b) local t = { } a:piecewise(",", function(sa) @@ -1296,7 +1298,7 @@ function input.aux.splitpathexpr(str, t, validate) if not ok then break end end while true do - ok = false + local ok = false str = str:gsub("{([^{}]-)}", function(a) ok = true return a @@ -2165,7 +2167,7 @@ do resolvers.file = resolvers.filename resolvers.path = resolvers.pathname - function resolve(instance,str) + local function resolve(instance,str) if type(str) == "table" then for k, v in pairs(str) do str[k] = resolve(instance,v) or v diff --git a/tex/context/base/luat-tex.lua b/tex/context/base/luat-tex.lua index 1081af517..894160bb6 100644 --- a/tex/context/base/luat-tex.lua +++ b/tex/context/base/luat-tex.lua @@ -207,7 +207,7 @@ if texconfig and not texlua then do ws("fonts load time - %s seconds", input.loadtime(fonts)) end if xml then - ws("xml load time - %s seconds", input.loadtime(lxml)) + ws("xml load time - %s seconds (backreferences: %i, outer filtering time: %s)", input.loadtime(xml), #lxml.self, input.loadtime(lxml)) end if mptopdf then ws("mps conversion time - %s seconds", input.loadtime(mptopdf)) @@ -245,6 +245,10 @@ if texconfig and not texlua then do if fonts then ws("loaded fonts - %s", fonts.logger.report()) -- last because it is often a long list end + if xml then -- so we are in mkiv, we need a different check + -- todo: \nofshipouts + ws("shipped out pages - %i (of %i processed pages)", tex.count['nofshipouts'], tex.count['realpageno']-1) -- last because we want to see this + end end end end diff --git a/tex/context/base/lxml-ini.lua b/tex/context/base/lxml-ini.lua index 0c26e08e4..7963e73f6 100644 --- a/tex/context/base/lxml-ini.lua +++ b/tex/context/base/lxml-ini.lua @@ -6,15 +6,41 @@ if not modules then modules = { } end modules ['lxml-ini'] = { license = "see context related readme files" } +-- todo: speed up + +local texsprint, texprint = tex.sprint or print, tex.print or print +local format, concat = string.format, table.concat +local type, next, tonumber = type, next, tonumber + document = document or { } document.xml = document.xml or { } lxml = { } lxml.loaded = { } -lxml.self = { } +lxml.myself = { } + +local loaded = lxml.loaded +local myself = lxml.myself + +lxml.self = myself -- be backward compatible for a while + +local function get_id(id) + return (type(id) == "table" and id) or loaded[id] or myself[tonumber(id)] -- no need for tonumber if we pass without "" +end + +lxml.id = get_id + +function lxml.root(id) + return loaded[id] +end do + xml.specialhandler = xml.specialhandler or { } + + local specialhandler = xml.specialhandler + local serialize = xml.serialize + local crlf = lpeg.P("\r\n") local cr = lpeg.P("\r") local lf = lpeg.P("\n") @@ -25,35 +51,60 @@ do local verbose = lpeg.C((1-(space+newline))^1) local capture = ( - newline^2 * lpeg.Cc("") / tex.print + - newline * lpeg.Cc(" ") / tex.sprint + - content / tex.sprint + newline^2 * lpeg.Cc("") / texprint + + newline * lpeg.Cc(" ") / texsprint + + content / texsprint )^0 - xml.specialhandler = { } +--~ local function sprint(root) +--~ if not root then +--~ -- quit +--~ elseif type(root) == 'string' then +--~ capture:match(root) +--~ elseif next(root) then +--~ serialize(root,sprint,nil,nil,specialhandler) +--~ end +--~ end local function sprint(root) if not root then -- quit - elseif type(root) == 'string' then - lpeg.match(capture,root) - elseif next(root) then - xml.serialize(root,sprint,nil,nil,xml.specialhandler) + else + local tr = type(root) + if tr == "string" then + capture:match(root) + elseif tr == "table" then + serialize(root,sprint,nil,nil,specialhandler) + end end end xml.sprint = sprint - function xml.tprint(root) - if type(root) == "table" then + function xml.tprint(root) -- we can move sprint inline + local tr = type(root) + if tr == "table" then for i=1,#root do sprint(root[i]) end - elseif type(root) == "string" then + elseif tr == "string" then sprint(root) end end + function xml.cprint(root) -- content + if not root then + -- quit + elseif type(root) == 'string' then + capture:match(root) + elseif root.dt then -- the main one + serialize(root.dt,sprint,nil,nil,specialhandler) + else -- probably dt + serialize(root,sprint,nil,nil,specialhandler) + end + end + + -- lines (untested) local buffer = { } @@ -68,9 +119,9 @@ do if not root then -- quit elseif type(root) == 'string' then - lpeg.match(capture,root) - elseif next(root) then - xml.serialize(root, lines) + capture:match(root) + elseif next(root) then -- tr == 'table' + serialize(root, lines) end end @@ -88,15 +139,15 @@ do local aftercommand = "" local capture = ( - newline / function( ) tex.sprint(tex.texcatcodes,linecommand .. "{}") end + - verbose / function(s) tex.sprint(tex.vrbcatcodes,s) end + - space / function( ) tex.sprint(tex.texcatcodes,spacecommand .. "{}") end + newline / function( ) texsprint(tex.texcatcodes,linecommand .. "{}") end + + verbose / function(s) texsprint(tex.vrbcatcodes,s) end + + space / function( ) texsprint(tex.texcatcodes,spacecommand .. "{}") end )^0 function toverbatim(str) - if beforecommand then tex.sprint(tex.texcatcodes,beforecommand .. "{}") end - lpeg.match(capture,str) - if aftercommand then tex.sprint(tex.texcatcodes,aftercommand .. "{}") end + if beforecommand then texsprint(tex.texcatcodes,beforecommand .. "{}") end + capture:match(str) + if aftercommand then texsprint(tex.texcatcodes,aftercommand .. "{}") end end function lxml.set_verbatim(before,after,obeyedline,obeyedspace) @@ -104,18 +155,18 @@ do end function lxml.set_cdata() - xml.specialhandler['@cd@'] = toverbatim + specialhandler['@cd@'] = toverbatim end function lxml.reset_cdata() - xml.specialhandler['@cd@'] = nil + specialhandler['@cd@'] = nil end function lxml.verbatim(id,before,after) - local root = lxml.id(id) - if before then tex.sprint(tex.ctxcatcodes,string.format("%s[%s]",before,root.tg)) end - xml.serialize(root.dt,toverbatim,nil,nil,nil,true) -- was root - if after then tex.sprint(tex.ctxcatcodes,after) end + local root = get_id(id) + if before then texsprint(tex.ctxcatcodes,format("%s[%s]",before,root.tg)) end + serialize(root.dt,toverbatim,nil,nil,nil,true) -- was root + if after then texsprint(tex.ctxcatcodes,after) end end function lxml.inlineverbatim(id) lxml.verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim") @@ -126,155 +177,169 @@ do end --- now comes the lxml one - -function lxml.id(id) - return (type(id) == "table" and id) or lxml.loaded[id] or lxml.self[tonumber(id)] -end - -function lxml.root(id) - return lxml.loaded[id] -end +local xmlsprint = xml.sprint +local xmltprint = xml.tprint -- redefine xml load xml.originalload = xml.load ---~ function xml.load(filename) ---~ input.starttiming(lxml) ---~ local x = xml.originalload(filename) ---~ input.stoptiming(lxml) ---~ return x ---~ end - function xml.load(filename) - input.starttiming(lxml) + input.starttiming(xml) local xmldata = xml.convert((filename and input.loadtexfile(texmf.instance,filename)) or "") - input.stoptiming(lxml) + input.stoptiming(xml) return xmldata end function lxml.load(id,filename) - lxml.loaded[id] = xml.load(filename) - return lxml.loaded[id], filename + loaded[id] = xml.load(filename) + return loaded[id], filename end function lxml.include(id,pattern,attribute,recurse) - input.starttiming(lxml) - xml.include(lxml.id(id),pattern,attribute,recurse,function(name) return (name and input.loadtexfile(texmf.instance,name)) or "" end) - input.stoptiming(lxml) + input.starttiming(xml) + xml.include(get_id(id),pattern,attribute,recurse,function(name) return (name and input.loadtexfile(texmf.instance,name)) or "" end) + input.stoptiming(xml) end function lxml.utfize(id) - xml.utfize(lxml.id(id)) + xml.utfize(get_id(id)) end +local xmlfilter, xmlfirst, xmllast, xmlall = xml.filter, xml.first, xml.last, xml.all +local xmlcollect, xmlcontent, xmlcollect_texts = xml.collect, xml.content, xml.collect_texts +local xmlattribute, xmlindex = xml.filters.attribute, xml.filters.index +local xmlelements = xml.elements + function lxml.filter(id,pattern) - xml.sprint(xml.filter(lxml.id(id),pattern)) + xmlsprint(xmlfilter(get_id(id),pattern)) end - function lxml.first(id,pattern) - xml.sprint(xml.first(lxml.id(id),pattern)) + xmlsprint(xmlfirst(get_id(id),pattern)) end - function lxml.last(id,pattern) - xml.sprint(xml.last(lxml.id(id),pattern)) + xmlsprint(xmllast(get_id(id),pattern)) end - function lxml.all(id,pattern) - xml.tprint(xml.collect(lxml.id(id),pattern)) + xmltprint(xmlcollect(get_id(id),pattern)) end - function lxml.nonspace(id,pattern) - xml.tprint(xml.collect(lxml.id(id),pattern,true)) + xmltprint(xmlcollect(get_id(id),pattern,true)) +end +function lxml.content(id,pattern) + xmlsprint(xmlcontent(get_id(id),pattern) or "") end function lxml.strip(id,pattern) - xml.strip(lxml.id(id),pattern) + xml.strip(get_id(id),pattern) end function lxml.text(id,pattern) - xml.tprint(xml.collect_texts(lxml.id(id),pattern) or {}) + xmltprint(xmlcollect_texts(get_id(id),pattern) or {}) end -function lxml.content(id,pattern) - xml.sprint(xml.content(lxml.id(id),pattern) or "") +function lxml.raw(id,pattern) -- the content, untouched by commands + local c = xmlfilter(get_id(id),pattern) + if c then + texsprint(concat(c.dt,"")) + end end function lxml.stripped(id,pattern) - local str = xml.content(lxml.id(id),pattern) or "" - xml.sprint((str:gsub("^%s*(.-)%s*$","%1"))) + local str = xmlcontent(get_id(id),pattern) or "" + xmlsprint((str:gsub("^%s*(.-)%s*$","%1"))) end function lxml.flush(id) - xml.sprint(lxml.id(id).dt) + xmlsprint(get_id(id).dt) end function lxml.index(id,pattern,i) - xml.sprint((xml.filters.index(lxml.id(id),pattern,i))) + xmlsprint((xmlindex(get_id(id),pattern,i))) end function lxml.attribute(id,pattern,a,default) --todo: snelle xmlatt - local str = xml.filters.attribute(lxml.id(id),pattern,a) or "" - tex.sprint((str == "" and default) or str) + local str = xmlattribute(get_id(id),pattern,a) or "" + texsprint((str == "" and default) or str) end function lxml.count(id,pattern) - tex.sprint(xml.count(lxml.id(id),pattern) or 0) + texsprint(xml.count(get_id(id),pattern) or 0) end function lxml.name(id) -- or remapped name? - local r = lxml.id(id) + local r = get_id(id) if r.ns then - tex.sprint(r.ns .. ":" .. r.tg) + texsprint(r.ns .. ":" .. r.tg) else - tex.sprint(r.tg) + texsprint(r.tg) end end function lxml.tag(id) - tex.sprint(lxml.id(id).tg or "") + texsprint(get_id(id).tg or "") end function lxml.namespace(id) -- or remapped name? - local root = lxml.id(id) - tex.sprint(root.rn or root.ns or "") + local root = get_id(id) + texsprint(root.rn or root.ns or "") end --~ function lxml.concat(id,what,separator,lastseparator) ---~ tex.sprint(table.concat(xml.collect_texts(lxml.id(id),what,true),separator or "")) +--~ texsprint(concat(xml.collect_texts(get_id(id),what,true),separator or "")) --~ end function lxml.concat(id,what,separator,lastseparator) - local t = xml.collect_texts(lxml.id(id),what,true) + local t = xmlcollect_texts(get_id(id),what,true) local separator = separator or "" local lastseparator = lastseparator or separator or "" for i=1,#t do - tex.sprint(t[i]) + texsprint(t[i]) if i == #t then -- nothing elseif i == #t-1 and lastseparator ~= "" then - tex.sprint(tex.ctxcatcodes,lastseparator) + texsprint(tex.ctxcatcodes,lastseparator) elseif separator ~= "" then - tex.sprint(tex.ctxcatcodes,separator) + texsprint(tex.ctxcatcodes,separator) end end end -function xml.command(root) -- todo: free self after usage, so maybe hash after all - -- no longer needed: xml.sflush() - if type(root.command) == "string" then - local n = #lxml.self + 1 - lxml.self[n] = root - if xml.trace_print then - texio.write_nl(string.format("tex.sprint: (((%s:%s)))",n,root.command)) - end - -- problems with empty elements - tex.sprint(tex.ctxcatcodes,string.format("\\xmlsetup{%s}{%s}",n,root.command)) -- no sprint, else spaces go wrong +-- string : setup +-- true : text (no <self></self>) +-- false : ignore +-- function : call + +-- todo: free self after usage, i.e. after the setup, which +-- means a call to lua; we can also choose a proper maximum +-- and cycle or maybe free on demand + +-- problems with empty elements +-- we use a real tex.sprint, else spaces go wrong +-- maybe just a .. because this happens often + +function xml.command(root, command) + local tc = type(command) + if tc == "string" then + -- setup + local n = #myself + 1 + myself[n] = root + texsprint(tex.ctxcatcodes,format("\\xmlsetup{%i}{%s}",n,command)) + elseif tc == "function" then + -- function + command(root) + elseif command == true then + -- text (no <self></self>) / so, no mkii fallback then +--~ local n = #myself + 1 +--~ myself[n] = root +--~ texsprint(tex.ctxcatcodes,format("\\ctxlua{lxml.flush(%s)}",n)) -- hm, efficient? +xmltprint(root.dt) + elseif command == false then + -- ignore else - root.command(root) + -- fuzzy, so ignore too end end function lxml.setaction(id,pattern,action) - for rt, dt, dk in xml.elements(lxml.id(id),pattern) do + for rt, dt, dk in xmlelements(get_id(id),pattern) do dt[dk].command = action end end @@ -283,47 +348,62 @@ lxml.trace_setups = false function lxml.setsetup(id,pattern,setup) local trace = lxml.trace_setups - if not setup or setup == "" or setup == "*" then - for rt, dt, dk in xml.elements(lxml.id(id),pattern) do + if not setup or setup == "" or setup == "*" or setup == "-" then + for rt, dt, dk in xmlelements(get_id(id),pattern) do local dtdk = dt and dt[dk] or rt local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg - if ns == "" then - dtdk.command = tg + local command = (ns == "" and tg) or (ns .. ":" .. tg) + if setup == "-" then + dtdk.command = false + if trace then + texio.write_nl(format("lpath matched -> %s -> skipped", command)) + end else - dtdk.command = ns .. ":" .. tg - end - if trace then - texio.write_nl(string.format("lpath matched -> %s -> %s", dtdk.command, dtdk.command)) + dtdk.command = command + if trace then + texio.write_nl(format("lpath matched -> %s -> %s", command, command)) + end end end else - local a, b = setup:match("^(.+:)(%*)$") + local a, b = setup:match("^(.+:)([%*%-])$") if a and b then - for rt, dt, dk in xml.elements(lxml.id(id),pattern) do + for rt, dt, dk in xmlelements(get_id(id),pattern) do local dtdk = (dt and dt[dk]) or rt local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg - dtdk.command = a .. tg - if trace then - if ns == "" then - texio.write_nl(string.format("lpath matched -> %s -> %s", tg, dtdk.command)) - else - texio.write_nl(string.format("lpath matched -> %s:%s -> %s", ns, tg, dtdk.command)) + if b == "-" then + dtdk.command = false + if trace then + if ns == "" then + texio.write_nl(format("lpath matched -> %s -> skipped", tg)) + else + texio.write_nl(format("lpath matched -> %s:%s -> skipped", ns, tg)) + end + end + else + dtdk.command = a .. tg + if trace then + if ns == "" then + texio.write_nl(format("lpath matched -> %s -> %s", tg, dtdk.command)) + else + texio.write_nl(format("lpath matched -> %s:%s -> %s", ns, tg, dtdk.command)) + end end end end else if trace then - texio.write_nl(string.format("lpath pattern -> %s -> %s", pattern, setup)) + texio.write_nl(format("lpath pattern -> %s -> %s", pattern, setup)) end - for rt, dt, dk in xml.elements(lxml.id(id),pattern) do + for rt, dt, dk in xmlelements(get_id(id),pattern) do local dtdk = (dt and dt[dk]) or rt dtdk.command = setup if trace then local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg if ns == "" then - texio.write_nl(string.format("lpath matched -> %s -> %s", tg, setup)) + texio.write_nl(format("lpath matched -> %s -> %s", tg, setup)) else - texio.write_nl(string.format("lpath matched -> %s:%s -> %s", ns, tg, setup)) + texio.write_nl(format("lpath matched -> %s:%s -> %s", ns, tg, setup)) end end end @@ -332,7 +412,7 @@ function lxml.setsetup(id,pattern,setup) end function lxml.idx(id,pattern,i) - local r = lxml.id(id) + local r = get_id(id) if r then local rp = r.patterns if not rp then @@ -344,7 +424,7 @@ function lxml.idx(id,pattern,i) end local rpi = rp[pattern] and rp[pattern][i] if rpi then - xml.sprint(rpi) + xmlsprint(rpi) end end end @@ -358,14 +438,14 @@ do command = command:gsub("^([\'\"])(.-)%1$", "%2") traverse(root, lpath(pattern), function(r,d,k) -- this can become pretty large - local n = #lxml.self + 1 - lxml.self[n] = (d and d[k]) or r - tex.sprint(tex.ctxcatcodes,string.format("\\xmlsetup{%s}{%s}",n,command)) + local n = #myself + 1 + myself[n] = (d and d[k]) or r + texsprint(tex.ctxcatcodes,format("\\xmlsetup{%s}{%s}",n,command)) end) end function lxml.command(id,pattern,command) - xml.filters.command(lxml.id(id),pattern,command) + xml.filters.command(get_id(id),pattern,command) end end @@ -403,8 +483,7 @@ do end end local root = xml.load(filename) - local format = string.format - for r, d, k in xml.elements(root,"directive") do + for r, d, k in xmlelements(root,"directive") do local dk = d[k] local at = dk.at local attribute, value, element = at.attribute or "", at.value or "", at.element or '*' @@ -431,7 +510,7 @@ do end function lxml.directives.handle_setup(category,root,attribute,element) - root = lxml.id(root) + root = get_id(root) attribute = attribute if attribute then local value = root.at[attribute] @@ -444,20 +523,19 @@ do element = ns .. ':' .. tg end end - local format = string.format local setup = data[format("%s::%s::%s",element,attribute,value)] if setup then setup = setup[category] end if setup then - tex.sprint(tex.ctxcatcodes,format("\\directsetup{%s}",setup)) + texsprint(tex.ctxcatcodes,format("\\directsetup{%s}",setup)) else setup = data[format("%s::%s::*",element,attribute)] if setup then setup = setup[category] end if setup then - tex.sprint(tex.ctxcatcodes,format("\\directsetup{%s}",setup:gsub('%*',value))) + texsprint(tex.ctxcatcodes,format("\\directsetup{%s}",setup:gsub('%*',value))) end end end @@ -467,14 +545,14 @@ do end function xml.getbuffer(name) -- we need to make sure that commands are processed - xml.tostring(xml.convert(table.join(buffers.data[name] or {},""))) + xml.tostring(xml.convert(concat(buffers.data[name] or {},""))) end function lxml.loadbuffer(id,name) - input.starttiming(lxml) - lxml.loaded[id] = xml.convert(table.join(buffers.data[name or id] or {},"")) - input.stoptiming(lxml) - return lxml.loaded[id], name or id + input.starttiming(xml) + loaded[id] = xml.convert(concat(buffers.data[name or id] or {},"")) + input.stoptiming(xml) + return loaded[id], name or id end -- for the moment here: @@ -482,7 +560,6 @@ end lxml.set_verbatim("\\xmlcdatabefore", "\\xmlcdataafter", "\\xmlcdataobeyedline", "\\xmlcdataobeyedspace") lxml.set_cdata() - do local traced = { } @@ -500,10 +577,121 @@ do if h then local d = tonumber(h,16) local u = unicode.utf8.char(d) - texio.write_nl(string.format("entity: %s / %s / %s / n=%s",h,d,u,traced[v])) + texio.write_nl(format("entity: %s / %s / %s / n=%s",h,d,u,traced[v])) else - texio.write_nl(string.format("entity: %s / n=%s",v,traced[v])) + texio.write_nl(format("entity: %s / n=%s",v,traced[v])) + end + end + end + +end + +-- yes or no ... + +do + + local function with_elements_only(e,handle) + if e and handle then + local etg = e.tg + if etg then + if e.special and etg ~= "@rt@" then + if resthandle then + resthandle(e) + end + else + local edt = e.dt + if edt then + for i=1,#edt do + local e = edt[i] + if type(e) == "table" then + handle(e) + with_elements_only(e,handle) + end + end + end + end + end + end + end + + local function with_elements_only(e,handle,depth) + if e and handle then + local edt = e.dt + if edt then + depth = depth or 0 + for i=1,#edt do + local e = edt[i] + if type(e) == "table" then + handle(e,depth) + with_elements_only(e,handle,depth+1) + end + end + end + end + end + + xml.with_elements_only = with_elements_only + + local function to_text(e) + if e.command == nil then + local etg = e.tg + if etg and e.special and etg ~= "@rt@" then + e.command = false -- i.e. skip + else + e.command = true -- i.e. no <self></self> + end + end + end + local function to_none(e) + if e.command == nil then + e.command = false -- i.e. skip + end + end + + function lxml.set_command_to_text(id) + xml.with_elements_only(get_id(id),to_text) + end + + function lxml.set_command_to_none(id) + xml.with_elements_only(get_id(id),to_none) + end + + function lxml.get_command_status(id) + local status, stack = {}, {} + local function get(e,d) + local ns, tg = e.ns, e.tg + local name = tg + if ns ~= "" then name = ns .. ":" .. tg end + stack[d] = name + local ec = e.command + if ec == true then + ec = "system: text" + elseif ec == false then + ec = "system: skip" + elseif ec == nil then + ec = "system: not set" + elseif type(ec) == "string" then + ec = "setup: " .. ec + else -- function + ec = tostring(ec) + end + local tag = table.concat(stack," => ",1,d) + local s = status[tag] + if not s then + s = { } + status[tag] = s + end + s[ec] = (s[ec] or 0) + 1 + end + if id then + xml.with_elements_only(get_id(id),get) + return status + else + local t = { } + for id, _ in pairs(lxml.loaded) do + t[id] = lxml.get_command_status(id) end + return t end end diff --git a/tex/context/base/lxml-ini.tex b/tex/context/base/lxml-ini.tex index 750e242fc..93ab1b704 100644 --- a/tex/context/base/lxml-ini.tex +++ b/tex/context/base/lxml-ini.tex @@ -28,6 +28,7 @@ \def\xmlnonspace #1#2{\ctxlua{lxml.nonspace("#1","#2")}} \def\xmltext #1#2{\ctxlua{lxml.text("#1","#2")}} \def\xmlcontent #1#2{\ctxlua{lxml.content("#1","#2")}} +\def\xmlraw #1#2{\ctxlua{lxml.raw("#1","#2")}} \def\xmlstripped #1#2{\ctxlua{lxml.stripped("#1","#2")}} \def\xmlstrip #1#2{\ctxlua{lxml.strip("#1","#2")}} \def\xmlflush #1{\ctxlua{lxml.flush("#1")}} @@ -51,10 +52,11 @@ \def\xmlremapname #1#2#3#4{\ctxlua{xml.remapname(lxml.id("#1"),"#2","#3","#4")}} % element \def\xmlremapnamespace#1#2#3{\ctxlua{xml.rename_space(lxml.id("#1"),"#2","#3")}} % document \def\xmldelete #1#2{\ctxlua{xml.delete(lxml.id("#1"),"#2")}} -\def\xmlinclude #1#2#3{\ctxlua{lxml.include(lxml.id("#1"),"#2","#3",true)}} -\def\xmldoifelse #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",false))}} -\def\xmldoifelsetext #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",true ))}} -\def\xmlverbatim #1{\ctxlua{lxml.verbatim(lxml.id("#1"))}} +\def\xmlinclude #1#2#3{\ctxlua{lxml.include("#1","#2","#3",true)}} +\def\xmlverbatim #1{\ctxlua{lxml.verbatim("#1")}} + +%def\xmldoifelse #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",false))}} +%def\xmldoifelsetext #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",true ))}} \def\xmldoifelse #1#2{\ctxlua{commands.doifelse(xml.found(lxml.id("#1"),"#2",false))}} \def\xmldoifelsetext #1#2{\ctxlua{commands.doifelse(xml.found(lxml.id("#1"),"#2",true ))}} @@ -63,6 +65,8 @@ \def\xmldoifnot #1#2{\ctxlua{commands.doifnot (xml.found(lxml.id("#1"),"#2",false))}} \def\xmldoifnottext #1#2{\ctxlua{commands.doifnot (xml.found(lxml.id("#1"),"#2",true ))}} +\def\xmldefaulttotext #1{\ifcase\xmlprocessingmode\or\or \ctxlua{lxml.set_command_to_text("#1")}\fi} +\def\xmldefaulttonone #1{\ifcase\xmlprocessingmode\or\or\or\ctxlua{lxml.set_command_to_none("#1")}\fi} % \startxmlsetups xml:include % \xmlinclude{main}{include}{filename|href} @@ -84,13 +88,25 @@ \let\xmlregistersetup\xmlappendsetup \def\xmlregisteredsetups - {\the\registeredxmlsetups\registeredxmlsetups\emptytoks} + {\xmlstarttiming + \the\registeredxmlsetups\registeredxmlsetups\emptytoks + \xmlstoptiming} + +\chardef\xmlprocessingmode=0 % 0=mixed, 1=mkivonly, 2=mkivonly-default-text, 3=mkivonly-default-none + +\def\xmlstarttiming{\ctxlua{input.starttiming(lxml)}} +\def\xmlstoptiming {\ctxlua{input.stoptiming (lxml)}} \def\doxmlprocess#1#2#3#4% {\begingroup \def\xmldocument{#2}% #1{#2}{#3}% - \enableXML + \ifcase\xmlprocessingmode + \enableXML + \fi + \xmlstarttiming + \xmldefaulttotext\xmldocument + \xmlstoptiming \doifelsenothing{#4} {\directsetup{xml:process}} {\directsetup{#4}}% @@ -104,9 +120,6 @@ \xmlall\xmldocument{/} \stopsetups -% \long\def\xmlloop#1#2#3% -% {\dorecurse{\xmlcount{#1}{#2}}{\def\xmli####1####2{\xmlidx{#1}{#2/####1}{####2}}#3}} - \long\def\xmlloop#1#2#3% {\def\xmli##1##2{\xmlidx{#1}{#2/##1}{##2}}% \dorecurse{\xmlcount{#1}{#2}}{#3}} @@ -180,7 +193,7 @@ % entities \def\xmlresolveentities - {\ctxlua{xml.set_text_cleanup(xml.set_text_cleanup(xml.resolve_text_entities))}} + {\ctxlua{xml.set_text_cleanup(xml.resolve_text_entities)}} \def\xmltraceentities {\ctxlua{xml.set_text_cleanup(lxml.trace_text_entities)}% @@ -219,7 +232,7 @@ % % An example by Wolfgang Schuster: % % \startxmlsetups xml:mysetups -% \xmlsetsetup{\xmldocument}{section{xml:*} +% \xmlsetsetup{\xmldocument}{section}{xml:*} % \xmlsetsetup{\xmldocument}{title|p}{xml:*} % \stopxmlsetups % diff --git a/tex/context/base/m-chart.tex b/tex/context/base/m-chart.tex index e08ff4276..f5d0ea7b9 100644 --- a/tex/context/base/m-chart.tex +++ b/tex/context/base/m-chart.tex @@ -13,7 +13,8 @@ % to do: \localpushmacro/\localpopmacro (dohandleflowchart etc) -% will be redone with layers and dimexpr +% will be redone with layers and dimexpr ro even better, by just using +% textext %D This is an experimental module. Pieces of code will be moved %D to other modules. More features are possible but will be diff --git a/tex/context/base/m-timing.tex b/tex/context/base/m-timing.tex index 24c2d1e9a..e86e34ab4 100644 --- a/tex/context/base/m-timing.tex +++ b/tex/context/base/m-timing.tex @@ -11,6 +11,8 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +\ifx\ShowNamedUsage\undefined \else \endinput \fi + %D Written at the end of 2007, this module is dedicated to Taco. Reaching this %D point in \LUATEX\ was a non trivial effort. By visualizing a bit what happens %D when pages come out of \LUATEX, you may get an idea what is involved. It took diff --git a/tex/context/base/math-ini.lua b/tex/context/base/math-ini.lua index b060095cf..c02be7455 100644 --- a/tex/context/base/math-ini.lua +++ b/tex/context/base/math-ini.lua @@ -103,14 +103,15 @@ function mathematics.setmathsymbol(name,class,family,slot,largefamily,largeslot, class = mathematics.classes[class] or class -- no real checks needed family = mathematics.families[family] or family -- \unexpanded ? \relax needed for the codes? + local classes = mathematics.classes if largefamily and largeslot then largefamily = mathematics.families[largefamily] or largefamily - if class == mathradical then + if class == classes.radical then tex.sprint(("\\xdef\\%s{%s\\relax}"):format(name,mathematics.radical(class,family,slot,largefamily,largeslot))) - else + elseif class == classes.open or class == classes.close then tex.sprint(("\\xdef\\%s{%s\\relax}"):format(name,mathematics.delimiter(class,family,slot,largefamily,largeslot))) end - elseif class == mathaccent then + elseif class == classes.accent then tex.sprint(("\\xdef\\%s{%s\\relax}"):format(name,mathematics.mathaccent(class,family,slot))) elseif unicode then tex.sprint(("\\xdef\\%s{%s}"):format(name,utf.char(unicode))) @@ -136,9 +137,14 @@ end mathematics.trace = false + + function mathematics.define() local slots = mathematics.slots.current - local function trace(k,c,f,i,fe,ie) + local setmathcharacter = mathematics.setmathcharacter + local setmathsymbol = mathematics.setmathsymbol + local trace = mathematics.trace + local function report(k,c,f,i,fe,ie) if fe then logs.report("mathematics",string.format("a - symbol 0x%05X -> %s -> %s %s (%s %s)",k,c,f,i,fe,ie)) elseif c then @@ -157,32 +163,32 @@ function mathematics.define() local s = slots[k] if s then local f, i, fe, ie = s[1], s[2], s[3], s[4] - if mathematics.trace then - trace(k,c,f,i,fe,ie) + if trace then + report(k,c,f,i,fe,ie) end - mathematics.setmathcharacter(k,m,f,i,fe,ie) + setmathcharacter(k,m,f,i,fe,ie) end elseif c then local s = slots[k] if s then local f, i, fe, ie = s[1], s[2], s[3], s[4] - if mathematics.trace then - trace(k,c,f,i,fe,ie) + if trace then + report(k,c,f,i,fe,ie) end - mathematics.setmathsymbol(c,m,f,i,fe,ie,k) - mathematics.setmathcharacter(k,m,f,i,fe,ie) + setmathsymbol(c,m,f,i,fe,ie,k) + setmathcharacter(k,m,f,i,fe,ie) end elseif v.contextname then local s = slots[k] local c = v.contextname if s then local f, i, fe, ie = s[1], s[2], s[3], s[4] - if mathematics.trace then - trace(k,c,f,i,fe,ie) + if trace then + report(k,c,f,i,fe,ie) end -- todo: mathortext - -- mathematics.setmathsymbol(c,m,f,i,fe,ie,k) - mathematics.setmathcharacter(k,m,f,i,fe,ie) + -- setmathsymbol(c,m,f,i,fe,ie,k) + setmathcharacter(k,m,f,i,fe,ie) end else local a = v.adobename @@ -195,10 +201,10 @@ function mathematics.define() elseif m == "number" then f, i = mathematics.families.numbers, k end - if mathematics.trace then - trace(k,a,f,i,fe,ie) + if trace then + report(k,a,f,i,fe,ie) end - mathematics.setmathcharacter(k,m,f,i,fe,ie) + setmathcharacter(k,m,f,i,fe,ie) end end end @@ -216,7 +222,7 @@ mathematics.slots.traditional = { [0x03B2] = { "lcgreek", 0x0C }, [0x03B3] = { "lcgreek", 0x0D }, [0x03B4] = { "lcgreek", 0x0E }, --- [0x03B5] = { "lcgreek", 0x00 }, -- varepsilon + [0x03B5] = { "lcgreek", 0x0F }, -- epsilon [0x03B6] = { "lcgreek", 0x10 }, [0x03B7] = { "lcgreek", 0x11 }, [0x03B8] = { "lcgreek", 0x12 }, @@ -233,7 +239,7 @@ mathematics.slots.traditional = { [0x03C3] = { "lcgreek", 0x1B }, [0x03C4] = { "lcgreek", 0x1C }, [0x03C5] = { "lcgreek", 0x1D }, - [0x03C6] = { "lcgreek", 0x1E }, +-- [0x03C6] = { "lcgreek", 0x1E }, -- varphi [0x03C7] = { "lcgreek", 0x1F }, [0x03C8] = { "lcgreek", 0x20 }, [0x03C9] = { "lcgreek", 0x21 }, @@ -263,14 +269,18 @@ mathematics.slots.traditional = { [0x03A8] = { "ucgreek", 0x09 }, [0x03A9] = { "ucgreek", 0x0A }, - [0x03B5] = { "vargreek", 0x22 }, -- varepsilon + [0x03F5] = { "vargreek", 0x22 }, -- varepsilon [0x03D1] = { "vargreek", 0x23 }, -- vartheta [0x03D6] = { "vargreek", 0x24 }, -- varpi [0x03F1] = { "vargreek", 0x25 }, -- varrho [0x03C2] = { "vargreek", 0x26 }, -- varsigma + [0x03C6] = { "vargreek", 0x27 }, -- varphi + [0x03D5] = { "lcgreek", 0x1E }, -- phi + -- [0x03F0] = { "", 0x00 }, -- varkappa + [0x0021] = { "mr", 0x21 }, -- ! [0x0028] = { "mr", 0x28 }, -- ( [0x0029] = { "mr", 0x29 }, -- ) @@ -295,18 +305,15 @@ mathematics.slots.traditional = { [0x00B7] = { "sy", 0x01 }, -- cdot [0x00D7] = { "sy", 0x02 }, -- times [0x2022] = { "sy", 0x0F }, -- bullet - [0x2190] = { "sy", 0x20 }, -- leftarrow [0x2192] = { "sy", 0x21 }, -- rightarrow [0x2194] = { "sy", 0x24 }, -- leftrightarrow - [0x21D0] = { "sy", 0x28 }, -- Leftarrow [0x21D2] = { "sy", 0x29 }, -- Rightarrow [0x21D4] = { "sy", 0x2C }, -- Leftrightarrow - [0x2135] = { "sy", 0x40 }, -- aleph [0x2113] = { "mi", 0x60 }, -- ell - + [0x22C5] = { "sy", 0x01 }, -- cdot [0x25B3] = { "sy", 0x34 }, -- triangle up [0x1D6A4] = { "mi", 0x7B }, -- imath @@ -322,6 +329,12 @@ mathematics.slots.traditional = { [0x007C] = { "sy", 0x6A, "ex", 0x0C }, -- | [0x005C] = { "sy", 0x6E, "ex", 0x0F }, -- \ + [0x220F] = { "ex", 0x51 }, -- prod + [0x2211] = { "ex", 0x50 }, -- sum + [0x222B] = { "ex", 0x52 }, -- intop + [0x005E] = { "mr", 0x5E, "ex", 0x62 }, -- widehat + [0x007E] = { "mr", 0x7E, "ex", 0x65 }, -- widetilde + } mathematics.slots.current = mathematics.slots.traditional @@ -343,3 +356,10 @@ mathematics.slots.current = mathematics.slots.traditional --~ tex.sprint(tex.ctxcatcodes,"\\endgroup") --~ end --~ end + +function mathematics.utfmathclass(chr, default) + return characters.data[utf.byte(chr)].mathclass or default or "unknown" +end +function mathematics.utfmathcommand(chr, default) + return characters.data[utf.byte(chr)].mathname or default or "unknown" +end diff --git a/tex/context/base/math-ini.mkiv b/tex/context/base/math-ini.mkiv index 49539f79e..0b1b53caa 100644 --- a/tex/context/base/math-ini.mkiv +++ b/tex/context/base/math-ini.mkiv @@ -25,4 +25,21 @@ \ctxlua{mathematics.define()} +\def\utfmathclass #1{\ctxlua{tex.sprint (mathematics.utfmathclass ("#1"))}} +\def\utfmathcommand#1{\ctxlua{commands.cs(mathematics.utfmathcommand("#1"))}} + +\def\utfmathclassdefault #1#2{\ctxlua{ + tex.sprint(mathematics.utfmathclass("#1","#2")) +}} + +\def\utfmathcommanddefault#1#2#3{\ctxlua{ + local cmd = mathematics.utfmathcommand("#1","") or "" + if cmd == "" then + commands.cs("#2","#3") + else + commands.cs(cmd) + end}} + +% \let\math@normal@int\int \def\int{\math@normal@int\intlimits} + \protect \endinput diff --git a/tex/context/base/meta-dum.tex b/tex/context/base/meta-dum.tex index d6a4525b2..1d03fb16d 100644 --- a/tex/context/base/meta-dum.tex +++ b/tex/context/base/meta-dum.tex @@ -37,25 +37,25 @@ % to transparent colors \startuseMPgraphic{placeholder}{width,height,reduction,color} - numeric w, h, d, r ; color c, b, cc ; path p ; boolean t ; - t := is_transparent(\MPvar{color}) ; - c := not_transparent(\MPvar{color}) ; - b := not_transparent(white) ; - w := \MPvar{width} ; - h := \MPvar{height} ; - r := \MPvar{reduction} ; - d := max(w,h) ; - p := unitsquare xyscaled (w,h) ; - cc := r[.5c,b] ; - fill p withcolor if t : transparent(1,.5,cc) else : cc fi ; - for i := 1 upto 60 : - cc := r[c randomized(.3,.9),b] ; - fill fullcircle - scaled (d/5 randomized (d/5)) - shifted (center p randomized (d)) - withcolor if t : transparent(1,.5,cc) else : cc fi ; - endfor ; - clip currentpicture to p ; + numeric w, h, d, r ; color c, b, cc ; path p ; boolean t ; + t := is_transparent(\MPvar{color}) ; + c := not_transparent(\MPvar{color}) ; + b := not_transparent(white) ; + w := \MPvar{width} ; + h := \MPvar{height} ; + r := \MPvar{reduction} ; + d := max(w,h) ; + p := unitsquare xyscaled (w,h) ; + cc := r[.5c,b] ; + fill p withcolor if t : transparent(1,.5,cc) else : cc fi ; + for i := 1 upto 60 : + cc := r[c randomized(.3,.9),b] ; + fill fullcircle + scaled (d/5 randomized (d/5)) + shifted (center p randomized (d)) + withcolor if t : transparent(1,.5,cc) else : cc fi ; + endfor ; + clip currentpicture to p ; \stopuseMPgraphic \definepalet diff --git a/tex/context/base/meta-fig.tex b/tex/context/base/meta-fig.mkii index 3edd73b57..3edd73b57 100644 --- a/tex/context/base/meta-fig.tex +++ b/tex/context/base/meta-fig.mkii diff --git a/tex/context/base/meta-fig.mkiv b/tex/context/base/meta-fig.mkiv new file mode 100644 index 000000000..c2c2e127f --- /dev/null +++ b/tex/context/base/meta-fig.mkiv @@ -0,0 +1,84 @@ +%D \module +%D [ file=meta-fig, +%D version=2000.09.07, +%D title=\METAPOST\ Graphics, +%D subtitle=Stand Alone Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{MetaPost Graphics / Stand Alone Graphics} + +\unprotect + +%D This module implements a method for defining +%D stand||alone||graphics, that is, each graphic gets is own +%D page. Because graphics are wrapped in a \type {\framed}, +%D you can add overlays to the graphic directly, and since the +%D whole \CONTEXT\ machinery is available, you can also add +%D page backgrounds. +%D +%D \starttyping +%D \setupMPpage +%D [offset=1pt, +%D background=color, +%D backgroundcolor=green] +%D +%D \startMPpage +%D fill fullcircle scaled 10cm withcolor red ; +%D \stopMPpage +%D +%D \startMPpage +%D fill fullsquare rotated 45 scaled 8cm withcolor blue ; +%D \stopMPpage +%D \stoptyping +%D +%D Although this is hardly of any use, you can mix these +%D definitions with the text flow, since all settings are +%D kept local. The page is clipped to the image size. + +\presetlocalframed[\??mg] + +\def\setupMPpage + {\dodoubleargument\getparameters[\??mg]} + +\def\startMPpage + {\dodoubleempty\dostartMPpage} + +\long\def\dostartMPpage[#1][#2]#3\stopMPpage % second arg gobbles space + {\dostartfittingpage[\??mg][#1]% + \processMPgraphic{#3}% + \dostopfittingpage} + +\let\stopMPpage \relax % so that we can use it in \expanded + +\setupMPpage + [\c!scale=1000, + \c!strut=\v!no, + \c!align=, + \c!offset=\v!overlay, + \c!width=\v!fit, + \c!height=\v!fit, + \c!frame=\v!off] + +%D \macros +%D {MPfigure} +%D +%D A bit out of place, here but nevertheless: + +\def\MPfigure#1#2% test for dup figure, can be replaced by a textext + {\bgroup + \getfiguredimensionsonly[#1]% [\c!object=\v!no] already set + \startMPcode + externalfigure "#1" + xscaled \figurewidth\space + yscaled \figureheight\space + #2 ; + \stopMPcode + \egroup} + +\protect \endinput diff --git a/tex/context/base/meta-ini.tex b/tex/context/base/meta-ini.mkii index d807795be..302bdff99 100644 --- a/tex/context/base/meta-ini.tex +++ b/tex/context/base/meta-ini.mkii @@ -69,6 +69,12 @@ \maxnofMPgraphics = 4000 % metafun disables the 4K boundary +\appendtoks \runMPgraphicsfalse \to \everyfastmode +\appendtoks \insertMPgraphicsfalse \to \everyfastmode +\appendtoks \flushMPgraphics \to \everygoodbye % \everylastshipout + +\def\@@MPG{@MPG@} + \startMPextensions if unknown context_tool: input mp-tool; fi; if unknown context_spec: input mp-spec; fi; @@ -485,43 +491,36 @@ \def\MPrunfile#1% {\bufferprefix mprun.#1} -%D We also have to make sure that \METAPOST\ knows this: - -\startMPextensions - if not known _data_prefix_: - string _data_prefix_,_data_suffix_; - fi; - _data_prefix_:="\bufferprefix mpd-"; - _data_suffix_:=".mpd"; -\stopMPextensions - %D \macros %D {getMPdata} %D -%D The current data is loaded with: - -\def\getMPdata - {\startreadingfile - \readlocfile\MPdatafile\donothing\donothing - \stopreadingfile} - %D When we collect graphics in one file, we run into %D troubles, since \METAPOST\ has a built in limit (of 4) %D on the number of files it can handle. It's therefore %D better to collect all data in one file and filter it. +\def\MPdataMPDfile{\jobname-mp.mpd} +\def\MPdataMPOfile{\jobname-mp.mpo} +\def\MPdataMPYfile{\jobname-mp.mpy} + +\startMPextensions + boolean collapse_data; collapse_data:=true; + def data_mpd_file = "\MPdataMPDfile" enddef ; + def data_mpo_file = "\MPdataMPOfile" enddef ; + def data_mpy_file = "\MPdataMPYfile" enddef ; +\stopMPextensions + \def\getMPdata - {\long\def\MPdata##1##2% - {\ifnum##1=\currentMPgraphic\relax##2\fi}% + {\long\def\MPdata##1##2{\ifnum##1=\currentMPgraphic\relax##2\fi}% \startreadingfile - \readlocfile{\MPgraphicfile.mpd}\donothing\donothing + \startnointerference + \readlocfile\MPdataMPDfile\donothing\donothing + \stopnointerference \stopreadingfile} %D We have to enable this mechanism with: \startMPextensions - boolean collapse_data; collapse_data:=true; - _data_suffix_:=".mpd"; % overloads previous one \stopMPextensions %D For the moment, the next one is a private macro: @@ -664,8 +663,8 @@ %D accomplished by: \def\douseMPlibrary#1% - {\ifundefined{\c!file\f!javascriptprefix#1}% - \letvalueempty{\c!file\f!javascriptprefix#1}% + {\ifundefined{\c!file\f!metapostprefix#1}% + \letvalueempty{\c!file\f!metapostprefix#1}% \makeshortfilename[\truefilename{\f!metapostprefix#1}]% \startreadingfile \readsysfile\shortfilename{\showmessage\m!metapost1{#1}}\donothing diff --git a/tex/context/base/meta-ini.mkiv b/tex/context/base/meta-ini.mkiv new file mode 100644 index 000000000..a73c92911 --- /dev/null +++ b/tex/context/base/meta-ini.mkiv @@ -0,0 +1,1110 @@ +%D \module +%D [ file=meta-ini, +%D version=2008.03.25, +%D title=\METAPOST\ Graphics, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{MetaPost Graphics / Initializations} + +\unprotect + +\startmessages dutch library: metapost + title: metapost + 1: metapost bibliotheek -- wordt geladen +\stopmessages + +\startmessages english library: metapost + title: metapost + 1: loading metapost library -- +\stopmessages + +\startmessages german library: metapost + title: metapost + 1: loading metapost library -- +\stopmessages + +\startmessages czech library: metapost + title: metapost + 1: loading metapost library -- +\stopmessages + +\startmessages italian library: metapost + title: metapost + 1: caricamento della libreria metapost -- +\stopmessages + +\startmessages norwegian library: metapost + title: metapost + 1: metapost bibliotek -- blir lest inn +\stopmessages + +\startmessages romanian library: metapost + title: metapost + 1: se incarca biblioteca metapost -- +\stopmessages + +\startmessages french library: metapost + title: metapost + 1: chargement de la bibliothèque metapost -- +\stopmessages + +%D Instead of sharing code with \MKII, I decided to copy +%D the code. Otherwise maintainance becomes a pain and after all, +%D the \MKII\ code will not change. + +\let\useMETAFUNformattrue \relax \let\useMETAFUNformatfalse\relax +\let \longMPlinestrue \relax \let \longMPlinesfalse\relax +\let \runMPgraphicstrue \relax \let \runMPgraphicsfalse\relax +\let\runMPTEXgraphicstrue \relax \let\runMPTEXgraphicsfalse\relax +\let \MPstaticgraphictrue \relax \let \MPstaticgraphicfalse\relax + +\let\maxnofMPgraphics\scratchcounter + +\newtoks \MPextensions % mp, once +\newtoks \MPinitializations % tex, each +\newtoks \MPuserinclusions % mp, user +\newtoks \MPfinalizations % mp, user +\newtoks \everyMPgraphic % mp +\newtoks \everyMPTEXgraphic % tex + +\long\def\startMPextensions#1\stopMPextensions + {\global\MPextensions\expandafter{\the\MPextensions#1}} + +\long\def\startMPinitializations#1\stopMPinitializations + {\global\MPinitializations\expandafter{\the\MPinitializations#1}} + +\long\def\startMPinclusions + {\dosingleempty\dostartMPinclusions} + +\long\def\dostartMPinclusions[#1]#2\stopMPinclusions + {\doifnot{#1}{+}{\global\MPuserinclusions\emptytoks}% + \global\MPuserinclusions\expandafter{\the\MPuserinclusions#2}} + +\def\MPinclusions + {\dosingleempty\doMPinclusions} + +\long\def\doMPinclusions[#1]#2% + {\doifnot{#1}{+}{\global\MPuserinclusions\emptytoks}% + \global\MPuserinclusions\expandafter{\the\MPuserinclusions#2}} + +\ifx \overlaywidth \undefined \def \overlaywidth {4cm} \fi +\ifx \overlayheight \undefined \def \overlayheight {3cm} \fi +\ifx \overlaylinewidth \undefined \def \overlaylinewidth {0pt} \fi + +\def\presetMPdefinitions + {\edef\overlaywidth {\overlaywidth \space}% + \edef\overlayheight {\overlayheight \space}% + \edef\overlaylinewidth{\overlaylinewidth\space}% + \edef\currentwidth {\the\hsize \space}% + \edef\currentheight {\the\vsize \space}} + +\def\currentMPformat{metafun} + +\long\def\processMPgraphic#1% todo: extensions and inclusions outside beginfig + {\blabelgroup + \enableincludeMPgraphics + \the\everyMPgraphic + \presetMPdefinitions + \setMPrandomseed % this has to change + % we need to preexpand the token lists + \setbox\MPgraphicbox\hbox\bgroup + \ctxlua { + metapost.graphic( + "\currentMPformat", + \@EA\!!bs\the\MPinitializations;\theMPrandomseed;#1;\!!es, % code + \@EA\@EA\@EA\!!bs\@EA\the\@EA\MPextensions\@EA;\the\MPuserinclusions;\!!es % optional preamble + ) + }% + \egroup + \placeMPgraphic + \global\MPextensions\emptytoks + \global\MPuserinclusions\emptytoks + \elabelgroup} + +\newif\ifsetMPrandomseed \setMPrandomseedtrue % false by default + +\def\setMPrandomseed + {\let\theMPrandomseed\empty + \ifsetMPrandomseed \ifx\getrandomnumber\undefined \else + \getrandomnumber\localMPseed\zerocount{4095}% + \def\theMPrandomseed{randomseed:=\localMPseed}% + \fi\fi} + +%D To be integrated + +\def\@@MPG{@MPG@} + +\def\doifMPgraphicelse#1% + {\blabelgroup + \doifdefinedelse{\@@MPG#1}% + {\elabelgroup\firstoftwoarguments} + {\elabelgroup\secondoftwoarguments}} + +\def\includeMPgraphic#1% + {\executeifdefined{\@@MPG#1};} + +\def\enableincludeMPgraphics + {\let\handleuseMPgraphic \secondoftwoarguments + \let\handlereusableMPgraphic\secondoftwoarguments} + +\let\MPdrawingdata\empty + +\newif\ifMPdrawingdone \MPdrawingdonefalse +\newif\ifMPshiftdrawing \MPshiftdrawingfalse + +\def\resetMPdrawing + {\globallet\MPdrawingdata\empty + \global\MPdrawingdonefalse} + +\def\pushMPdrawing + {\globalpushmacro\MPdrawingdata + \globallet\MPdrawingdata\empty} + +\def\popMPdrawing + {\globalpopmacro\MPdrawingdata} + +\def\getMPdrawing + {\ifMPdrawingdone + \expandafter\processMPgraphic\expandafter{\MPdrawingdata}% + \fi} + +\def\startMPdrawing + {\dosingleempty\dostartMPdrawing} + +\long\def\dostartMPdrawing[#1]#2\stopMPdrawing + {\relax + \bgroup + \enableincludeMPgraphics + \presetMPdefinitions % in case #2 has measures + \doifelse{#1}{-}{\convertargument#2\to\asciia}{\long\def\asciia{#2}}% + \long\xdef\MPdrawingdata{\MPdrawingdata\asciia}% + \egroup} + +\let\stopMPdrawing\relax + +\let\MPdrawingdata\empty + +\newif\ifMPdrawingdone \MPdrawingdonefalse +\newif\ifMPshiftdrawing \MPshiftdrawingfalse + +\def\resetMPdrawing + {\globallet\MPdrawingdata\empty + \global\MPdrawingdonefalse} + +\def\pushMPdrawing + {\globalpushmacro\MPdrawingdata + \globallet\MPdrawingdata\empty} + +\def\popMPdrawing + {\globalpopmacro\MPdrawingdata} + +\def\getMPdrawing + {\ifMPdrawingdone + \expandafter\processMPgraphic\expandafter{\MPdrawingdata}% + \fi} + +\def\startMPdrawing + {\dosingleempty\dostartMPdrawing} + +\long\def\dostartMPdrawing[#1]#2\stopMPdrawing + {\relax + \bgroup + \enableincludeMPgraphics + \presetMPdefinitions % in case #2 has measures + \doifelse{#1}{-}{\convertargument#2\to\asciia}{\long\def\asciia{#2}}% + \long\xdef\MPdrawingdata{\MPdrawingdata\asciia}% + \egroup} + +\let\stopMPdrawing\relax + +\long\def\startMPclip#1#2\stopMPclip + {\blabelgroup + \long\setgvalue{MPC:#1}{\ctxlua{metapost.getclippath(\!!bs#2\!!es)}}% + \elabelgroup} + +\let\stopMPclip\relax + +\def\grabMPclippath#1#2#3#4#5% + {\blabelgroup + \edef\width {#3\space}\let\overlaywidth \width + \edef\height{#4\space}\let\overlayheight\height + \doifundefined{MPC::#1} + {\doifdefinedelse{MPC:#1} + {\xdef\MPclippath{\getvalue{MPC:#1}}% + \ifx\MPclippath\empty\xdef\MPclippath{#5}\fi + \setxvalue{MPC::#1}{\MPclippath}} + {\setxvalue{MPC::#1}{#5}}}% + \xdef\MPclippath{\getvalue{MPC::#1}}% + % #2 : method is obsolete, only pdf now, we can always + % gsub the result to ps + \elabelgroup} + +%D Next we will use these support macros. + +\startMPextensions + if unknown context_tool: input mp-tool; fi; + if unknown context_spec: input mp-spec; fi; + if unknown context_grph: input mp-grph; fi; +\stopMPextensions + +%D Since we want lables to follow the document settings, we +%D also set the font related variables. + +\startMPinitializations % scale is not yet ok + defaultfont:="\truefontname{Regular}"; + defaultscale:=\the\bodyfontsize/10pt; +\stopMPinitializations + +\startMPinitializations % scale is not yet ok + defaultfont:="rm-lmtt10"; +\stopMPinitializations + +%D In order to support fancy text features (like outline +%D fonts), we set: + +\startMPextensions + graphictextformat:="context"; + graphictextdirective "\the\everyMPTEXgraphic"; +\stopMPextensions + +%D A signal that we're in combines \CONTEXT||\METAFUN mode: + +\startMPextensions + string contextversion; + contextversion:="\contextversion"; +\stopMPextensions + +%D Some safeguards: +%D +%D \starttyping +%D \appendtoks \cleanupfeatures \to \everyMPgraphic +%D \stoptyping +%D +%D No, we don't want that (else we loose UTF etc). + +%D Another one: + +\prependtoks \MPstaticgraphictrue \to \everyoverlay +\prependtoks \MPstaticgraphictrue \to \everypagebody + +% %D We save the number of graphics for the sake of \TEXEXEC. +% +% \newcounter\totalnofMPgraphics +% +% \def\thenofMPgraphics{\the\nofMPgraphics} % from supp-mps +% +% \appendtoks +% \savecurrentvalue\totalnofMPgraphics\thenofMPgraphics +% \to \everybye + +%D \macros +%D {setupMPvariables} +%D +%D When we build collections of \METAPOST\ graphics, like +%D background and buttons, the need for passing settings +%D arises. By (mis|)|using the local prefix that belongs to +%D \type {\framed}, we get a rather natural interface to +%D backgrounds. To prevent conflicts, we will use the \type +%D {-} in \METAPOST\ specific variables, like: +%D +%D \starttyping +%D \setupMPvariables[meta:button][size=20pt] +%D \stoptyping + +\def\@@meta{meta:} + +\def\setupMPvariables + {\dodoubleempty\dosetupMPvariables} + +\def\dosetupMPvariables[#1][#2]% + {\ifsecondargument + \getrawparameters[#1:][#2]% brr, todo: [\@@meta#1:] + \else + \getrawparameters[\@@meta][#1]% + \fi} + +\let\@@framed\s!unknown + +\def\MPvariable#1% + {\csname + \ifcsname\@@framed\@@meta#1\endcsname\@@framed\fi\@@meta#1% + \endcsname} + +\let\MPvar\MPvariable + +\let\setMPvariables\setupMPvariables + +\def\MPrawvar#1#2{\csname#1:#2\endcsname} + +%D \macros +%D {startuniqueMPgraphic, uniqueMPgraphic} +%D +%D This macros is probably of most use to myself, since I like +%D to use graphics that adapt themselves. The next \METAPOST\ +%D kind of graphic is both unique and reused when possible. +%D +%D \starttyping +%D \defineoverlay[example][\uniqueMPgraphic{test}] +%D +%D \startuniqueMPgraphic {test} +%D draw unitsquare xscaled \overlaywidth yscaled \overlayheight ; +%D \stopuniqueMPgraphic +%D \stoptyping + +\def\overlaystamp % watch the \MPcolor, since colors can be redefined + {\overlaywidth:\overlayheight:\overlaydepth:\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} + +%D A better approach is to let additional variables play a role +%D in determining the uniqueness. In the next macro, the +%D second, optional, argument is used to guarantee the +%D uniqueness, as well as prepare variables for passing them to +%D \METAPOST. +%D +%D \starttyping +%D \startuniqueMPgraphic{meta:hash}{gap,angle,...} +%D \stoptyping +%D +%D The calling macro also accepts a second argument. For +%D convenient use in overlay definitions, we use \type {{}} +%D instead of \type {[]}. +%D +%D \starttyping +%D \uniqueMPgraphic{meta:hash}{gap=10pt,angle=30} +%D \stoptyping + +\newcount\MPobjectcounter +\newif \ifMPshiftdrawing \MPshiftdrawingfalse +\newbox \MPgraphicbox + +\def\placeMPgraphic + {\ifMPshiftdrawing + \edef\next + {\wd\MPgraphicbox\the\wd\MPgraphicbox + \ht\MPgraphicbox\the\ht\MPgraphicbox + \dp\MPgraphicbox\the\dp\MPgraphicbox}% + \setbox\MPgraphicbox\hbox + {\hskip\MPllx\onebasepoint\raise\MPlly\onebasepoint\box\MPgraphicbox}% + \next + \fi + \box\MPgraphicbox} + +\def\reuseMPbox#1#2#3#4#5% space delimiting would save some tokens + {\xdef\MPllx{#2}% but it's not worth the effort and looks + \xdef\MPlly{#3}% ugly as well + \xdef\MPurx{#4}% + \xdef\MPury{#5}% + \getobject{MP}{#1}} + +\long\def\handleuniqueMPgraphic#1#2#3% + {\blabelgroup + \def\@@meta{#1:}% + \extendMPoverlaystamp{#2}% incl prepare + \ifundefined{\@@MPG\overlaystamp:#1}% + \enableincludeMPgraphics + \forgetall + \global\advance\MPobjectcounter\plusone + \setobject{MP}{\number\MPobjectcounter}\vbox{\processMPgraphic{#3}}% + \setxvalue{\@@MPG\overlaystamp:#1}{\noexpand\reuseMPbox{\number\MPobjectcounter}{\MPllx}{\MPlly}{\MPurx}{\MPury}}% + \fi + \getvalue{\@@MPG\overlaystamp:#1}% + \elabelgroup} + +\long\def\startuniqueMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartuniqueMPgraphic} + +\long\def\dostartuniqueMPgraphic#1#2#3\stopuniqueMPgraphic% + {\long\setgvalue{\@@MPG#1}{\handleuniqueMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\unexpanded\def\uniqueMPgraphic + {\dodoublegroupempty\douniqueMPgraphic} + +\def\douniqueMPgraphic#1#2% + {\blabelgroup + \setupMPvariables[#1][#2]% + \getvalue{\@@MPG#1}\empty + \elabelgroup} + +\let\stopuniqueMPcode \relax % so that we can use it in \expanded + +\long\def\handleuseMPgraphic#1#2#3% + {\blabelgroup + \forgetall % check this + \def\@@meta{#1:}% + \prepareMPvariables{#2}% + \enableincludeMPgraphics + \processMPgraphic{#3}% + \elabelgroup} + +\long\def\startuseMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartuseMPgraphic} + +\long\def\dostartuseMPgraphic#1#2#3\stopuseMPgraphic + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\long\def\startusableMPgraphic % redundant but handy + {\blabelgroup + \dodoublegroupempty\dostartusableMPgraphic} + +\long\def\dostartusableMPgraphic#1#2#3\stopusableMPgraphic + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\let\stopuseMPgraphic \relax % so that we can use it in \expanded +\let\stopusableMPgraphic \relax % so that we can use it in \expanded + +\long\def\handlereusableMPgraphic#1#2#3% + {\blabelgroup + \def\@@meta{#1:}% + \prepareMPvariables{#2}% + \enableincludeMPgraphics + \global\advance\MPobjectcounter\plusone + \setobject{MP}{\number\MPobjectcounter}\vbox{\processMPgraphic{#3}}% + \setxvalue{\@@MPG#1}{\noexpand\reuseMPbox{\number\MPobjectcounter}{\MPllx}{\MPlly}{\MPurx}{\MPury}}% + \getvalue{\@@MPG#1}% + \elabelgroup} + +\long\def\startreusableMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartreusableMPgraphic} + +\long\def\dostartreusableMPgraphic#1#2#3\stopreusableMPgraphic + {\long\setgvalue{\@@MPG#1}{\handlereusableMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\let\stopreusableMPgraphic \relax % so that we can use it in \expanded + +\unexpanded\def\useMPgraphic + {\dodoublegroupempty\douseMPgraphic} + +\def\douseMPgraphic#1#2% + {\blabelgroup + \doifsomething{#2}{\setupMPvariables[#1][#2]}% + \getvalue{\@@MPG#1}\empty + \elabelgroup} + +\let\reuseMPgraphic \useMPgraphic % we can save a setup here if needed +\let\reusableMPgraphic\reuseMPgraphic % we can save a setup here if needed + +\let\stopuseMPcode \relax % so that we can use it in \expanded +\let\stopusableMPcode \relax % so that we can use it in \expanded +\let\stopreusableMPcode \relax % so that we can use it in \expanded +\let\stopuniqueMPcode \relax % so that we can use it in \expanded + +\def\enableincludeMPgraphics + {\let\handleuseMPgraphic \thirdofthreearguments + \let\handlereusableMPgraphic\thirdofthreearguments} + +%D \macros +%D {startuniqueMPpagegraphic,uniqueMPpagegraphic} +%D +%D Experimental. + +\def\MPpageprefix{\doifoddpageelse oe:} + +\def\overlaypagestamp + {\MPpageprefix\overlaywidth:\overlayheight:\overlaydepth:\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} + +\long\def\startuniqueMPpagegraphic + {\blabelgroup + \dodoublegroupempty\dostartuniqueMPpagegraphic} + +\long\def\dostartuniqueMPpagegraphic#1#2#3\stopuniqueMPpagegraphic + {\long\setgvalue{\@@MPG o:#1}{\handleuniqueMPgraphic{o:#1}{#2}{#3}}% + \long\setgvalue{\@@MPG e:#1}{\handleuniqueMPgraphic{e:#1}{#2}{#3}}% + \elabelgroup} + +\unexpanded\def\uniqueMPpagegraphic + {\dodoublegroupempty\douniqueMPpagegraphic} + +\def\douniqueMPpagegraphic#1#2% + {\blabelgroup + \let\overlaystamp\overlaypagestamp + \setupMPvariables[\MPpageprefix#1][#2]% prefix is new here + \getvalue{\@@MPG\MPpageprefix#1}{}% + \elabelgroup} + +%D One way of defining a stamp is: +%D +%D \starttyping +%D \def\extendMPoverlaystamp#1% +%D {\def\docommand##1% +%D {\edef\overlaystamp{\overlaystamp:\MPvariable{##1}}}% +%D \processcommalist[#1]\docommand} +%D \stoptyping + +%D Since we need to feed \METAPOST\ with expanded dimensions, +%D we introduce a dedicated expansion engine. + +\def\prepareMPvariable#1% + {\ifundefined{\@@framed\@@meta#1}% + \doprepareMPvariable{\@@meta#1}% + \else + \doprepareMPvariable{\@@framed\@@meta#1}% + \fi} + +% \startlines +% \def\xxx{\lineheight} \doprepareMPvariable{xxx} \xxx +% \def\xxx{2pt} \doprepareMPvariable{xxx} \xxx +% \def\xxx{2} \doprepareMPvariable{xxx} \xxx +% \def\xxx{\scratchcounter} \doprepareMPvariable{xxx} \xxx +% \def\xxx{red} \doprepareMPvariable{xxx} \xxx +% \def\xxx{0.4} \doprepareMPvariable{xxx} \xxx +% \stoplines + +\def\doprepareMPvariable#1% + {\edef\theMPvariable{\getvalue{#1}}% + \doifelsenothing\theMPvariable + {\setevalue{#1}{\MPcolor{black}}} + {\defconvertedcommand\ascii\theMPvariable % otherwise problems + \doifcolorelse \ascii % with 2\bodyfontsize + {\setevalue{#1}{\MPcolor\theMPvariable}} + {% can be aux macro + \setbox\scratchbox\hbox{\scratchdimen\theMPvariable sp}% + \ifdim\wd\scratchbox=\zeropoint + % \scratchcounter\theMPvariable + % \setevalue{#1}{\the\scratchcounter}% + % also accepts 0.number : + \setevalue{#1}{\number\theMPvariable}% + \else + \scratchdimen\theMPvariable + \setevalue{#1}{\the\scratchdimen}% + \fi}}} + +%D We redefine \type {\extendMPoverlaystamp} to preprocess +%D variables using \type {\prepareMPvariable}. + +\def\doextendMPoverlaystamp#1% + {\prepareMPvariable{#1}% + \edef\overlaystamp{\overlaystamp:\MPvariable{#1}}} + +\def\extendMPoverlaystamp#1% + {\processcommalist[#1]\doextendMPoverlaystamp} + +\def\prepareMPvariables#1% + {\processcommalist[#1]\prepareMPvariable} + +%D \macros +%D {MPdatafile} +%D +%D We redefine a macro from \type {supp-mps.tex}: + +\def\MPdataMPDfile{\jobname-mp.mpd} +\def\MPdataMPOfile{\jobname-mp.mpo} +\def\MPdataMPYfile{\jobname-mp.mpy} + +\startMPextensions + boolean collapse_data; collapse_data:=true; + def data_mpd_file = "\MPdataMPDfile" enddef ; + def data_mpo_file = "\MPdataMPOfile" enddef ; + def data_mpy_file = "\MPdataMPYfile" enddef ; +\stopMPextensions + +\newcount\currentMPgraphic \currentMPgraphic\zerocount + +\def\getMPdata + {%\let\MPdata\secondoftwoarguments + \long\def\MPdata##1##2{\ifnum##1=\currentMPgraphic\relax##2\fi}% + \startreadingfile + \startnointerference + \readlocfile\MPdataMPDfile\donothing\donothing + \stopnointerference + \stopreadingfile} + +%D \macros +%D {MPrunfile} +%D +%D This one is more abstract and does not assume knowledge +%D of buffer prefixes. + +\def\MPrunfile#1% + {\bufferprefix mprun.#1} + +%D For the moment, the next one is a private macro: + +% TODO ! ! ! ! ! ! + +\def\processMPbuffer + {\dosingleempty\doprocessMPbuffer} + +\def\doprocessMPbuffer[#1]% + {\doifelsenothing{#1} + {\doprocessMPbuffer[\jobname]} + {\processMPgraphic{\ctxlua{tex.sprint(tex.ctxcatcodes,buffers.collect(string.split("#1",",")))}}}} + +\def\runMPbuffer + {\dosingleempty\dorunMPbuffer} + +\def\dorunMPbuffer[#1]% processing only + {\startnointerference\doprocessMPbuffer[#1]\stopnointerference} + +%D \macros +%D {startMPenvironment, resetMPenvironment} +%D +%D In order to synchronize the main \TEX\ run and the runs +%D local to \METAPOST, environments can be passed. + +\ifx\everyMPTEXgraphic\undefined + \newtoks\everyMPTEXgraphic +\fi + +%D A more general way of passing environments is: + +\def\startMPenvironment % second arg gobbles spaces, so that reset gives \emptytoks + {\bgroup + \catcode`\^^M=\@@space + \dodoubleempty\dostartMPenvironment} + +\long\def\dostartMPenvironment[#1][#2]#3\stopMPenvironment + {\egroup + \doif{#1}\s!reset\resetMPenvironment % reset mp toks + \doif{#1}\v!global{#3}% % use in main doc too + \doif{#1}+{#3}% % use in main doc too + \defconvertedargument\ascii{#3}% + \expandafter\appendtoks\ascii\to\everyMPTEXgraphic} + +\def\resetMPenvironment + {\everyMPTEXgraphic\emptytoks % = is really needed ! + \startMPenvironment + \global\loadfontfileoncetrue + \stopMPenvironment} + +\resetMPenvironment + +\def\useMPenvironmentbuffer[#1]% + {\expanded{\startMPenvironment\noexpand\readfile{\TEXbufferfile{\jobname}}{}{}}\stopMPenvironment} + +\useMPenvironmentbuffer[mp] + +%D This command takes \type {[reset]} as optional +%D argument. +%D +%D \starttyping +%D \startMPenvironment +%D \setupbodyfont[pos,14.4pt] +%D \stopMPenvironment +%D +%D \startMPcode +%D draw btex \sl Hans Hagen etex scaled 5 ; +%D \stopMPcode +%D \stoptyping +%D +%D The most simple case: + +\long\def\startMPcode#1\stopMPcode + {\processMPgraphic{#1}} + +\let\stopMPcode\relax + +%D The \type {\resetMPenvironment} is a quick way to erase +%D the token list. +%D +%D You should be aware of independencies. For instance, if you use a font +%D in a graphic that is not used in the main document, you need to load the +%D typescript at the outer level (either directly or by using the global +%D option). +%D +%D \starttyping +%D \usetypescript[palatino][texnansi] +%D +%D \startMPenvironment +%D \usetypescript[palatino][texnansi] +%D \enableregime[utf] +%D \setupbodyfont[palatino] +%D \stopMPenvironment +%D +%D \startMPpage +%D draw btex aap‒noot coördinatie – één etex ; +%D \stopMPpage +%D \stoptyping + +%D Loading specific \METAPOST\ related definitions is +%D accomplished by: + +\def\douseMPlibrary#1% + {\ifundefined{\c!file\f!metapostprefix#1}% + \letvalueempty{\c!file\f!metapostprefix#1}% + \makeshortfilename[\truefilename{\f!metapostprefix#1}]% + \startreadingfile + \readsysfile\shortfilename{\showmessage\m!metapost1{#1}}\donothing + \stopreadingfile + \fi} + +\def\useMPlibrary[#1]% + {\processcommalist[#1]\douseMPlibrary} + +%D \macros +%D {setMPtext, MPtext, MPstring, MPbetex} +%D +%D To be documented: +%D +%D \starttyping +%D \setMPtext{identifier}{text} +%D +%D \MPtext {identifier} +%D \MPstring{identifier} +%D \MPbetex {identifier} +%D \stoptyping + +\def\@@MPT{@MPT@} + +\def\forceMPTEXgraphic + {\long\def\checkMPTEXgraphic##1{\global\MPTEXgraphictrue}} + +\def\setMPtext#1#2% todo : #1 must be made : safe + {%\forceMPTEXgraphic + \defconvertedargument\ascii{#2}% + \dodoglobal\letvalue{\@@MPT#1}\ascii} + +\def\MPtext #1{\executeifdefined{\@@MPT#1}\empty} +\def\MPstring #1{"\executeifdefined{\@@MPT#1}\empty"} +\def\MPbetex #1{btex \executeifdefined{\@@MPT#1}\empty\space etex} + +%D Unfortunately \METAPOST\ does not have \CMYK\ support +%D built in, but by means of specials we can supply the +%D information needed to handle them naturaly. + +\newif\ifMPcmykcolors \MPcmykcolorstrue +\newif\ifMPspotcolors \MPspotcolorstrue + +\startMPinitializations + cmykcolors:=\ifMPcmykcolors true\else false\fi; + spotcolors:=\ifMPspotcolors true\else false\fi; +\stopMPinitializations + +%D In order to communicate conveniently with the \TEX\ +%D engine, we introduce some typesetting variables. + +\startMPextensions + color OverlayColor,OverlayLineColor; +\stopMPextensions + +\startMPinitializations + OverlayWidth:=\overlaywidth; + OverlayHeight:=\overlayheight; + OverlayDepth:=\overlayheight; + OverlayColor:=\MPcolor{\overlaycolor}; + OverlayLineWidth:=\overlaylinewidth; + OverlayLineColor:=\MPcolor{\overlaylinecolor}; + % + BaseLineSkip:=\the\baselineskip; + LineHeight:=\the\baselineskip; + BodyFontSize:=\the\bodyfontsize; + % + TopSkip:=\the\topskip; + StrutHeight:=\strutheight; + StrutDepth:=\strutdepth; + % + CurrentWidth:=\the\hsize; + CurrentHeight:=\the\vsize; + % + EmWidth:=\the\emwidth; + ExHeight:=\the\exheight; + % + PageNumber:=\the\pageno; + RealPageNumber:=\the\realpageno; + LastPageNumber:= \lastpage; +\stopMPinitializations + +\appendtoks + \disablediscretionaries + \disablecompoundcharacters +\to \everyMPgraphic + +\appendtoks + \expanded{\definecolor[currentcolor][\currentcolorname]}% +\to \everyMPgraphic + +\appendtoks + \baselineskip1\baselineskip + \lineheight 1\lineheight + \topskip 1\topskip +\to \everyMPgraphic + +\appendtoks + \let \# \letterhash + \let \_ \letterunderscore + \let \& \letterampersand + \let \{ \letteropenbrace + \let \} \letterclosebrace +\to \everyMPgraphic + +%D Alas, the prologue settings differ per driver. + +\ifx\undefined\MPprologues \def\MPprologues{0} \fi + +\startMPinitializations + prologues:=\MPprologues; + mpprocset:=1; +\stopMPinitializations + +\appendtoks + \def\MPprologues{0}% + \def\MPOSTdriver{dvips}% +\to \everyresetspecials + +%D \macros +%D {PDFMPformoffset} +%D +%D In \PDF, forms are clipped and therefore we have to take +%D precautions to get this right. Since this is related to +%D objects, we use the same offset as used there. + +\def\PDFMPformoffset{\objectoffset} + +%D \macros +%D {insertMPfile} +%D +%D Bypassing the special driver and figure mechanism is not +%D that nice but saves upto 5\% time in embedding \METAPOST\ +%D graphics by using the low level \PDF\ converter directly, +%D given of course that we use \PDFTEX. As a result we need to +%D fool around with the object trigger. + +\newtoks\everyinsertMPfile + +\let\insertMPfileARG\insertMPfile + +\def\insertMPfile#1#2% in context #2 is empty + {\doifelsenothing{#2}{\doinsertMPfile{#1}}{\insertMPfileARG{#1}{#2}}} + +\def\includeMPasEPS#1% untested !! + {\bgroup + \message{[MP as EPS #1]}% + \the\everyinsertMPfile + \dogetEPSboundingbox{#1}\!!widtha\!!heighta\!!widthb\!!heightb + \setbox\scratchbox\vbox to \!!heightb + {\vfill + \let \@@DriverImageType \c!mps + \def \@@DriverImageFile {#1}% + \edef\@@DriverImageWidth {\the\!!widthb }% + \edef\@@DriverImageHeight{\the\!!heightb}% + \doinsertfile}% + \wd\scratchbox\!!widthb + \dp\scratchbox\zeropoint + \box\scratchbox + \egroup} + +\def\includeMPasPDF#1% + {\bgroup + \the\everyinsertMPfile + \ifinobject \else \chardef\makeMPintoPDFobject\plustwo \fi % when needed + \convertMPtoPDF{#1}{1}{1}% no \plusone ! + \egroup} + +%D So, using a low level approach (thereby avoiding the slower +%D figure analysis macros) pays off. This kind of +%D optimizations are a bit tricky since we must make sure that +%D special resources end up in the (PDF) files. Because the +%D \METAPOST\ to \PDF\ can handle objects itself, it is not +%D that complicated. + +%D We hook a couple of initializations into the graphic +%D macros. + +\appendtoks + \let\figuretypes\c!mps + \runutilityfilefalse + \consultutilityfilefalse +\to \everyinsertMPfile + +%D One more: (still needed?) + +\startMPextensions + def initialize_form_numbers = + do_initialize_numbers; + enddef; +\stopMPextensions + +\startMPinitializations + HSize:=\the\hsize ; + VSize:=\the\vsize ; +\stopMPinitializations + +\startMPextensions + vardef ForegroundBox = + unitsquare xysized(HSize,VSize) + enddef ; + vardef PageFraction = + if \lastpage>1: (\realfolio-1)/(\lastpage-1) else: 1 fi + enddef ; +\stopMPextensions + +%D And some more. These are not really needed since we +%D don't use the normal figure inclusion macros any longer. + +\appendtoks + \externalfigurepostprocessors\emptytoks % safeguard +\to \everyinsertMPfile + +%D We also take care of disabling fancy figure features, that +%D can terribly interfere when dealing with symbols, +%D background graphics and running (postponed) graphics. +%D You won't believe me if I tell you what funny side effects +%D can occur. One took me over a day to uncover when +%D processing the screen version of the \METAFUN\ manual. + +%D For my eyes only: + +\def\doifelseMPgraphic#1{\doifdefinedelse{\@@MPG#1}} + +%D \macros +%D {startMPcolor} +%D +%D The following time consuming method uses \METAPOST\ to +%D calculate a color. This enables a match between colors +%D resulting from a complex calculation (e.g. for a title +%D page) and those in the text. + +% \startuseMPgraphic{somecolors} +% color c[] ; c[1] := .7[red,green] ; c[2] := .7[blue,yellow] ; +% \stopuseMPgraphic + +% \startMPcolor[shade-1][t=.2,a=1] +% \includeMPgraphic{somecolors} ; fill fullcircle withcolor c[1] ; +% \stopMPcolor + +% \startMPcolor[shade-2][t=.2,a=1] +% \includeMPgraphic{somecolors} ; fill fullcircle withcolor c[2] ; +% \stopMPcolor + +% \blackrule[width=\hsize,height=4cm,color=shade-1] +% \blackrule[width=\hsize,height=4cm,color=shade-2] + +\def\startMPcolor + {\dodoubleempty\dostartMPcolor} + +\long\def\dostartMPcolor[#1][#2]#3\stopMPcolor % slow but sometimes handy + {\startnointerference + \def\handleMPgraycolor{\expanded{\defineglobalcolor[#1][s=\!MPgMPa1,#2]}}% + \def\handleMPrgbcolor {\expanded{\defineglobalcolor[#1][r=\!MPgMPa1,g=\!MPgMPa2,b=\!MPgMPa3,#2]}}% + \def\handleMPcmykcolor{\expanded{\defineglobalcolor[#1][c=\!MPgMPa1,m=\!MPgMPa2,y=\!MPgMPa3,k=\!MPgMPa4,#2]}}% + \processMPgraphic{#3}% + \stopnointerference} + +%D New: + +\definelayerpreset % no dx,dy - else nasty non-mp placement + [mp] + [\c!y=-\MPury bp, + \c!x=\MPllx bp, + \c!method=\v!fit] + +\definelayer + [mp] + [\c!preset=mp] + +%D Usage: +%D +%D \starttyping +%D \defineproperty[one][layer][state=start] +%D \defineproperty[two][layer][state=stop] +%D +%D \startuseMPgraphic{step-1} +%D fill fullcircle scaled 10cm withcolor red ; +%D \stopuseMPgraphic +%D +%D \startuseMPgraphic{step-2} +%D fill fullcircle scaled 5cm withcolor green ; +%D \stopuseMPgraphic +%D +%D \setlayer[mp]{\property[one]{\useMPgraphic{step-1}}} +%D \setlayer[mp]{\property[two]{\useMPgraphic{step-2}}} +%D +%D \ruledhbox{\flushlayer[mp]} +%D \stoptyping +%D +%D Reusing graphics is also possible (now): +%D +%D \starttyping +%D \startreusableMPgraphic{axis} +%D tickstep := 1cm ; ticklength := 2mm ; +%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ; +%D tickstep := tickstep/2 ; ticklength := ticklength/2 ; +%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ; +%D \stopreusableMPgraphic +%D +%D \startuseMPgraphic{demo} +%D drawpoint "1cm,1.5cm" ; +%D \stopuseMPgraphic +%D +%D \definelayer[mp][preset=mp] +%D \setlayer[mp]{\reuseMPgraphic{axis}} +%D \setlayer[mp]{\useMPgraphic{demo}} +%D \ruledhbox{\flushlayer[mp]} +%D \stoptyping + +%D \macros +%D {startstaticMPfigure,useMPstaticfigure} +%D +%D Static figures are processed only when there has been +%D something changed. Here is Aditya Mahajan's testcase: +%D +%D \startbuffer +%D \startstaticMPfigure{circle} +%D fill fullcircle scaled 1cm withcolor blue; +%D \stopstaticMPfigure +%D +%D \startstaticMPfigure{axis} +%D drawarrow (0,0)--(2cm,0) ; +%D drawarrow (0,0)--(0,2cm) ; +%D label.llft(textext("(0,0)") ,origin) ; +%D \stopstaticMPfigure +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +\def\usestaticMPfigure + {\dodoubleempty\dousestaticMPfigure} + +\def\dousestaticMPfigure[#1][#2]% + {\ifsecondargument + \scale[#2]{\reuseMPgraphic{#1@S@}}% + \else + \reuseMPgraphic{#1@S@}% + \fi} + +\def\startstaticMPfigure#1#2\stopstaticMPfigure + {\startreusableMPgraphic{static:#1}#2\stopreusableMPgraphic} + +\long\def\startstaticMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartstaticMPgraphic} + +\long\def\dostartstaticMPgraphic#1#2#3\stopstaticMPgraphic + {\long\setgvalue{\@@MPG#1@S@}{\handlereusableMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +%D New: + +\newconditional\manyMPspecials % when set to true, > 1000 specials can be used + +\settrue \manyMPspecials % per 1/4/2006 + +\prependtoks + _special_div_ := 1000\ifconditional\manyMPspecials0\fi ; +\to \MPextensions + +%D Needed (will become default): + +\prependtoks + \resetlanguagespecifics +\to \everyMPgraphic + +%D Needed too. + +\let\initializeMPgraphics\relax + +% Done. + +\protect \endinput diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua index 64c886950..648ff1afb 100644 --- a/tex/context/base/meta-pdf.lua +++ b/tex/context/base/meta-pdf.lua @@ -609,9 +609,9 @@ do function mptopdf.parsers.lpeg() if mptopdf.data:find("%%%%BeginResource: procset mpost") then - lpeg.match(captures_new,mptopdf.data) + captures_new:match(mptopdf.data) else - lpeg.match(captures_old,mptopdf.data) + captures_old:match(mptopdf.data) end end diff --git a/tex/context/base/meta-tex.tex b/tex/context/base/meta-tex.mkii index 720de6fb3..720de6fb3 100644 --- a/tex/context/base/meta-tex.tex +++ b/tex/context/base/meta-tex.mkii diff --git a/tex/context/base/meta-tex.mkiv b/tex/context/base/meta-tex.mkiv new file mode 100644 index 000000000..6afc7efc5 --- /dev/null +++ b/tex/context/base/meta-tex.mkiv @@ -0,0 +1,35 @@ +%D \module +%D [ file=meta-tex, +%D version=2006.06.07, +%D title=\CONTEXT\ Support Macros, +%D subtitle=\METAPOST\ fast text insertion, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +\long\def\startTeXtexts#1\stopTeXtexts{[do we need TeXtexts in MkIV]} +\long\def\doTeXtext[#1]#2{[do we need TeXtexts in MkIV]} +\long\def\TeXtext{\dosingleempty\doTeXtext} + +\def\definetextext[#1]#2{\setvalue{textext@@#1}{#2}} + +% \definetextext[framed]{\framed} +% +% \startMPcode +% draw \sometxt[framed]{black} rotated 45 ; +% \stopMPcode + +\def\definetextext[#1]#2{\setvalue{@@st@@[#1]}{#2}} + +\long\def\sometxt#1#{\dosometxt{#1}} % grab optional [args] + +\long\def\dosometxt#1#2% + {textext("\ifcsname @@st@@#1\endcsname\csname @@st@@#1\endcsname{#2}\else#2\fi")} + +\protect \endinput diff --git a/tex/context/base/meta-txt.tex b/tex/context/base/meta-txt.tex index de4239e85..564a40752 100644 --- a/tex/context/base/meta-txt.tex +++ b/tex/context/base/meta-txt.tex @@ -24,6 +24,9 @@ % textext ipv btex ... etex +\ifx\undefined\MPtoks \newtoks\MPtoks \fi +\ifx\undefined\MPnox \newbox \MPbox \fi + \unprotect \startMPextensions @@ -46,16 +49,16 @@ \def\startshapetext[#1]% {\global\newcounter\currentshapetext - \global\setbox\shapetextbox=\vbox\bgroup + \global\setbox\shapetextbox\vbox\bgroup \expanded{\switchtobodyfont[\@@shbodyfont]}% \dontcomplain \hsize\parwidth \setuptolerance[\v!verytolerant,\v!stretch]% - \!!counta=0 - \!!toksa=\emptytoks + \!!counta\zerocount + \!!toksa\emptytoks \def\docommand##1% - {\setbox\scratchbox=\hbox{\useMPgraphic{##1}}% - \global\chardef\parfirst=0 + {\setbox\scratchbox\hbox{\useMPgraphic{##1}}% + \global\chardef\parfirst\zerocount \getMPdata % \readlocfile{\MPdatafile}{}{}% \setshapecharacteristics \advance\!!counta by \parlines @@ -71,7 +74,7 @@ \ifparseries\def\par{\endgraf\adaptparshape}\fi \EveryPar{\begstrut}} -\def\stopshapetext% +\def\stopshapetext {\endstrut %\removebottomthings \egroup @@ -104,8 +107,8 @@ \global\parhoffset \getvalue{parhoffset:\currentshapetext}% \global\parwidth \getvalue{parwidth:\currentshapetext}% \global\parheight \getvalue{parheight:\currentshapetext}} - {\global\parlines 1 - \global\chardef\parfirst 0 + {\global\parlines \plusone + \global\chardef\parfirst \zerocount \global\parvoffset \zeropoint \global\parhoffset \zeropoint \global\parwidth \hsize @@ -172,7 +175,7 @@ \MPtoks\emptytoks \resetMPdrawing \startMPdrawing - \includeMPgraphic{followtokens} + \includeMPgraphic{followtokens} ; picture pic[] ; numeric len[], n ; n := 0 ; \stopMPdrawing \handletokens#1\with\processfollowingtoken @@ -268,10 +271,10 @@ [medium] \startuniqueMPgraphic{EnglishRule}{height,width,color} - height = \MPvar{height} ; - x1 = 0 ; x3 = \MPvar{width} ; x2 = x4 = .5x3 ; - y1 = y3 = 0 ; y2 = -y4 = height/2 ; - fill z1..z2..z3 & z3..z4..z1 & cycle withcolor \MPvar{color} ; + height = \MPvar{height} ; + x1 = 0 ; x3 = \MPvar{width} ; x2 = x4 = .5x3 ; + y1 = y3 = 0 ; y2 = -y4 = height/2 ; + fill z1..z2..z3 & z3..z4..z1 & cycle withcolor \MPvar{color} ; \stopuniqueMPgraphic \def\EnglishRule% diff --git a/tex/context/base/mlib-ctx.lua b/tex/context/base/mlib-ctx.lua new file mode 100644 index 000000000..6ada8ad19 --- /dev/null +++ b/tex/context/base/mlib-ctx.lua @@ -0,0 +1,39 @@ +if not modules then modules = { } end modules ['mlib-ctx'] = { + version = 1.001, + comment = "companion to mlib-ctx.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- todo + +local format, join = string.format, table.concat +local sprint = tex.sprint + +metapost = metapost or {} +metapost.defaultformat = "metafun" + +function metapost.graphic(mpsformat,str,preamble) + local mpx = metapost.format(mpsformat or metapost.defaultformat) + metapost.graphic_base_pass(mpx,str,preamble) +end + +function metapost.filterclippath(result) + if result then + local figures = result.fig + if figures and #figures > 0 then + local figure = figures[1] + local objects = figure:objects() + if objects then + for o=1,#objects do + local object = objects[o] + if object.type == "start_clip" then + return join(flushnormalpath(object.path,{ }),"\n") + end + end + end + end + end + return "" +end diff --git a/tex/context/base/mlib-ctx.tex b/tex/context/base/mlib-ctx.tex new file mode 100644 index 000000000..6f56b7e68 --- /dev/null +++ b/tex/context/base/mlib-ctx.tex @@ -0,0 +1,81 @@ +%D \module +%D [ file=mlib-ctx, +%D version=2008.03.25, +%D title=\METAPOST\ Integrated Graphics, +%D subtitle=Basics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This file contains the \MPLIB\ variants of the by now ancient +%D \MPTOPDF\ code. + +\writestatus{loading}{MetaPost Library Graphics / Initializations} + +\registerctxluafile{mlib-run}{1.001} +\registerctxluafile{mlib-ctx}{1.001} + +\unprotect + +\protect \endinput + +% local mpgraphic = [[ +% for i=1 upto 1000 : +% beginfig(0); +% draw halfcircle scaled 1cm withcolor green ; +% picture p ; p := "oeps" infont defaultfont scaled .75 rotated 45 ; +% p := p shifted - (xpart center p,0) ; +% draw p ; draw boundingbox p ; +% endfig ; +% beginfig(0); +% draw halfcircle scaled 1cm dashed evenly withcolor green ; +% endfig ; +% beginfig(0); +% pickup pencircle xscaled .5mm yscaled .25mm rotated 45 ; +% draw halfcircle scaled 1cm withcolor red ; +% endfig ; +% beginfig(0); +% draw halfcircle scaled 1cm ; +% endfig ; +% beginfig(0); +% pickup pencircle xscaled .5mm yscaled .25mm rotated 45 ; +% for k:=1 upto 10 : +% draw halfcircle scaled uniformdeviate(1cm) withcolor (red/(k/4)) ; +% endfor ; +% endfig ; +% endfor ; +% ]] +% -- local mpx = metapost.format("metafun") +% metapost.process(metapost.format("metafun"),mpgraphic) + +% \starttext +% \setupcolors[state=start] +% \definecolor[red] [r=1] +% \definecolor[cyan][c=1] +% \setbox\scratchbox\hbox{\startMPcode\stopMPcode} % first specials are forgotten +% \definecolor[sss][t=.5,a=1,r=1] +% \definespotcolor[oeps1][green][p=.5] +% \definespotcolor[oeps2][green][p=.25] +% \definespotcolor[oeps3][green][p=.25,t=.5,a=1] +% \startMPpage +% fill fullcircle scaled 10cm withcolor \MPcolor{red} ; +% fill fullcircle scaled 8cm withcolor cmyk(1,0,0,0) ; +% fill fullcircle scaled 6cm withcolor cmyk(0,1,0,0) ; +% fill fullcircle scaled 4cm withcolor cmyk(0,0,1,0) ; +% fill fullcircle scaled 2cm withcolor cmyk(0,0,0,1) ; +% currentpicture := currentpicture shifted (-7.5cm,0) ; +% fill fullcircle scaled 10cm withcolor transparent(1,0.75,cmyk(0,0,1,0)) ; +% fill fullcircle scaled 8cm withcolor \MPcolor{sss} ; +% fill fullcircle scaled 6cm withcolor \MPcolor{oeps1} ; +% fill fullcircle scaled 4cm withcolor \MPcolor{oeps2} ; +% currentpicture := currentpicture shifted (-7.5cm,0) ; +% fill fullcircle scaled 10cm withcolor \MPcolor{oeps3} ; +% circular_shade(fullcircle scaled 8cm, 1, red, blue) ; +% circular_shade(fullcircle scaled 6cm, 1, (1,0,0,0), (0,1,0,0)) ; +% circular_shade(fullcircle scaled 4cm, 1, cmyk(.5,.5,1,0), (0,1,0,0)) ; +% \stopMPpage +% \stoptext diff --git a/tex/context/base/mlib-pdf.lua b/tex/context/base/mlib-pdf.lua new file mode 100644 index 000000000..8b4cbdb19 --- /dev/null +++ b/tex/context/base/mlib-pdf.lua @@ -0,0 +1,469 @@ +if not modules then modules = { } end modules ['mlib-pdf'] = { + version = 1.001, + comment = "companion to mlib-ctx.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +local format, join = string.format, table.concat +local sprint = tex.sprint +local abs, sqrt, round = math.abs, math.sqrt, math.round + +metapost = metapost or { } + +function metapost.convert(result, trialrun) + if trialrun then + metapost.parse(result) + else + metapost.flush(result) + end +end + +metapost.n = 0 + +function metapost.comment(message) + if message then + sprint(tex.ctxcatcodes,format("\\MPLIBtoPDF{\\letterpercent\\space mps graphic %s: %s}", metapost.n, message)) + end +end + +function metapost.startfigure(llx,lly,urx,ury,message) + metapost.n = metapost.n + 1 + sprint(tex.ctxcatcodes,format("\\startMPLIBtoPDF{%s}{%s}{%s}{%s}",llx,lly,urx,ury)) + if message then metapost.comment(message) end +end + +function metapost.stopfigure(message) + if message then metapost.comment(message) end + sprint(tex.ctxcatcodes,"\\stopMPLIBtoPDF") +end + +function metapost.flushfigure(pdfliterals) -- table + if #pdfliterals > 0 then + sprint(tex.ctxcatcodes,"\\MPLIBtoPDF{",join(pdfliterals,"\n"),"}") + end +end + +function metapost.textfigure(font,size,text,width,height,depth) + text = text:gsub(".","\\hbox{%1}") -- kerning happens in metapost (i have to check if this is true for mplib) + sprint(tex.ctxcatcodes,format("\\MPLIBtextext{%s}{%s}{%s}{%s}{%s}",font,size,text,0,-number.dimenfactors.bp*depth)) +end + +-- the pen calculations are taken from metapost, first converted by +-- taco from c to lua, and then optimized by hans, so all errors are his + +local function pyth(a,b) + return sqrt(a*a + b*b) -- much faster than sqrt(a^2 + b^2) +end + +local aspect_bound = 10/65536 +local aspect_default = 1/65536 +local bend_tolerance = 131/65536 +local eps = 0.0001 + +local function coord_range_x(h, dz) -- direction x + local zlo, zhi = 0, 0 + for i=1, #h do + local p = h[i] + local z = p.x_coord + if z < zlo then zlo = z elseif z > zhi then zhi = z end + z = p.right_x + if z < zlo then zlo = z elseif z > zhi then zhi = z end + z = p.left_x + if z < zlo then zlo = z elseif z > zhi then zhi = z end + end + return (zhi - zlo <= dz and aspect_bound) or aspect_default +end + +local function coord_range_y(h, dz) -- direction y + local zlo, zhi = 0, 0 + for i=1, #h do + local p = h[i] + local z = p.y_coord + if z < zlo then zlo = z elseif z > zhi then zhi = z end + z = p.right_y + if z < zlo then zlo = z elseif z > zhi then zhi = z end + z = p.left_y + if z < zlo then zlo = z elseif z > zhi then zhi = z end + end + return (zhi - zlo <= dz and aspect_bound) or aspect_default +end + +local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 + +local function pen_characteristics(object) + local p = object.pen[1] + local x_coord, y_coord, left_x, left_y, right_x, right_y = p.x_coord, p.y_coord, p.left_x, p.left_y, p.right_x, p.right_y + local wx, wy, width + if right_x == x_coord and left_y == y_coord then + wx = abs(left_x - x_coord) + wy = abs(right_y - y_coord) + else + wx = pyth(left_x - x_coord, right_x - x_coord) + wy = pyth(left_y - y_coord, right_y - y_coord) + end + if wy/coord_range_x(object.path, wx) >= wx/coord_range_y(object.path, wy) then + width = wy + else + width = wx + end + sx, rx, ry, sy, tx, ty = left_x, left_y, right_x, right_y, x_coord, y_coord + if width ~= 1 then + if width == 0 then + sx, sy = 1, 1 + else + rx, ry, sx, sy = rx/width, ry/width, sx/width, sy/width + end + end + -- sx rx ry sy tx ty -> 1 0 0 1 0 0 is ok, but 0 0 0 0 0 0 not + if true then + if abs(sx) < eps then sx = eps end + if abs(sy) < eps then sy = eps end + else + -- this block looks complicated but it only captures invalid transforms + -- to be checked rx vs sx and so + local det = sx/sy - ry/rx + local aspect = 4*aspect_bound + aspect_default + if abs(det) < aspect then + local s + if det >= 0 then + s, aspect = 1, aspect - det + else + s, aspect = -1, -aspect - det -- - ? + end + local absrx, absry, abssy, abssx = abs(rx), abs(ry), abs(sy), abs(sx) + if abssx + abssy >= absry + absrx then -- was yy + if abssx > abssy then + sy = sy + (aspect + s*abssx) / sx + else + sx = sx + (aspect + s*abssy) / sy + end + else + if absry > absrx then + rx = rx + (aspect + s*absry) / ry + else + ry = ry + (aspect + s*absrx) / rx + end + end + end + end + divider = sx*sy - rx*ry + return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), width +end + +local function concat(px, py) + local dx, dy = px-tx, py-ty + return (sy*dx-ry*dy)/divider,(sx*dy-rx*dx)/divider +end + +local function curved(ith,pth) + local d = pth.left_x - ith.right_x + if abs(ith.right_x-ith.x_coord-d) <= bend_tolerance and abs(pth.x_coord-pth.left_x-d) <= bend_tolerance then + d = pth.left_y - ith.right_y + if abs(ith.right_y-ith.y_coord-d) <= bend_tolerance and abs(pth.y_coord-pth.left_y-d) <= bend_tolerance then + return false + end + end + return true +end + +local function flushnormalpath(path, t, open) + local pth, ith + for i=1,#path do + pth = path[i] + if not ith then + t[#t+1] = format("%f %f m",pth.x_coord,pth.y_coord) + elseif curved(ith,pth) then + t[#t+1] = format("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) + else + t[#t+1] = format("%f %f l",pth.x_coord,pth.y_coord) + end + ith = pth + end + if not open then + local one = path[1] + if curved(pth,one) then + t[#t+1] = format("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) + else + t[#t+1] = format("%f %f l",one.x_coord,one.y_coord) + end + end + return t +end + +local function flushconcatpath(path, t, open) + t[#t+1] = format("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) + local pth, ith + for i=1,#path do + pth = path[i] + if not ith then + t[#t+1] = format("%f %f m",concat(pth.x_coord,pth.y_coord)) + elseif curved(ith,pth) then + local a, b = concat(ith.right_x,ith.right_y) + local c, d = concat(pth.left_x,pth.left_y) + t[#t+1] = format("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord)) + else + t[#t+1] = format("%f %f l",concat(pth.x_coord, pth.y_coord)) + end + ith = pth + end + if not open then + local one = path[1] + if curved(pth,one) then + local a, b = concat(pth.right_x,pth.right_y) + local c, d = concat(one.left_x,one.left_y) + t[#t+1] = format("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord)) + else + t[#t+1] = format("%f %f l",concat(one.x_coord,one.y_coord)) + end + end + return t +end + +metapost.specials = metapost.specials or { } + +-- we have two extension handlers, one for pre and postscripts, and one for colors + +function metapost.flush(result) -- pdf flusher, table en dan concat is sneller, 1 literal + if result then + local figures = result.fig + if figures then + local colorconverter = metapost.colorconverter() -- function ! + local colorhandler = metapost.colorhandler + for f=1, #figures do + local figure = figures[f] + local objects = figure:objects() + local t = { } + local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false + local bbox = figure:boundingbox() + local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4] -- faster than unpack + if urx < llx then + -- invalid + metapost.startfigure(0,0,0,0,"invalid") + metapost.stopfigure() + else + metapost.startfigure(llx,lly,urx,ury,"begin") + t[#t+1] = "q" + if objects then + for o=1,#objects do + local object = objects[o] + local objecttype = object.type + if objecttype == "start_bounds" or objecttype == "stop_bounds" then + -- skip + elseif objecttype == "start_clip" then + t[#t+1] = "q" + flushnormalpath(object.path,t,false) + t[#t+1] = "W n" + elseif objecttype == "stop_clip" then + t[#t+1] = "Q" + miterlimit, linecap, linejoin, dashed = -1, -1, -1, false + elseif objecttype == "special" then + metapost.specials.register(object.prescript) + elseif objecttype == "text" then + t[#t+1] = "q" + local ot = object.transform -- 3,4,5,6,1,2 + t[#t+1] = format("%f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) -- TH: format("%f %f m %f %f %f %f 0 0 cm",unpack(ot)) + metapost.flushfigure(t) + t = { } + metapost.textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) + t[#t+1] = "Q" + else + -- alternatively we can pass on the stack, could be a helper + local currentobject = { -- not needed when no extensions + type = object.type, + miterlimit = object.miterlimit, + linejoin = object.linejoin, + linecap = object.linecap, + color = object.color, + dash = object.dash, + path = object.path, + htap = object.htap, + pen = object.pen, + prescript = object.prescript, + postscript = object.postscript, + } + -- + local before, inbetween, after = nil, nil, nil + -- + local cs, cr = currentobject.color, nil + -- todo document why ... + if cs and colorhandler and round(cs[1]*10000) == 123 then -- test in function + currentobject, cr = colorhandler(cs,currentobject,t,colorconverter) + objecttype = currentobject.type + end + -- + local prescript = currentobject.prescript + if prescript then + -- move test to function + local special = metapost.specials[prescript] + if special then + currentobject, before, inbetween, after = special(currentobject.postscript,currentobject,t) + objecttype = currentobject.type + end + end + -- + cs = currentobject.color + if cs then + t[#t+1], cr = colorconverter(cs,objecttype) + end + -- + if before then object, t = before() end + local ml = currentobject.miterlimit + if ml and ml ~= miterlimit then + miterlimit = ml + t[#t+1] = format("%f M",ml) + end + local lj = currentobject.linejoin + if lj and lj ~= linejoin then + linejoin = lj + t[#t+1] = format("%i j",lj) + end + local lc = currentobject.linecap + if lc and lc ~= linecap then + linecap = lc + t[#t+1] = format("%i J",lc) + end + local dl = currentobject.dash + if dl then + local d = format("[%s] %i d",join(dl.dashes or {}," "),dl.offset) + if d ~= dashed then + dashed = d + t[#t+1] = dashed + end + elseif dashed then + t[#t+1] = "[] 0 d" + dashed = false + end + if inbetween then object, t = inbetween() end + local path = currentobject.path + local transformed, penwidth = false, 1 + local open = path and path[1].left_type and path[#path].right_type -- at this moment only "end_point" + if path then + local pen = currentobject.pen + if pen and #pen==1 then + transformed, penwidth = pen_characteristics(object) -- boolean, value + t[#t+1] = format("%f w",penwidth) -- todo: only if changed + end + if transformed then + t[#t+1] = "q" + flushconcatpath(path,t,open) + else + flushnormalpath(path,t,open) + end + end + local path = currentobject.htap + if path then + flushnormalpath(path,t,open) + end + if objecttype == "fill" then + t[#t+1] = "h f" + elseif objecttype == "outline" then + t[#t+1] = (open and "S") or "h S" + end + if transformed then + t[#t+1] = "Q" + end + if cr and currentobject.color then -- and o < #objects + t[#t+1] = cr + end + if after then object, t = after() end + end + end + end + t[#t+1] = "Q" + metapost.flushfigure(t) + metapost.stopfigure("end") + end + end + end + end +end + +function metapost.parse(result) + if result then + local figures = result.fig + if figures then + for f=1, #figures do + local figure = figures[f] + local objects = figure:objects() + if objects then + for o=1,#objects do + local object = objects[o] + if object.type == "outline" then + local prescript = object.prescript + if prescript then + local special = metapost.specials[prescript] + if special then + special(object.postscript,object) + end + end + end + end + end + end + end + end +end + +function metapost.pdfliterals(result) + -- only simple graphics, tracing + local start = metapost.startfigure + local stop = metapost.stopfigure + local flush = metapost.flushfigure + local t = { } + function metapost.flushfigure(literals) + for i=1, #literals do + t[#t+1] = literals[i] + end + end + function metapost.startfigure() + tex.sprint(tex.ctxcatcodes,"\\startnointerference") + end + function metapost.stopfigure() + tex.sprint(tex.ctxcatcodes,"\\stopnointerference") + end + metapost.flush(result) + metapost.startfigure = start + metapost.stopfigure = stop + metapost.flushfigure = flush + return t +end + +function metapost.totable(result) + local figure = result and result.fig and result.fig[1] + if figure then + local t = { } + local objects = figure:objects() + for _, object in ipairs(objects) do + local tt = { } + for _, field in ipairs(mplib.fields(object)) do + tt[field] = object[field] + end + t[#t+1] = tt + end + local b = figure:boundingbox() + return { + boundingbox = { llx = b[1], lly = b[2], urx = b[3], ury = b[4] }, + objects = t + } + else + return nil + end +end + +function metapost.colorconverter() + return function(c,type) + if type == "fill" then + if #c == 4 then return format("%.3f %.3f %.3f %.3f k", c[1],c[2],c[3],c[4]), "0 g" + elseif #c == 3 then return format("%.3f %.3f %.3f rg", c[1],c[2],c[3]), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if #c == 4 then return format("%.3f %.3f %.3f %.3f K", c[1],c[2],c[3],c[4]), "0 G" + elseif #c == 3 then return format("%.3f %.3f %.3f RG", c[1],c[2],c[3]), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end +end diff --git a/tex/context/base/mlib-pdf.tex b/tex/context/base/mlib-pdf.tex new file mode 100644 index 000000000..b7b8506ad --- /dev/null +++ b/tex/context/base/mlib-pdf.tex @@ -0,0 +1,90 @@ +%D \module +%D [ file=mlib-pdf, +%D version=2008.03.25, +%D title=\METAPOST\ Integrated Graphics, +%D subtitle=Conversion to PDF, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +\registerctxluafile{mlib-pdf}{1.001} + +\let\MPLIBtoPDF\pdfliteral + +\def\MPLIBboundingbox#1#2#3#4% + {\xdef\MPllx{#1}% + \xdef\MPlly{#2}% + \xdef\MPurx{#3}% + \xdef\MPury{#4}% + \xdef\MPwidth {\the\dimexpr#3\onebasepoint-#1\onebasepoint\relax}% + \xdef\MPheight{\the\dimexpr#4\onebasepoint-#2\onebasepoint\relax}} + +\def\startMPLIBtoPDF#1#2#3#4% watch the transparency reset + {\hbox\bgroup + \MPLIBboundingbox{#1}{#2}{#3}{#4}% + \forgetall + \setbox\scratchbox\vbox\bgroup + \noindent % this is really needed in order to force tex into proper cm's + \startMPresources} + +\def\stopMPLIBtoPDF % watch the transparency reset + {%\dohandleMPresettransparency % not needed + \stopMPresources + \egroup + \setbox\scratchbox\hbox\bgroup + \hskip-\MPllx\onebasepoint + \raise-\MPlly\onebasepoint + \box\scratchbox + \egroup + \setbox\scratchbox\vbox to \MPheight\bgroup + \vfill + \hsize\MPwidth + \smashbox\scratchbox + \box\scratchbox + \egroup + \wd\scratchbox\MPwidth + \ht\scratchbox\MPheight + \dopackageMPgraphic\scratchbox + \egroup} + +% \def\MPLIBtextext#1#2#3#4#5% +% {\begingroup +% \def\MPtextdata{#3}% delegate the splitter to lua +% \defconvertedcommand\MPtextdata\MPtextdata % no edef +% \splitstring\MPtextdata\at::::\to\MPtexttag\and\MPtextnumber +% \executeifdefined{handleMPtext\MPtexttag} +% {\setbox\scratchbox\hbox +% {\font\temp=#1\space at #2\onebasepoint +% \let\c\char +% \temp +% \MPfshowcommand{#3}}% +% \setbox\scratchbox\hbox +% {\hskip#4\onebasepoint +% \raise#5\onebasepoint +% \box\scratchbox}% +% \smashbox\scratchbox +% \box\scratchbox}% +% \endgroup} + +\def\MPLIBtextext#1#2#3#4#5% + {\begingroup + \setbox\scratchbox\hbox + {\font\temp=#1\space at #2\onebasepoint + \let\c\char + \temp + \MPfshowcommand{#3}}% + \setbox\scratchbox\hbox + {\hskip#4\onebasepoint + \raise#5\onebasepoint + \box\scratchbox}% + \smashbox\scratchbox + \box\scratchbox + \endgroup} + +\protect \endinput diff --git a/tex/context/base/mlib-pps.lua b/tex/context/base/mlib-pps.lua new file mode 100644 index 000000000..de658d21b --- /dev/null +++ b/tex/context/base/mlib-pps.lua @@ -0,0 +1,820 @@ +if not modules then modules = { } end modules ['mlib-pps'] = { -- prescript, postscripts and specials + version = 1.001, + comment = "companion to mlib-ctx.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- todo + +local format, join = string.format, table.concat +local sprint = tex.sprint +local round = math.round + +local rgbtocmyk = colors.rgbtocmyk +local rgbtogray = colors.rgbtogray +local cmyktorgb = colors.cmyktorgb +local cmyktogray = colors.cmyktogray + +metapost = metapost or { } +metapost.specials = metapost.specials or { } +metapost.specials.data = metapost.specials.data or { } + +local data = metapost.specials.data + +local colordata = { {}, {}, {}, {}, {} } + +--~ (r,g,b) => cmyk : r=123 g= 1 b=hash +--~ => spot : r=123 g= 2 b=hash +--~ => transparent rgb : r=123 g= 3 b=hash +--~ => transparent cmyk : r=123 g= 4 b=hash +--~ => transparent spot : r=123 g= 5 b=hash +--~ => rest : r=123 g=n>10 b=whatever + +function metapost.specials.register(str) -- only colors + local size, content, n, class = str:match("^%%%%MetaPostSpecial: (%d+) (.*) (%d+) (%d+)$") + if class then + local data = { } + for s in content:gmatch("[^ ]+") do + data[#data+1] = s + end + class, n = tonumber(class), tonumber(n) + if class == 3 or class == 4 or class == 5 then -- weird + colordata[class][n] = data + else + colordata[class][tonumber(data[1])] = data + end + end +end + +function metapost.colorhandler(cs, object, result, colorconverter) + local cr = "0 g 0 G" + local what = round(cs[2]*10000) + local data = colordata[what][round(cs[3]*10000)] + if not data then + -- + elseif what == 1 then + result[#result+1], cr = colorconverter({ data[2], data[3], data[4], data[5]}, object.type) + elseif what == 2 then + ctx.registerspotcolor(data[2]) + result[#result+1] = ctx.pdfcolor(colors.model,colors.register('color',nil,'spot',data[2],data[3],data[4],data[5])) + else + if what == 3 then + result[#result+1], cr = colorconverter({ data[3], data[4], data[5]}, object.type) + elseif what == 4 then + result[#result+1], cr = colorconverter({ data[3], data[4], data[5], data[6]}, object.type) + elseif what == 5 then + ctx.registerspotcolor(data[3]) + result[#result+1] = ctx.pdfcolor(colors.model,colors.register('color',nil,'spot',data[3],data[4],data[5],data[6])) + end + object.prescript = "tr" + object.postscript = data[1] .. "," .. data[2] + end + object.color = nil + return object, cr +end + +function metapost.colorspec(cs) + local what = round(cs[2]*10000) + local data = colordata[what][round(cs[3]*10000)] + if not data then + return { 0 } + elseif what == 1 then + return { data[2], data[3], data[4], data[5] } + elseif what == 2 then + ctx.registerspotcolor(data[2]) + return ctx.pdfcolor(colors.model,colors.register('color',nil,'spot',data[2],data[3],data[4],data[5])) + elseif what == 3 then + return { data[3], data[4], data[5] } + elseif what == 4 then + return { data[3], data[4], data[5], data[6] } + elseif what == 5 then + ctx.registerspotcolor(data[3]) + return ctx.pdfcolor(colors.model,colors.register('color',nil,'spot',data[3],data[4],data[5],data[6])) + end +end + +function metapost.specials.tr(specification,object,result) + local a, t = specification:match("^(.+),(.+)$") + local before = a and t and function() + result[#result+1] = format("/Tr%s gs",transparencies.register('mp',a,t)) + return object, result + end + local after = before and function() + result[#result+1] = "/Tr0 gs" + return object, result + end + return object, before, nil, after +end + +--~ -- possible speedup: hash registered colors +--~ +--~ function metapost.specials.sp(specification,object,result) -- todo: color conversion +--~ local s = object.color[1] +--~ object.color = nil +--~ local before = function() +--~ local spec = specification:split(" ") +--~ ctx.registerspotcolor(spec[1]) +--~ result[#result+1] = ctx.pdfcolor(colors.model,colors.register('color',nil,'spot',spec[1],spec[2],spec[3],s)) +--~ return object, result +--~ end +--~ local after = function() +--~ result[#result+1] = "0 g 0 G" +--~ return object, result +--~ end +--~ return object, before, nil, nil +--~ end + +-- Unfortunately we cannot use cmyk colors natively because there is no +-- generic color allocation primitive ... it's just an rgbcolor color.. This +-- means that we cannot pass colors in either cmyk or rgb form. +-- +-- def cmyk(expr c,m,y,k) = +-- 1 withprescript "cc" withpostscript ddddecimal (c,m,y,k) +-- enddef ; +-- +-- This is also an example of a simple plugin. + +--~ function metapost.specials.cc(specification,object,result) +--~ object.color = specification:split(" ") +--~ return object, nil, nil, nil +--~ end +--~ function metapost.specials.cc(specification,object,result) +--~ local c = specification:split(" ") +--~ local o = object.color[1] +--~ c[1],c[2],c[3],c[4] = o*c[1],o*c[2],o*c[3],o*c[4] +--~ return object, nil, nil, nil +--~ end + +-- thanks to taco's reading of the postscript manual: +-- +-- x' = sx * x + ry * y + tx +-- y' = rx * x + sy * y + ty + +function metapost.specials.fg(specification,object,result) + local op = object.path + local first, second, fourth = op[1], op[2], op[4] + local tx, ty = first.x_coord , first.y_coord + local sx, sy = second.x_coord - tx, fourth.y_coord - ty + local rx, ry = second.y_coord - ty, fourth.x_coord - tx + if sx == 0 then sx = 0.00001 end + if sy == 0 then sy = 0.00001 end + local before = specification and function() + metapost.flushfigure(result) + sprint(tex.ctxcatcodes,format("\\MPLIBfigure{%f}{%f}{%f}{%f}{%f}{%f}{%s}",sx,rx,ry,sy,tx,ty,specification)) + return object, { } + end + return { } , before, nil, nil -- replace { } by object for tracing +end + +local nofshades = 0 -- todo: hash resources, start at 1000 in order not to clash with older + +local function normalize(ca,cb) + if #cb == 1 then + if #ca == 4 then + cb[1], cb[2], cb[3], cb[4] = 0, 0, 0, 1-cb[1] + else + cb[1], cb[2], cb[3] = cb[1], cb[1], cb[1] + end + elseif #cb == 3 then + if #ca == 4 then + cb[1], cb[2], cb[3], cb[4] = rgbtocmyk(cb[1],cb[2],cb[3]) + else + cb[1], cb[2], cb[3] = cmyktorgb(cb[1],cb[2],cb[3],cb[4]) + end + end +end + +function metapost.specials.cs(specification,object,result) -- spot colors? + nofshades = nofshades + 1 + metapost.flushfigure(result) + result = { } + local t = specification:split(" ") + -- we need a way to move/scale + local ca = t[4]:split(":") + local cb = t[8]:split(":") + if round(ca[1]*10000) == 123 then ca = metapost.colorspec(ca) end + if round(cb[1]*10000) == 123 then cb = metapost.colorspec(cb) end + if type(ca) == "string" then + -- spot color, not supported, maybe at some point use the fallbacks + sprint(tex.ctxcatcodes,format("\\MPLIBcircularshade{%s}{%s %s}{%.3f}{%.3f}{%s}{%s}{%s %s %s %s %s %s}", + nofshades, + t[1], t[2], 0, 1, 1, "DeviceGray", + t[5], t[6], t[7], t[9], t[10], t[11])) + else + if #ca > #cb then + normalize(ca,cb) + elseif #ca < #cb then + normalize(cb,ca) + end + local model = colors.model + if model == "all" then + model= (#ca == 4 and "cmyk") or (#ca == 3 and "rgb") or "gray" + end + if model == "rgb" then + if #ca == 4 then + ca[1], ca[2], ca[3] = cmyktorgb(ca[1],ca[2],ca[3],ca[4]) + cb[1], cb[2], cb[3] = cmyktorgb(cb[1],cb[2],cb[3],cb[4]) + elseif #ca == 1 then + local a, b = 1-ca[1], 1-cb[1] + ca[1], ca[2], ca[3] = a, a, a + cb[1], cb[2], cb[3] = b, b, b + end + sprint(tex.ctxcatcodes,format("\\MPLIBcircularshade{%s}{%s %s}{%.3f %.3f %.3f}{%.3f %.3f %.3f}{%s}{%s}{%s %s %s %s %s %s}", + nofshades, + t[1], t[2], ca[1], ca[2], ca[3], cb[1], cb[2], cb[3], 1, "DeviceRGB", + t[5], t[6], t[7], t[9], t[10], t[11])) + elseif model == "cmyk" then + if #ca == 3 then + ca[1], ca[2], ca[3], ca[4] = rgbtocmyk(ca[1],ca[2],ca[3]) + cb[1], cb[2], cb[3], ca[4] = rgbtocmyk(cb[1],cb[2],cb[3]) + elseif #ca == 1 then + ca[1], ca[2], ca[3], ca[4] = 0, 0, 0, ca[1] + cb[1], cb[2], cb[3], ca[4] = 0, 0, 0, ca[1] + end + sprint(tex.ctxcatcodes,format("\\MPLIBcircularshade{%s}{%s %s}{%.3f %.3f %.3f %.3f}{%.3f %.3f %.3f %.3f}{%s}{%s}{%s %s %s %s %s %s}", + nofshades, + t[1], t[2], ca[1], ca[2], ca[3], ca[4], cb[1], cb[2], cb[3], cb[4], 1, "DeviceCMYK", + t[5], t[6], t[7], t[9], t[10], t[11])) + else + if #ca == 4 then + ca[1] = cmyktogray(ca[1],ca[2],ca[3],ca[4]) + cb[1] = cmyktogray(cb[1],cb[2],cb[3],cb[4]) + elseif #ca == 3 then + ca[1] = rgbtogray(ca[1],ca[2],ca[3]) + cb[1] = rgbtogray(cb[1],cb[2],cb[3]) + end + sprint(tex.ctxcatcodes,format("\\MPLIBcircularshade{%s}{%s %s}{%.3f}{%.3f}{%s}{%s}{%s %s %s %s %s %s}", + nofshades, + t[1], t[2], ca[1], cb[1], 1, "DeviceGray", + t[5], t[6], t[7], t[9], t[10], t[11])) + end + end + local before = function() + result[#result+1] = "q /Pattern cs" + return object, result + end + local after = function() + result[#result+1] = format("W n /MpSh%s sh Q", nofshades) + return object, result + end + object.color, object.type = nil, nil + return object, before, nil, after +end + +function metapost.specials.ls(specification,object,result) + nofshades = nofshades + 1 + metapost.flushfigure(result) + result = { } + local t = specification:split(" ") + -- we need a way to move/scale + local ca = t[4]:split(":") + local cb = t[7]:split(":") + if round(ca[1]*10000) == 123 then ca = metapost.colorspec(ca) end + if round(cb[1]*10000) == 123 then cb = metapost.colorspec(cb) end + if type(ca) == "string" then + -- spot color, not supported, maybe at some point use the fallbacks + sprint(tex.ctxcatcodes,format("\\MPLIBlinearshade{%s}{%s %s}{%.3f}{%.3f}{%s}{%s}{%s %s %s %s}", + nofshades, + t[1], t[2], 0, 1, 1, "DeviceGray", + t[5], t[6], t[8], t[9])) + else + if #ca > #cb then + normalize(ca,cb) + elseif #ca < #cb then + normalize(cb,ca) + end + local model = colors.model + if model == "all" then + model= (#ca == 4 and "cmyk") or (#ca == 3 and "rgb") or "gray" + end + if model == "rgb" then + if #ca == 4 then + ca[1], ca[2], ca[3] = cmyktorgb(ca[1],ca[2],ca[3],ca[4]) + cb[1], cb[2], cb[3] = cmyktorgb(cb[1],cb[2],cb[3],cb[4]) + elseif #ca == 1 then + local a, b = 1-ca[1], 1-cb[1] + ca[1], ca[2], ca[3] = a, a, a + cb[1], cb[2], cb[3] = b, b, b + end + sprint(tex.ctxcatcodes,format("\\MPLIBlinearshade{%s}{%s %s}{%.3f %.3f %.3f}{%.3f %.3f %.3f}{%s}{%s}{%s %s %s %s}", + nofshades, + t[1], t[2], ca[1], ca[2], ca[3], cb[1], cb[2], cb[3], 1, "DeviceRGB", + t[5], t[6], t[8], t[9])) + elseif model == "cmyk" then + if #ca == 3 then + ca[1], ca[2], ca[3], ca[4] = rgbtocmyk(ca[1],ca[2],ca[3]) + cb[1], cb[2], cb[3], ca[4] = rgbtocmyk(cb[1],cb[2],cb[3]) + elseif #ca == 1 then + ca[1], ca[2], ca[3], ca[4] = 0, 0, 0, ca[1] + cb[1], cb[2], cb[3], ca[4] = 0, 0, 0, ca[1] + end + sprint(tex.ctxcatcodes,format("\\MPLIBlinearshade{%s}{%s %s}{%.3f %.3f %.3f %.3f}{%.3f %.3f %.3f %.3f}{%s}{%s}{%s %s %s %s}", + nofshades, + t[1], t[2], ca[1], ca[2], ca[3], ca[4], cb[1], cb[2], cb[3], cb[4], 1, "DeviceCMYK", + t[5], t[6], t[8], t[9])) + else + if #ca == 4 then + ca[1] = cmyktogray(ca[1],ca[2],ca[3],ca[4]) + cb[1] = cmyktogray(cb[1],cb[2],cb[3],cb[4]) + elseif #ca == 3 then + ca[1] = rgbtogray(ca[1],ca[2],ca[3]) + cb[1] = rgbtogray(cb[1],cb[2],cb[3]) + end + sprint(tex.ctxcatcodes,format("\\MPLIBlinearshade{%s}{%s %s}{%.3f}{%.3f}{%s}{%s}{%s %s %s %s}", + nofshades, + t[1], t[2], ca[1], cb[1], 1, "DeviceGray", + t[5], t[6], t[8], t[9])) + end + end + local before = function() + result[#result+1] = "q /Pattern cs" + return object, result + end + local after = function() + result[#result+1] = format("W n /MpSh%s sh Q", nofshades) + return object, result + end + object.color, object.type = nil, nil + return object, before, nil, after +end + +-- no need for a before here + +local current_format, current_graphic + +--~ metapost.first_box, metapost.last_box = 1000, 1100 + +metapost.textext_current = metapost.first_box + +function metapost.specials.tf(specification,object) +--~ print("setting", metapost.textext_current) + sprint(tex.ctxcatcodes,format("\\MPLIBsettext{%s}{%s}",metapost.textext_current,specification)) + if metapost.textext_current < metapost.last_box then + metapost.textext_current = metapost.textext_current + 1 + end + return { }, nil, nil, nil +end + +function metapost.specials.ts(specification,object,result) + -- print("getting", metapost.textext_current) + local op = object.path + local first, second, fourth = op[1], op[2], op[4] + local tx, ty = first.x_coord , first.y_coord + local sx, sy = second.x_coord - tx, fourth.y_coord - ty + local rx, ry = second.y_coord - ty, fourth.x_coord - tx + if sx == 0 then sx = 0.00001 end + if sy == 0 then sy = 0.00001 end + local before = function() + --~ metapost.flushfigure(result) + --~ sprint(tex.ctxcatcodes,format("\\MPLIBgettext{%f}{%f}{%f}{%f}{%f}{%f}{%s}",sx,rx,ry,sy,tx,ty,metapost.textext_current)) + --~ result = { } + result[#result+1] = format("q %f %f %f %f %f %f cm", sx,rx,ry,sy,tx,ty) + metapost.flushfigure(result) + local b = metapost.textext_current + sprint(tex.ctxcatcodes,format("\\MPLIBgettextscaled{%s}{%s}{%s}",b, metapost.sxsy(tex.wd[b],tex.ht[b],tex.dp[b]))) + result = { "Q" } + if metapost.textext_current < metapost.last_box then + metapost.textext_current = metapost.textext_current + 1 + end + return object, result + end + return { }, before, nil, nil -- replace { } by object for tracing +end + +function metapost.colorconverter() + local model = colors.model + if model == "all" then + return function(c,type) + if type == "fill" then + if #c == 4 then return format("%.3f %.3f %.3f %.3f k", c[1],c[2],c[3],c[4]), "0 g" + elseif #c == 3 then return format("%.3f %.3f %.3f rg", c[1],c[2],c[3]), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if #c == 4 then return format("%.3f %.3f %.3f %.3f K", c[1],c[2],c[3],c[4]), "0 G" + elseif #c == 3 then return format("%.3f %.3f %.3f RG", c[1],c[2],c[3]), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + elseif model == "rgb" then + return function(c,type) + if type == "fill" then + if #c == 4 then return format("%.3f %.3f %.3f rg",cmyktorgb(c[1],c[2],c[3],c[4])), "0 g" + elseif #c == 3 then return format("%.3f %.3f %.3f rg", c[1],c[2],c[3]), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if #c == 4 then return format("%.3f %.3f %.3f RG",cmyktorgb(c[1],c[2],c[3],c[4])), "0 G" + elseif #c == 3 then return format("%.3f %.3f %.3f RG", c[1],c[2],c[3]), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + elseif model == "cmyk" then + return function(c,type) + if type == "fill" then + if #c == 4 then return format("%.3f %.3f %.3f %.3f k", c[1],c[2],c[3],c[4]), "0 g" + elseif #c == 3 then return format("%.3f %.3f %.3f %.3f k",rgbtocmyk(c[1],c[2],c[3])), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if #c == 4 then return format("%.3f %.3f %.3f %.3f K", c[1],c[2],c[3],c[4]), "0 G" + elseif #c == 3 then return format("%.3f %.3f %.3f %.3f K",rgbtocmyk(c[1],c[2],c[3])), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + else + return function(c,type) + if type == "fill" then + if #c == 4 then return format("%.3f g",cmyktogray(c[1],c[2],c[3],c[4])), "0 g" + elseif #c == 3 then return format("%.3f g",rgbtogray (c[1],c[2],c[3])), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if #c == 4 then return format("%.3f G",cmyktogray(c[1],c[2],c[3],colors[4])), "0 G" + elseif #c == 3 then return format("%.3f G",rgbtogray (c[1],c[2],c[3])), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + end +end + +--~ local cmyk_fill = "%.3f %.3f %.3f %.3f k" +--~ local rgb_fill = "%.3f %.3f %.3f rg" +--~ local gray_fill = "%.3f g" +--~ local reset_fill = "0 g" + +--~ local cmyk_stroke = "%.3f %.3f %.3f %.3f K" +--~ local rgb_stroke = "%.3f %.3f %.3f RG" +--~ local gray_stroke = "%.3f G" +--~ local reset_stroke = "0 G" + +metapost.reducetogray = true + +function metapost.colorconverter() + local model = colors.model + local reduce = metapost.reducetogray + if model == "all" then + return function(c,type) + local n = #c + if reduce and n == 3 then if c[1] == c[2] and c[1] == c[3] then n = 1 end end + if type == "fill" then + if n == 4 then return format("%.3f %.3f %.3f %.3f k", c[1],c[2],c[3],c[4]), "0 g" + elseif n == 3 then return format("%.3f %.3f %.3f rg", c[1],c[2],c[3]), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if n == 4 then return format("%.3f %.3f %.3f %.3f K", c[1],c[2],c[3],c[4]), "0 G" + elseif n == 3 then return format("%.3f %.3f %.3f RG", c[1],c[2],c[3]), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + elseif model == "rgb" then + return function(c,type) + local n = #c + if reduce and n == 3 then if c[1] == c[2] and c[1] == c[3] then n = 1 end end + if type == "fill" then + if n == 4 then return format("%.3f %.3f %.3f rg",cmyktorgb(c[1],c[2],c[3],c[4])), "0 g" + elseif n == 3 then return format("%.3f %.3f %.3f rg", c[1],c[2],c[3]), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if n == 4 then return format("%.3f %.3f %.3f RG",cmyktorgb(c[1],c[2],c[3],c[4])), "0 G" + elseif n == 3 then return format("%.3f %.3f %.3f RG", c[1],c[2],c[3]), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + elseif model == "cmyk" then + return function(c,type) + local n = #c + if reduce and n == 3 then if c[1] == c[2] and c[1] == c[3] then n = 1 end end + if type == "fill" then + if n == 4 then return format("%.3f %.3f %.3f %.3f k", c[1],c[2],c[3],c[4]), "0 g" + elseif n == 3 then return format("%.3f %.3f %.3f %.3f k",rgbtocmyk(c[1],c[2],c[3])), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if n == 4 then return format("%.3f %.3f %.3f %.3f K", c[1],c[2],c[3],c[4]), "0 G" + elseif n == 3 then return format("%.3f %.3f %.3f %.3f K",rgbtocmyk(c[1],c[2],c[3])), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + else + return function(c,type) + local n = #c + if reduce and n == 3 then if c[1] == c[2] and c[1] == c[3] then n = 1 end end + if type == "fill" then + if n == 4 then return format("%.3f g",cmyktogray(c[1],c[2],c[3],c[4])), "0 g" + elseif n == 3 then return format("%.3f g",rgbtogray (c[1],c[2],c[3])), "0 g" + else return format("%.3f g", c[1]), "0 g" + end + else + if n == 4 then return format("%.3f G",cmyktogray(c[1],c[2],c[3],c[4])), "0 G" + elseif n == 3 then return format("%.3f G",rgbtogray (c[1],c[2],c[3])), "0 G" + else return format("%.3f G", c[1]), "0 G" + end + end + end + end +end + +-- textext stuff + +--~ do +--~ +--~ local P, V, Cs = lpeg.P, lpeg.V, lpeg.Cs +--~ +--~ local btex = P("btex") +--~ local etex = P("etex") +--~ local vtex = P("verbatimtex") +--~ local ttex = P("textext") +--~ local gtex = P("graphictext") +--~ local spacing = P(" \n\r\t\v")^0 +--~ local left = P("(") +--~ local right = P(")") +--~ local dquote = P('"') +--~ local ddquote = P('\\"') / "\\\" & ditto & \"" +--~ +--~ local found, n = false, 0 +--~ +--~ local function textext_first(s) +--~ local str = format('_tex_text_f_("%s")',s) +--~ found, n = true, n + 1 +--~ return str +--~ end +--~ local function textext_second() +--~ local str = format('_tex_text_s_(%s,%spt,%spt,%spt)',n,tex.wd[n]/65536,tex.ht[n]/65536,tex.dp[n]/65536) +--~ found, n = true, n + 1 +--~ return str +--~ end +--~ local function graphictext_first(s) +--~ local str = format('_graphic_text_f_("%s")',s) +--~ found = true +--~ return str +--~ end +--~ local function graphictext_second() +--~ local str = format('_graphic_text_s_') +--~ found = true +--~ return str +--~ end +--~ +--~ -- the next lpegs can be more efficient (in code only) by not using a grammar +--~ +--~ local first = P { +--~ [1] = Cs(((V(2) + V(3))/textext_first + V(4)/graphictext_first + 1)^0), +--~ [2] = (btex + vtex) * spacing * Cs((1-etex)^0) * spacing * etex, +--~ [3] = ttex * spacing * left * spacing * V(5) * spacing * right, +--~ [4] = gtex * spacing * V(5), +--~ [5] = dquote * Cs((ddquote + (1-dquote))^0) * dquote, +--~ } +--~ +--~ local second = P { +--~ [1] = Cs(((V(2) + V(3))/textext_second + V(4)/graphictext_second + 1)^0), +--~ [2] = (btex + vtex) * spacing * Cs((1-etex)^0) * spacing * etex, +--~ [3] = ttex * spacing * left * spacing * V(5) * spacing * right, +--~ [4] = gtex * spacing * V(5), +--~ [5] = dquote * Cs((ddquote + (1-dquote))^0) * dquote, +--~ } +--~ +--~ function metapost.texttext_first(str) +--~ found, n = false, metapost.first_box -- or 0 no fallback, better an error +--~ return first:match(str), found +--~ end +--~ function metapost.texttext_second(str) +--~ found, n = false, metapost.first_box -- or 0 no fallback, better an error +--~ return second:match(str), found +--~ end +--~ +--~ end +--~ +--~ local factor = 65536*(7200/7227) +--~ +--~ function metapost.edefsxsy(wd,ht,dp) -- helper for text +--~ commands.edef("sx",(wd ~= 0 and 1/( wd /(factor))) or 0) +--~ commands.edef("sy",(wd ~= 0 and 1/((ht+dp)/(factor))) or 0) +--~ end +--~ +--~ function metapost.sxsy(wd,ht,dp) -- helper for text +--~ return (wd ~= 0 and 1/(wd/(factor))) or 0, (wd ~= 0 and 1/((ht+dp)/(factor))) or 0 +--~ end +--~ +--~ metapost.intermediate = metapost.intermediate or {} +--~ metapost.intermediate.actions = metapost.intermediate.actions or {} +--~ metapost.intermediate.needed = false +--~ +--~ function metapost.graphic_base_pass(mpsformat,str,preamble) +--~ local prepared, done = metapost.texttext_first(str) +--~ metapost.textext_current = metapost.first_box +--~ metapost.intermediate.needed = false +--~ if done then +--~ current_format, current_graphic = mpsformat, str +--~ metapost.process(mpsformat, { +--~ preamble or "", +--~ "beginfig(1); ", +--~ prepared, +--~ "endfig ;" +--~ }, true ) -- true means: trialrun +--~ if metapost.intermediate.needed then +--~ for _, action in pairs(metapost.intermediate.actions) do +--~ action() +--~ end +--~ end +--~ sprint(tex.ctxcatcodes,"\\ctxlua{metapost.graphic_extra_pass()}") +--~ else +--~ metapost.process(mpsformat, { +--~ preamble or "", +--~ "beginfig(1); ", +--~ str, +--~ "endfig ;" +--~ } ) +--~ end +--~ end +--~ +--~ function metapost.graphic_extra_pass() +--~ local prepared, done = metapost.texttext_second(current_graphic) +--~ metapost.textext_current = metapost.first_box +--~ metapost.process(current_format, { +--~ "beginfig(0); ", +--~ prepared, +--~ "endfig ;" +--~ }) +--~ end + +--~ At the cost of passing data about the texts to MP, the following +--~ solution also handles textexts that are more complex and part of +--~ formats. + +do + + local P, S, V, Cs = lpeg.P, lpeg.S, lpeg.V, lpeg.Cs + + local btex = P("btex") + local etex = P(" etex") + local vtex = P("verbatimtex") + local ttex = P("textext") + local gtex = P("graphictext") + local spacing = S(" \n\r\t\v")^0 + local dquote = P('"') + + local found = false + + local function convert(str) + found = true + return "textext(\"" .. str .. "\")" + end + local function ditto(str) + return "\" & ditto & \"" + end + local function register() + found = true + end + + local parser = P { + [1] = Cs((V(2)/register + V(3)/convert + 1)^0), + [2] = ttex + gtex, + [3] = (btex + vtex) * spacing * Cs((dquote/ditto + (1 - etex))^0) * etex, + } + + -- currently a a one-liner produces less code + + local parser = Cs(((ttex + gtex)/register + ((btex + vtex) * spacing * Cs((dquote/ditto + (1 - etex))^0) * etex)/convert + 1)^0) + + function metapost.check_texts(str) + found = false + return parser:match(str), found + end + +end + +local factor = 65536*(7200/7227) + +function metapost.edefsxsy(wd,ht,dp) -- helper for text + commands.edef("sx",(wd ~= 0 and 1/( wd /(factor))) or 0) + commands.edef("sy",(wd ~= 0 and 1/((ht+dp)/(factor))) or 0) +end + +function metapost.sxsy(wd,ht,dp) -- helper for text + return (wd ~= 0 and 1/(wd/(factor))) or 0, (wd ~= 0 and 1/((ht+dp)/(factor))) or 0 +end + +function metapost.text_texts_data() + local t, n = { }, 0 + for i = metapost.first_box, metapost.last_box do + n = n + 1 + if tex.box[i] then + t[#t+1] = format("_tt_w_[%i]:=%f;_tt_h_[%i]:=%f;_tt_d_[%i]:=%f;", n,tex.wd[i]/factor, n,tex.ht[i]/factor, n,tex.dp[i]/factor) + else + break + end + end + return t +end + +metapost.intermediate = metapost.intermediate or {} +metapost.intermediate.actions = metapost.intermediate.actions or {} +metapost.intermediate.needed = false + +function metapost.graphic_base_pass(mpsformat,str,preamble) + local prepared, done = metapost.check_texts(str) + metapost.textext_current = metapost.first_box + metapost.intermediate.needed = false + if done then + current_format, current_graphic = mpsformat, prepared + metapost.process(mpsformat, { + preamble or "", + "beginfig(1); ", + "_trial_run_ := true ;", + prepared, + "endfig ;" + }, true ) -- true means: trialrun + if metapost.intermediate.needed then + for _, action in pairs(metapost.intermediate.actions) do + action() + end + end + sprint(tex.ctxcatcodes,"\\ctxlua{metapost.graphic_extra_pass()}") + else + metapost.process(mpsformat, { + preamble or "", + "beginfig(1); ", + "_trial_run_ := false ;", + str, + "endfig ;" + } ) + end +end + +function metapost.graphic_extra_pass() + metapost.textext_current = metapost.first_box + metapost.process(current_format, { + "beginfig(0); ", + "_trial_run_ := false ;", + join(metapost.text_texts_data()," ;\n"), + current_graphic, + "endfig ;" + }) +end + +function metapost.getclippath(data) + local mpx = metapost.format("metafun") + if mpx and data then + input.starttiming(metapost) + input.starttiming(metapost.exectime) + local result = mpx:execute(format("beginfig(1);%s;endfig;",data)) + input.stoptiming(metapost.exectime) + if result.status > 0 then + print("error", result.status, result.error or result.term or result.log) + result = "" + else + result = metapost.filterclippath(result) + end + input.stoptiming(metapost) + sprint(result) + end +end + +do -- not that beautiful but ok, we could save a md5 hash in the tui file ! + + local graphics = { } + local start = [[\starttext]] + local preamble = [[\def\MPLIBgraphictext#1{\startTEXpage[scale=10000]#1\stopTEXpage}]] + local stop = [[\stoptext]] + + function metapost.specials.gt(specification,object) -- number, so that we can reorder + graphics[#graphics+1] = format("\\MPLIBgraphictext{%s}",specification) + metapost.intermediate.needed = true + return { }, nil, nil, nil + end + + function metapost.intermediate.actions.makempy() + if #graphics > 0 then + local mpofile = tex.jobname .. "-mp" + local mpyfile = file.replacesuffix(mpofile,"mpy") + local pdffile = file.replacesuffix(mpofile,"pdf") + local texfile = file.replacesuffix(mpofile,"tex") + io.savedata(texfile, { start, preamble, join(graphics,"\n"), stop }, "\n") + os.execute(format("context --once %s", texfile)) + if io.exists(pdffile) then + os.execute(format("pstoedit -ssp -dt -f mpost %s %s", pdffile, mpyfile)) + local result = { } + if io.exists(mpyfile) then + local data = io.loaddata(mpyfile) + for figure in data:gmatch("beginfig(.-)endfig") do + result[#result+1] = format("begingraphictextfig%sendgraphictextfig ;\n", figure) + end + io.savedata(mpyfile,join(result,"")) + end + end + graphics = { } + end + end + +end diff --git a/tex/context/base/mlib-pps.tex b/tex/context/base/mlib-pps.tex new file mode 100644 index 000000000..df325c316 --- /dev/null +++ b/tex/context/base/mlib-pps.tex @@ -0,0 +1,61 @@ +%D \module +%D [ file=mlib-pps, +%D version=2008.03.25, +%D title=\METAPOST\ Integrated Graphics, +%D subtitle=Basics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +\registerctxluafile{mlib-pps}{1.001} + +\def\MPLIBcircularshade#1#2#3#4#5#6#7% nr domain color-a color-b ? colorspace oordinates + {\immediate\pdfobj{<</FunctionType 2 /Domain [#2] /C0 [#3] /C1 [#4] /N #5>>}% + \immediate\pdfobj{<</ShadingType 3 /ColorSpace /#6 /Function \the\pdflastobj\space 0 R /Coords [#7] /Extend [true true]>>}% + \appendtoPDFdocumentshades{/MpSh#1 \the\pdflastobj\space0 R }} + +\def\MPLIBlinearshade#1#2#3#4#5#6#7% nr domain color-a color-b ? colorspace oordinates + {\immediate\pdfobj{<</FunctionType 2 /Domain [#2] /C0 [#3] /C1 [#4] /N #5>>}% + \immediate\pdfobj{<</ShadingType 2 /ColorSpace /#6 /Function \the\pdflastobj\space 0 R /Coords [#7] /Extend [true true]>>}% + \appendtoPDFdocumentshades{/MpSh#1 \the\pdflastobj\space0 R }} + +\def\MPLIBfigure#1#2#3#4#5#6#7% todo: move Q q to lua + {\setbox\scratchbox\hbox{\externalfigure[#7]}% + \ctxlua{metapost.edefsxsy(\number\wd\scratchbox,\number\ht\scratchbox,0)}% + \pdfliteral direct{q #1 #2 #3 #4 #5 #6 cm}% no direct + \vbox to \zeropoint{\vss\hbox to \zeropoint{\scale[sx=\sx,sy=\sy]{\box\scratchbox}\hss}}% + \pdfliteral direct{Q}} + +\def\MPLIBsettext#1% #2% + {\global\setbox#1\hbox}% {#2}} + +% \def\MPLIBgettext#1#2#3#4#5#6#7% we can also use this for the figure and pass sx/sy +% {\ctxlua{metapost.edefsxsy(\number\wd#7,\number\ht#7,\number\dp#7)}% +% \pdfliteral{q #1 #2 #3 #4 #5 #6 cm}% +% \vbox to \zeropoint{\vss\hbox to \zeropoint{\scale[sx=\sx,sy=\sy]{\raise\dp#7\box#7}\hss}}% +% \pdfliteral{Q}} + +\def\MPLIBgettextscaled#1#2#3% + {\vbox to \zeropoint{\vss\hbox to \zeropoint{\scale[sx=#2,sy=#3]{\raise\dp#1\box#1}\hss}}} + +\def\MPLIBallocate#1% + {\newbox\MPLIBfirst + \dorecurse{\numexpr#1-1\relax}{\newbox\MPLIBlast}% + \MPLIBregister} + +\def\MPLIBregister + {\ctxlua{metapost.first_box, metapost.last_box = \number\MPLIBfirst, \number\MPLIBlast}} + +\appendtoks \MPLIBallocate{500}\to \everydump +\appendtoks \MPLIBregister \to \everyjob + +\def\MPLIBgraphictext#1% + {\startTEXpage[scale=10000]#1\stopTEXpage} + +\protect \endinput diff --git a/tex/context/base/mlib-run.lua b/tex/context/base/mlib-run.lua new file mode 100644 index 000000000..aac7e030e --- /dev/null +++ b/tex/context/base/mlib-run.lua @@ -0,0 +1,219 @@ +if not modules then modules = { } end modules ['mlib-run'] = { + version = 1.001, + comment = "companion to mlib-ctx.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +--~ cmyk -> done, native +--~ spot -> done, but needs reworking (simpler) +--~ multitone -> +--~ shade -> partly done, todo: cm +--~ figure -> done +--~ hyperlink -> low priority, easy + +-- new * run +-- or +-- new * execute^1 * finish + +-- a*[b,c] == b + a * (c-b) + +--[[ldx-- +<p>The directional helpers and pen analysis are more or less translated from the +<l n='c'/> code. It really helps that Taco know that source so well. Taco and I spent +quite some time on speeding up the <l n='lua'/> and <l n='c'/> code. There is not +much to gain, especially if on ekeeps in mind that when integrated in <l n='tex'/> +only a part of the time is spent in <l n='metapost'/>. Of course an integrated +approach is way faster than an external <l n='metapost'/> and processing time +nears zero.</p> +--ldx]]-- + +local format = string.format + +metapost = metapost or { } + +local function finder(name, mode, ftype) + if mode=="w" then + return name + elseif input.aux.qualified_path(name) then + return name + else + return input.find_file((texmf and texmf.instance) or instance,name,ftype) + end +end + +metapost.finder = finder + +--~ statistics = { +--~ ["hash_size"]=1774, +--~ ["main_memory"]=50237, +--~ ["max_in_open"]=5, +--~ ["param_size"]=4, +--~ } + +metapost.parameters = { + hash_size = 100000, + main_memory = 2000000, + max_in_open = 50, + param_size = 100000, +} + +metapost.exectime = metapost.exectime or { } -- hack + +function metapost.make(name, target, version) + input.starttiming(mplib) + target = file.replacesuffix(target or name, "mem") + local mpx = mplib.new ( table.merged ( + metapost.parameters, + { + ini_version = true, + find_file = finder, + job_name = file.stripsuffix(target), + } + ) ) + if mpx then + input.starttiming(metapost.exectime) + local result = mpx:execute(format('\\ ; boolean mplib ; mplib := true ; string mp_parent_version ; mp_parent_version := "%s" ; show mp_parent_version ; input %s ;', version or "unknown", name)) + input.stoptiming(metapost.exectime) + if mpx then + mpx:finish() + end + end + input.stoptiming(mplib) + return mpx -- mpx = nil will free memory +end + +function metapost.load(name) + input.starttiming(mplib) + local mpx = mplib.new ( table.merged ( + metapost.parameters, + { + mem_name = file.replacesuffix(name,"mem"), + find_file = finder, + } + ) ) + if mpx then + input.starttiming(metapost.exectime) + mpx:execute("\\") + input.stoptiming(metapost.exectime) + end + input.stoptiming(mplib) + return mpx +end + +function metapost.unload(mpx) + input.starttiming(mplib) + if mpx then + mpx:finish() + end + input.stoptiming(mplib) +end + +function metapost.checkformat(mpsinput, mpsformat) + mpsinput = file.addsuffix(mpsinput or "metafun", "mp") + mpsformat = file.stripsuffix(file.basename(mpsformat or texconfig.formatname or tex.formatname or mpsinput)) + local mpsbase = file.stripsuffix(file.basename(mpsinput)) + if mpsbase ~= mpsformat then + mpsformat = mpsformat .. "-" .. mpsbase + end + mpsformat = file.addsuffix(mpsformat, "mem") + local pth = file.dirname(texconfig.formatname or "") + if pth ~= "" then + mpsformat = file.join(pth,mpsformat) + end + local the_version = environment.version or "unset version" + if io.exists(mpsformat) then + commands.writestatus("mplib", format("loading format: %s, name: %s", mpsinput, mpsformat)) + local mpx = metapost.load(mpsformat) + if mpx then + local result = mpx:execute(format("show mp_parent_version ;")) + local version = result.log:match(">> *(.-)[\n\r]") or "unknown" + version = version:gsub("[\'\"]","") + if version ~= the_version then + commands.writestatus("mplib", format("version mismatch: %s <> %s", version or "unknown", the_version)) + else + return mpx + end + end + end + commands.writestatus("mplib", format("making format: %s, name: %s", mpsinput, mpsformat)) + metapost.make(mpsinput,mpsformat,the_version) -- somehow return ... fails here + if io.exists(mpsformat) then + commands.writestatus("mplib", format("loading format: %s, name: %s", mpsinput, mpsformat)) + return metapost.load(mpsformat) + else + commands.writestatus("mplib", format("problems with format: %s, name: %s", mpsinput, mpsformat)) + end +end + +--~ if environment.initex then +--~ metapost.unload(metapost.checkformat("metafun")) +--~ end + +local mpxformats = {} + +function metapost.format(name) + local mpx = mpxformats[name] + if not mpx then + mpx = metapost.checkformat(name) + mpxformats[name] = mpx + end + return mpx +end + +function metapost.process(mpx, data, trialrun, showlog) + local result + if type(mpx) == "string" then + mpx = metapost.format(mpx) -- goody + end + if mpx and data then + input.starttiming(metapost) + if type(data) == "table" then + for i=1,#data do + local d = data[i] + if d then + input.starttiming(metapost.exectime) + result = mpx:execute(d) +--~ print(">>>",d) + input.stoptiming(metapost.exectime) + if not result then + metapost.report("error", "no result object returned") + elseif result.status > 0 then + metapost.report("error",result.error or result.term or result.log or "unknown") + elseif showlog then + metapost.report("info",result.term or "unknown") + elseif result.fig then + metapost.convert(result, trialrun) + end + else + metapost.report("error", "invalid graphic component " .. i) + end + end + else + input.starttiming(metapost.exectime) + result = mpx:execute(data) + input.stoptiming(metapost.exectime) +--~ print(">>>",data) + if not result then + metapost.report("error", "no result object returned") + elseif result.status > 0 then + metapost.report("error",result.error or result.term or result.log or "unknown") + elseif showlog then + metapost.report("info",result.term or "unknown") + elseif result.fig then + metapost.convert(result, trialrun) + end + end + input.stoptiming(metapost) + end + return result +end + +function metapost.convert(result, trialrun) + metapost.report('Warning','no converter set') +end + +function metapost.report(...) + logs.report(...) +end diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua index 195c604d2..76cbe3b76 100644 --- a/tex/context/base/node-ini.lua +++ b/tex/context/base/node-ini.lua @@ -321,6 +321,7 @@ end -- node-gly.lua if not fonts then fonts = { } end +if not fonts.otf then fonts.otf = { } end if not fonts.tfm then fonts.tfm = { } end if not fonts.tfm.id then fonts.tfm.id = { } end @@ -761,17 +762,17 @@ do local v = root[k] local t = type(v) if t == "number" then -if v == 0 then - -- skip -else - handle(("%s %s=%s,"):format(depth,key(k),v)) -end + if v == 0 then + -- skip + else + handle(("%s %s=%s,"):format(depth,key(k),v)) + end elseif t == "string" then -if v == "" then - -- skip -else - handle(("%s %s=%q,"):format(depth,key(k),v)) -end + if v == "" then + -- skip + else + handle(("%s %s=%q,"):format(depth,key(k),v)) + end elseif v then -- userdata or table serialize(v,k,handle,depth,m+1) end diff --git a/tex/context/base/page-num.tex b/tex/context/base/page-num.tex index 374cf89ef..404974f82 100644 --- a/tex/context/base/page-num.tex +++ b/tex/context/base/page-num.tex @@ -117,7 +117,7 @@ \global\resettingsubpagenumberfalse \fi \ifsubpaging - \edef\oldsubpage{\the\subpageno}% + \xdef\oldsubpage{\the\subpageno}% \incrementnumber[\s!subpage]% \global\subpageno\rawnumber[\s!subpage]\relax \ifnum\subpageno=\plusone diff --git a/tex/context/base/s-abr-01.tex b/tex/context/base/s-abr-01.tex index 6d984ca31..f7665177d 100644 --- a/tex/context/base/s-abr-01.tex +++ b/tex/context/base/s-abr-01.tex @@ -28,6 +28,7 @@ %logo [FGBBS] {fgbbs} \logo [ACROBAT] {Acro\-bat} \logo [AFM] {afm} +\logo [API] {api} \logo [ALEPH] {Aleph} % {\mathematics{\aleph}} \logo [ALGOL] {ALGOL} \logo [AMS] {ams} @@ -45,6 +46,7 @@ \logo [CCODE] {c} \logo [CALCMATH] {CalcMath} \logo [CD] {cd} +\logo [CPU] {cpu} \logo [CDROM] {cdrom} \logo [CID] {cid} \logo [CJK] {cjk} diff --git a/tex/context/base/s-map-10.tex b/tex/context/base/s-map-10.tex index ec7ef6128..362aa2700 100644 --- a/tex/context/base/s-map-10.tex +++ b/tex/context/base/s-map-10.tex @@ -385,9 +385,13 @@ \advance \rightskip 0pt plus 2em \MapsSubTitle\par} \fi - \blank[30pt] + \doifmodeelse{newstyle} + {\blank[22pt]} + {\blank[30pt]} \egroup \switchtobodyfont[rm,10pt] + \doifmode{newstyle} + {\noindent\MapsAuthor\blank[line]} \setupalign[width] \ifMapsInColumns \startcolumns\hyphenpenalty1000 diff --git a/tex/context/base/s-sys-01.tex b/tex/context/base/s-sys-01.tex index f695bd618..b982be649 100644 --- a/tex/context/base/s-sys-01.tex +++ b/tex/context/base/s-sys-01.tex @@ -1,5 +1,18 @@ % engine=luatex +%D \module +%D [ file=s-sys-01, +%D version=2008.03.32, +%D title=\CONTEXT\ Style File, +%D subtitle=Generate List of Math Symbol, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + \startluacode function showmath() local slots = mathematics.slots.traditional diff --git a/tex/context/base/spec-dpx.tex b/tex/context/base/spec-dpx.tex index d8ec1e518..6c4d8e447 100644 --- a/tex/context/base/spec-dpx.tex +++ b/tex/context/base/spec-dpx.tex @@ -219,7 +219,7 @@ \def\dopresetPDFtransparency#1#2% {\global\advance\PDFcurrenttransparency \plusone - \doPDFreserveDPXobject{TR:\the\PDFcurrenttransparency}{<< >>}% + \doPDFreserveDPXobject{TR:\the\PDFcurrenttransparency}{<< >>}% hack \special{pdf:\doPDFcheckedDPXobject{TR:\the\PDFcurrenttransparency}\PDFtransparancydictionary{#1}{#2}{}}% \edef\PDFtransparencyidentifier{/Tr\the\PDFcurrenttransparency}% \edef\PDFtransparencyreference {@TR:\the\PDFcurrenttransparency}% @@ -230,7 +230,7 @@ \PDFtransparencyreference\space}} \def\initializePDFtransparency - {\doPDFreserveDPXobject{TR:0}{<< >>}% + {\doPDFreserveDPXobject{TR:0}{<< >>}% hack \special{pdf:\doPDFcheckedDPXobject{TR:0}\PDFtransparancydictionary{1}{1}{/AIS false}}% \xdef\PDFtransparencyresetidentifier{/Tr0}% \xdef\PDFtransparencyresetreference{@TR:0}% @@ -393,10 +393,10 @@ \global\let\currentPDFresources\empty \fi \special{pdf:exobj}}% - \finalizeobjectbox\nextbox - \smashbox\nextbox - \flushatshipout{\box\nextbox}% - \egroup}% + \finalizeobjectbox\nextbox + \smashbox\nextbox + \flushatshipout{\box\nextbox}% + \egroup}% \hbox\bgroup} \def\doDVIPDFMXstopobject @@ -623,9 +623,24 @@ \def\doPDFcheckedDPXobject#1{\ifundefined{r:pdx:d:#1}object\else put\fi\space @#1\space} -% todo when etex is fixed, \everyPDFpresets, leeg voor pdftex, nodig voor dvipdfmx +% new, experimental, can save a run -\doPDFreserveDPXobjectfirst{FDF::docuextgstates}{<< >>} +\def\doreservePDFobject#1#2% + {\dosetobjectreference{#1}{#2}{@#1::#2}} + +\def\doPDFreserveddictionaryobject#1#2#3% + {\flushatshipout{\special{pdf:object @#1::#2 << #3 >>}}} + +\def\doPDFreservedarrayobject#1#2#3% + {\flushatshipout{\special{pdf:object @#1::#2 [ #3 ]}}} + +% maybe this is not needed + +\doreservePDFobject{FDF}{docuextgstates} +\doreservePDFobject{FDF}{colorspaces} +\doreservePDFobject{FDF}{docushades} + +% so this is to be checked \def\doPDFdictionaryobject#1#2#3% {\flushatshipout diff --git a/tex/context/base/spec-fdf.tex b/tex/context/base/spec-fdf.tex index f55cff7a7..0e7c62ec4 100644 --- a/tex/context/base/spec-fdf.tex +++ b/tex/context/base/spec-fdf.tex @@ -177,7 +177,8 @@ \def\checkPDFextgstates {\ifx\docuPDFextgstates\empty \else \ifnum\realpageno=\lastpage\relax - \doPDFdictionaryobject{FDF}{docuextgstates}{\docuPDFextgstates}% + %\doreservePDFobject{FDF}{docuextgstates}% + \doPDFreserveddictionaryobject{FDF}{docuextgstates}{\docuPDFextgstates}% \fi \doPDFgetobjectreference{FDF}{docuextgstates}\PDFobjectreference \doPDFpageresource{/ExtGState \PDFobjectreference}% @@ -219,7 +220,8 @@ \def\checkPDFcolorspaces {\ifx\docuPDFcolorspaces\empty \else \ifnum\realpageno=\lastpage\relax - \doPDFdictionaryobject{FDF}{colorspaces}{\docuPDFcolorspaces}% + %\doreservePDFobject{FDF}{colorspaces}% + \doPDFreserveddictionaryobject{FDF}{colorspaces}{\docuPDFcolorspaces}% \fi \doPDFgetobjectreference{FDF}{colorspaces}\PDFobjectreference \doPDFpageresource{/ColorSpace \PDFobjectreference}% @@ -239,7 +241,8 @@ \def\checkPDFshades {\ifx\docuPDFshades\empty \else \ifnum\realpageno=\lastpage\relax - \doPDFdictionaryobject{FDF}{docushades}{\docuPDFshades}% + %\doreservePDFobject{FDF}{docushades}% + \doPDFreserveddictionaryobject{FDF}{docushades}{\docuPDFshades}% \fi \doPDFgetobjectreference{FDF}{docushades}\PDFobjectreference \doPDFpageresource{/Shading \PDFobjectreference}% diff --git a/tex/context/base/spec-tpd.tex b/tex/context/base/spec-tpd.tex index d2a1d73a7..65fbcb06a 100644 --- a/tex/context/base/spec-tpd.tex +++ b/tex/context/base/spec-tpd.tex @@ -1256,6 +1256,10 @@ {\doPDFgetobjectnumber{#1}{#2}\PDFobjectnumber \immediate\pdfobj useobjnum \PDFobjectnumber {[ #3 ]}} +\doreservePDFobject{FDF}{docuextgstates} +\doreservePDFobject{FDF}{colorspaces} +\doreservePDFobject{FDF}{docushades} + %D \macros %D {defaultobjectreference,doPDFgetobjectreference} %D diff --git a/tex/context/base/supp-box.tex b/tex/context/base/supp-box.tex index 09c390309..1ba55b21e 100644 --- a/tex/context/base/supp-box.tex +++ b/tex/context/base/supp-box.tex @@ -1565,8 +1565,9 @@ \def\processisolatedwords##1##2{##2{##1}}% we split only once \fi \global\let\localbetweenisolatedwords\betweenisolatedwords - \setbox0\normalhbox % we default to spaces, but from inside out - {\ignorespaces#1% \localbetweenisolatedwords can be overruled + \setbox0\normalhbox % we default to spaces, but from inside out + {\normallanguage\minusone % needed for mkiv + \ignorespaces#1% \localbetweenisolatedwords can be overruled \global\isolatedlastskip\lastskip}% \setbox2\normalvbox {%\hyphenpenalty10000 % this one fails in \url breaking, diff --git a/tex/context/base/supp-mps.tex b/tex/context/base/supp-mps.tex index 145681489..6b3cea88c 100644 --- a/tex/context/base/supp-mps.tex +++ b/tex/context/base/supp-mps.tex @@ -514,7 +514,7 @@ \newtoks\MPTEXgraphicchecks \long\def\writecheckedMPgraphic#1% - {\ifforceMPTEXgraphic + {\ifgrTEXgraphic \global\MPTEXgraphictrue \else \global\MPTEXgraphicfalse diff --git a/tex/context/base/supp-tpi.tex b/tex/context/base/supp-tpi.tex index 2feeeac18..dc9d2e0e5 100644 --- a/tex/context/base/supp-tpi.tex +++ b/tex/context/base/supp-tpi.tex @@ -8,8 +8,8 @@ %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] %C %C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. %D This modules implements the conversion of graphic \TPIC\ %D specials using \METAPOST. @@ -19,6 +19,8 @@ %D \type{supp-spe} as well as the \METAPOST\ run||time support %D implemented in \type{supp-mps}. +\beginLUATEX \endinput \endLUATEX % to be sure, we don't want to load the following + \ifx\undefined\writestatus \input supp-mis \relax \fi \ifx\undefined\mimmickspecials \input supp-spe \relax \fi \ifx\undefined\MPgraphicbox \input supp-mps \relax \fi @@ -128,10 +130,10 @@ %D variable {\it currentpicture}, around the $x$-axis. \def\stopTPICspecials - {\startMPdrawing + {\startMPdrawing currentpicture:=currentpicture reflectedabout ((0,0),(4095,0)); \stopMPdrawing - \MPdrawingdonetrue + \MPdrawingdonetrue \setbox\MPgraphicbox\hbox {\getMPdrawing}% \setbox\MPgraphicbox\hbox to \zeropoint @@ -193,7 +195,7 @@ {\startTPICspecials \bgroup \dimen0=#1pt \dimen0=.07227\dimen0 - \startMPdrawing + \startMPdrawing pickup pencircle scaled \the\dimen0; \stopMPdrawing \egroup} @@ -271,7 +273,7 @@ \ifTPICdraw \def\TPICgrayscale{}% \fi - \startMPdrawing + \startMPdrawing \ifTPICfill fill\fi\ifTPICdraw draw\fi\space for i:=1 upto \the\TPICcounter-1: p[i]\ifTPICcurve..\else--\fi diff --git a/tex/context/base/symb-was.tex b/tex/context/base/symb-was.tex index ba2617306..d6c324864 100644 --- a/tex/context/base/symb-was.tex +++ b/tex/context/base/symb-was.tex @@ -136,7 +136,7 @@ \definesymbol [RIGHTarrow] [\WaldiSymbol {17}] \definesymbol [UParrow] [\WaldiSymbol {75}] \definesymbol [DOWNarrow] [\WaldiSymbol {76}] - \definesymbol [Box] [\WaldiSymbol {32}] + \definesymbol [Box] [\WaldiSymbol {50}] \definesymbol [APLbox] [\WaldiSymbol{126}] \definesymbol [XBox] [\WaldiSymbol {52}] \definesymbol [Bowtie] [\WaldiSymbol {49}] diff --git a/tex/context/base/syst-cat.tex b/tex/context/base/syst-cat.tex index e101bd78c..5f75ea2e7 100644 --- a/tex/context/base/syst-cat.tex +++ b/tex/context/base/syst-cat.tex @@ -410,7 +410,6 @@ %D \macros %D {installactivecharacter} - \def\installactivecharacter#1 % {\edef\temp{\detokenize{#1}}% \cctcounterc\expandafter`\temp\relax % relax needed diff --git a/tex/context/base/syst-etx.tex b/tex/context/base/syst-etx.tex index 5d7ab9a65..13163a681 100644 --- a/tex/context/base/syst-etx.tex +++ b/tex/context/base/syst-etx.tex @@ -230,7 +230,7 @@ %D are needed, but, for critical editions, we may need many %D more, so: -\chardef\@@insallocation = 32 +\chardef\@@insallocation = 32 %D However, there's a bug in \ETEX\ versions smaller than 2.2, %D so we need to play safe: diff --git a/tex/context/base/syst-gen.tex b/tex/context/base/syst-gen.tex index 739e0ed50..02d238a31 100644 --- a/tex/context/base/syst-gen.tex +++ b/tex/context/base/syst-gen.tex @@ -168,7 +168,7 @@ %D nesting deeper than one level, the system shows the %D protection level. -\newcount\protectionlevel +\ifx\protectionlevel\undefined \newcount\protectionlevel \fi \ifx\protect\undefined \def\protect{\writestatus{protection}{too much protection}} diff --git a/tex/context/base/syst-mtx.tex b/tex/context/base/syst-mtx.tex index 0abd89e57..e2a978671 100644 --- a/tex/context/base/syst-mtx.tex +++ b/tex/context/base/syst-mtx.tex @@ -50,7 +50,7 @@ \mathchardef\@@minallocation = 16 \mathchardef\@@medallocation = 256 \mathchardef\@@maxallocation = 32767 -\chardef \@@insallocation = 32 +\chardef \@@insallocation = 128 % was 32, but if we want continuous ranges (mplib) we need to pass 256 soon 32 \def\myalloc@#1#2#3#4#5% {\global\advance\count1#1by\@ne diff --git a/tex/context/base/type-gyr.tex b/tex/context/base/type-gyr.tex index 01d5c8202..80b391f77 100644 --- a/tex/context/base/type-gyr.tex +++ b/tex/context/base/type-gyr.tex @@ -11,6 +11,10 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +% \beginNEWTEX already-otf-gyre +% \endinput +% \endNEWTEX + %D These definitions will eventually replace the ones in \type {type-one}! % \usetypescriptfile[type-gyr] diff --git a/tex/context/base/type-otf.tex b/tex/context/base/type-otf.tex index 2b0a3dbdc..0449be878 100644 --- a/tex/context/base/type-otf.tex +++ b/tex/context/base/type-otf.tex @@ -131,13 +131,13 @@ \definefontsynonym [LMRoman12-Oblique] [file:lmromanslant12-regular][features=default] \definefontsynonym [LMRoman17-Oblique] [file:lmromanslant17-regular][features=default] \definefontsynonym [LMRoman10-BoldOblique] [file:lmromanslant10-bold] [features=default] - \definefontsynonym [LMRoman10-Demi] [file:lmromandemi10-oblique] [features=default] - \definefontsynonym [LMRoman10-DemiOblique] [file:lmromandemi10-regular] [features=default] - \definefontsynonym [LMRoman10-CapsRegular] [file:lmromancaps10-oblique] [features=default] % features=smallcaps? - \definefontsynonym [LMRoman10-CapsOblique] [file:lmromancaps10-regular] [features=default] + \definefontsynonym [LMRoman10-Demi] [file:lmromandemi10-regular] [features=default] + \definefontsynonym [LMRoman10-DemiOblique] [file:lmromandemi10-oblique] [features=default] + \definefontsynonym [LMRoman10-CapsRegular] [file:lmromancaps10-regular] [features=default] % features=smallcaps? + \definefontsynonym [LMRoman10-CapsOblique] [file:lmromancaps10-oblique] [features=default] - \definefontsynonym [LMRoman10-Dunhill] [file:lmromandunh10-oblique] [features=default] - \definefontsynonym [LMRoman10-DunhillOblique] [file:lmromandunh10-regular] [features=default] + \definefontsynonym [LMRoman10-Dunhill] [file:lmromandunh10-regular] [features=default] + \definefontsynonym [LMRoman10-DunhillOblique] [file:lmromandunh10-oblique] [features=default] \definefontsynonym [LMRoman10-Unslanted] [file:lmromanunsl10-regular] [features=default] \stoptypescript @@ -298,16 +298,16 @@ \definetypescriptprefix [n:chorus] [TeXGyreChorus] \definetypescriptprefix [f:chorus] [chorus] % not the full set \starttypescript [serif] [adventor,bonum,cursor,heros,pagella,schola,termes] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Regular] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-regular] [features=default] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Italic] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-italic] [features=default] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Bold] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-bold] [features=default] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldItalic] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-bolditalic] [features=default] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Caps] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-regular] [features=oldstyle] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-ItalicCaps] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-italic] [features=oldstyle] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldCaps] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-bold] [features=oldstyle] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldItalicCaps] [file:texgyre\typescriptthree\typescriptprefix{f:\typescripttwo}-bolditalic] [features=oldstyle] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Slanted] [\typescriptprefix{n:\typescripttwo}-Italic] [features=default] - \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldSlanted] [\typescriptprefix{n:\typescripttwo}-BoldItalic] [features=default] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Regular] [file:texgyre\typescriptprefix{f:\typescripttwo}-regular] [features=default] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Italic] [file:texgyre\typescriptprefix{f:\typescripttwo}-italic] [features=default] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Bold] [file:texgyre\typescriptprefix{f:\typescripttwo}-bold] [features=default] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldItalic] [file:texgyre\typescriptprefix{f:\typescripttwo}-bolditalic] [features=default] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Caps] [file:texgyre\typescriptprefix{f:\typescripttwo}-regular] [features=oldstyle] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-ItalicCaps] [file:texgyre\typescriptprefix{f:\typescripttwo}-italic] [features=oldstyle] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldCaps] [file:texgyre\typescriptprefix{f:\typescripttwo}-bold] [features=oldstyle] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldItalicCaps] [file:texgyre\typescriptprefix{f:\typescripttwo}-bolditalic] [features=oldstyle] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-Slanted] [\typescriptprefix{n:\typescripttwo}-Italic] [features=default] + \definefontsynonym [\typescriptprefix{n:\typescripttwo}-BoldSlanted] [\typescriptprefix{n:\typescripttwo}-BoldItalic] [features=default] \stoptypescript \starttypescript [serif] [pagella,termes,bonum,schola,chorus] [name] diff --git a/tex/context/base/x-cals.mkiv b/tex/context/base/x-cals.mkiv index 5d4a9b573..eba39cf64 100644 --- a/tex/context/base/x-cals.mkiv +++ b/tex/context/base/x-cals.mkiv @@ -19,6 +19,11 @@ do lxml.cals = { } lxml.cals.ignore_widths = false + local format = string.format + local texsprint = tex.sprint + local xmlsprint = xml.sprint + local xmlcprint = xml.cprint + local halignments = { left = "flushleft", right = "flushright", @@ -65,26 +70,24 @@ do end end - local texsprint = tex.sprint - local xmlsprint = xml.sprint - --local function texsprint(a,b) print(b) end --local function xmlsprint(a) print(a) end function lxml.cals.table(root,namespace) local prefix = (namespace or "cals") .. ":" - - local tgroupspec = "/" .. prefix .. "tgroup" - local colspec = "/" .. prefix .. "colspec" - local spanspec = "/" .. prefix .. "spanspec" - local hcolspec = "/" .. prefix .. "thead" .. "/" ..prefix .. "colspec" - local bcolspec = "/" .. prefix .. "tbody" .. "/" ..prefix .. "colspec" - local fcolspec = "/" .. prefix .. "tfoot" .. "/" ..prefix .. "colspec" - local entryspec = "/" .. prefix .. "entry" .. "|" ..prefix .. "entrytbl" - local hrowspec = "/" .. prefix .. "thead" .. "/" ..prefix .. "row" - local browspec = "/" .. prefix .. "tbody" .. "/" ..prefix .. "row" - local frowspec = "/" .. prefix .. "tfoot" .. "/" ..prefix .. "row" + local p = "/" .. prefix + + local tgroupspec = p .. "tgroup" + local colspec = p .. "colspec" + local spanspec = p .. "spanspec" + local hcolspec = p .. "thead" .. p .. "colspec" + local bcolspec = p .. "tbody" .. p .. "colspec" + local fcolspec = p .. "tfoot" .. p .. "colspec" + local entryspec = p .. "entry" .. "|" ..prefix .. "entrytbl" + local hrowspec = p .. "thead" .. p .. "row" + local browspec = p .. "tbody" .. p .. "row" + local frowspec = p .. "tfoot" .. p .. "row" local function tablepart(root, xcolspec, xrowspec, before, after) texsprint(tex.ctxcatcodes,before) @@ -122,13 +125,13 @@ do end local width = widths[col] if s or m or halign or valign or width then - texsprint(tex.ctxcatcodes,string.format("\\bTD[nx=%s,ny=%s,align={%s,%s},width=%s]", + texsprint(tex.ctxcatcodes,format("\\bTD[nx=%s,ny=%s,align={%s,%s},width=%s]", s or 1, (m or 0)+1, halign or "flushleft", valign or "high", width or "fit")) else texsprint(tex.ctxcatcodes,"\\bTD") end - -- xmlsprint(xml.content(dk)) - xmlsprint(dk) + -- xmlsprint(dk) + xmlcprint(dk) texsprint(tex.ctxcatcodes,"\\eTD") col = col + (s or 1) end @@ -140,7 +143,6 @@ do for r, d, k in xml.elements(lxml.id(root),tgroupspec) do local tgroup = d[k] --- print(tgroupspec,"!!!!!!!!!!!!!!!!!", r, d, k) lxml.directives.before(root,"cdx") -- "cals:table" texsprint(tex.ctxcatcodes, "\\bgroup") lxml.directives.setup(root,"cdx") -- "cals:table" diff --git a/tex/context/base/x-mml.mkiv b/tex/context/base/x-mml.mkiv index 8cbf07f08..ee042d010 100644 --- a/tex/context/base/x-mml.mkiv +++ b/tex/context/base/x-mml.mkiv @@ -26,7 +26,6 @@ % \xmlregistersetup{xml:mml:process} - \startxmlsetups xml:mml:process % \xmlutfize {\xmldocument} \xmlgrab {\xmldocument} {mml:*} {*} diff --git a/tex/context/base/x-mmp.mkiv b/tex/context/base/x-mmp.mkiv index 7b96b0d30..32bf4671c 100644 --- a/tex/context/base/x-mmp.mkiv +++ b/tex/context/base/x-mmp.mkiv @@ -14,15 +14,21 @@ % -- ignored: malignmark % -- luacode will be moved to x-mmp.lua +% \defineXMLentity[textspace] {\enspace} +% \defineXMLentity[textcomma] {{,}} +% \defineXMLentity[textperiod] {{.}} + \startluacode do lxml.mml = lxml.mml or { } + local texsprint = tex.sprint + local replacements = { - [" "] = "&textspace;", - ["."] = "&textperiod;", - [","] = "&textcomma;", +-- [" "] = utf.char(0x2002), -- "&textspace;" -> tricky, no &; in mkiv +-- ["."] = "{.}", +-- [","] = "{,}", } local reppattern = "([ %.%,])" @@ -31,7 +37,7 @@ local str = xml.content(lxml.id(id),pattern) or "" str = str:gsub("^%s*(.-)%s*$","%1") str = str:gsub(reppattern,replacements) - tex.sprint(str) + texsprint(str) end function lxml.mml.connect(id,pattern,separators) @@ -42,14 +48,14 @@ lxml.all(id,pattern) else local t = { } - for s in unicode.utf8.gmatch(separators,"([^%s])") do + for s in utf.gmatch(separators,"([^%s])") do t[#t+1] = s end for i=1,n do if i > 1 then - tex.sprint(tex.ctxcatcodes,"{") - tex.sprint(t[i] or t[#t] or "") - tex.sprint(tex.ctxcatcodes,"}") + texsprint(tex.ctxcatcodes,"{") + texsprint(t[i] or t[#t] or "") + texsprint(tex.ctxcatcodes,"}") end lxml.idx(id,pattern,i) -- kind of slow, some day ... end @@ -57,21 +63,21 @@ end local function flush(e,tag,toggle) - -- tex.sprint(tex.ctxcatcodes,(toggle and "^{") or "_{") + -- texsprint(tex.ctxcatcodes,(toggle and "^{") or "_{") if toggle then - tex.sprint(tex.ctxcatcodes,"^{") + texsprint(tex.ctxcatcodes,"^{") else - tex.sprint(tex.ctxcatcodes,"_{") + texsprint(tex.ctxcatcodes,"_{") end if tag == "none" then - tex.sprint(tex.ctxcatcodes,"{}") + texsprint(tex.ctxcatcodes,"{}") else xml.sprint(e.dt) end if not toggle then - tex.sprint(tex.ctxcatcodes,"}") + texsprint(tex.ctxcatcodes,"}") else - tex.sprint(tex.ctxcatcodes,"}{}") + texsprint(tex.ctxcatcodes,"}{}") end return not toggle end @@ -84,7 +90,7 @@ local e = d[k] local tag = e.tg if tag == "mprescripts" then - tex.sprint(tex.ctxcatcodes,"{}") + texsprint(tex.ctxcatcodes,"{}") done = true elseif done then toggle = flush(e,tag,toggle) @@ -172,12 +178,11 @@ % setups -\defineXMLentity[textspace] {\enspace} -\defineXMLentity[textcomma] {{,}} -\defineXMLentity[textperiod] {{.}} +% \defineXMLentity[_] {{\_{}}} +% \defineXMLentity[^] {{\^{}}} -\defineXMLentity[_] {{\_}} -\defineXMLentity[^] {{\^}} +% \defineXMLentity[_] {\string_} +% \defineXMLentity[^] {\normalorfiller\hat\widehat} \startsetups mml:semantics % todo: width=ex/ex/pt \xmlflush{#1} @@ -193,12 +198,16 @@ \stopxmlsetups \startxmlsetups mml:mn % todo: mathvariant mathsize mathcolor mathbackground - % function and style \begingroup \rm \ctxlua{lxml.mml.prepare_number("#1","*")} \endgroup \stopxmlsetups +\startxmlsetups mml:mn % todo: mathvariant mathsize mathcolor mathbackground + \mathop{\hbox{\mr \ctxlua{lxml.mml.prepare_number("#1","*")}}}% we need . and , properly spaced +\stopxmlsetups + + \startxmlsetups mml:mo \edef\MMPoperator{\xmlstripped{#1}{*}} \doifXMLentityelse{\detokenize\expandafter{\MMPoperator}} { @@ -332,15 +341,49 @@ {\xmlindex{#1}{/*}{1}}_{\xmlindex{#1}{/*}{2}}^{\xmlindex{#1}{/*}{3}} \stopsetups +% \startsetups mml:mover +% \mathop { +% \doifelse{\xmlatt{#1}{accent}}{true} { +% \begingroup +% \xmldoif{#1}{/*[position()==2 and @stretchy=='true']} { +% \let\normalorfiller\secondoftwoarguments +% } +% \xmlindex{#1}{/*}{2}{\xmlindex{#1}{/*}{1}} +% \endgroup +% } { +% \vbox { +% \m@th\ialign { +% \hss##\hss\crcr +% \noalign{\kern3\p@}% +% \disabledelimiter\doMMLfiller{\xmlindex{#1}{/*}{2}}\crcr +% \noalign{\kern3\p@\nointerlineskip}% +% \disabledelimiter\doMMLfiller{\xmlindex{#1}{/*}{1}}\crcr +% } +% } +% } +% } +% \limits +% \stopsetups + + \startsetups mml:mover \mathop { - \vbox { - \m@th\ialign { - \hss##\hss\crcr - \noalign{\kern3\p@}% - \disabledelimiter\doMMLfiller{\xmlindex{#1}{/*}{2}}\crcr - \noalign{\kern3\p@\nointerlineskip}% - \disabledelimiter\doMMLfiller{\xmlindex{#1}{/*}{1}}\crcr + \edef\mmlovertoken{\xmlraw{#1}{/mml:mo[position()==2]}} + \doifelse{\utfmathclass\mmlovertoken}{accent} { + \utfmathcommand\mmlovertoken{\xmlindex{#1}{/*}{1}} + } { + \vbox { + \m@th\ialign { + \hss##\hss\crcr + \noalign{\kern3\p@}% + \disabledelimiter\doMMLfiller{ + \xmlindex{#1}{/*}{2} + }\crcr + \noalign{\kern3\p@\nointerlineskip}% + \disabledelimiter\doMMLfiller{ + \xmlindex{#1}{/*}{1} + }\crcr + } } } } @@ -363,9 +406,15 @@ \stopsetups \startsetups mml:munderover - \xmlindex{#1}{/*}{1} - _{\disablefiller\disabledelimiter\xmlindex{#1}{/*}{2}} - ^{\disablefiller\disabledelimiter\xmlindex{#1}{/*}{3}} + \edef\mmlunderovertoken{\xmlraw{#1}{/mml:mo[position()==1]}} +% \doifelse{\utfmathclass\mmlunderovertoken}{limop} { +% \utfmathcommand\mmlunderovertoken +% } { +% \xmlindex{#1}{/*}{1} +% { + \utfmathcommanddefault\mmlunderovertoken{xmlindex}{{#1}{/*}{1}} + _{\disablefiller\disabledelimiter\xmlindex{#1}{/*}{2}} + ^{\disablefiller\disabledelimiter\xmlindex{#1}{/*}{3}} \stopsetups \startxmlsetups mml:mlabeledtr diff --git a/tex/context/base/xtag-ent.tex b/tex/context/base/xtag-ent.tex index 83f1b1139..a34fbab28 100644 --- a/tex/context/base/xtag-ent.tex +++ b/tex/context/base/xtag-ent.tex @@ -11,15 +11,6 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -% Here we also define fallbacks - -\defineXMLentities [tex] {tex} {\TeX} -\defineXMLentities [latex] {latex} {\LaTeX} -\defineXMLentities [context] {context} {\ConTeXt} -\defineXMLentities [omega] {omega} {\Omega} - -\defineXMLentities [Omega] {Omega} {\Omega} - % We need this for French and Hungarian. \defineXMLentity [colon] {\directdiscretionary{:}} diff --git a/tex/context/interface/keys-cz.xml b/tex/context/interface/keys-cz.xml index 66b5587be..894d25657 100644 --- a/tex/context/interface/keys-cz.xml +++ b/tex/context/interface/keys-cz.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="cz" version="2008.03.11 23:55"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="cz" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> diff --git a/tex/context/interface/keys-de.xml b/tex/context/interface/keys-de.xml index 3e8c0991f..4ca09a22d 100644 --- a/tex/context/interface/keys-de.xml +++ b/tex/context/interface/keys-de.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="de" version="2008.03.11 23:55"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="de" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> diff --git a/tex/context/interface/keys-en.xml b/tex/context/interface/keys-en.xml index 341f36028..102fcf0e6 100644 --- a/tex/context/interface/keys-en.xml +++ b/tex/context/interface/keys-en.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="en" version="2008.03.11 23:55"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="en" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> diff --git a/tex/context/interface/keys-fr.xml b/tex/context/interface/keys-fr.xml index 4dfc85ce3..d843caba6 100644 --- a/tex/context/interface/keys-fr.xml +++ b/tex/context/interface/keys-fr.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="fr" version="2008.03.11 23:55"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="fr" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> diff --git a/tex/context/interface/keys-it.xml b/tex/context/interface/keys-it.xml index 3231d2ee2..a97d897a6 100644 --- a/tex/context/interface/keys-it.xml +++ b/tex/context/interface/keys-it.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="it" version="2008.03.11 23:55"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="it" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> diff --git a/tex/context/interface/keys-nl.xml b/tex/context/interface/keys-nl.xml index 816cf99c9..cc340e462 100644 --- a/tex/context/interface/keys-nl.xml +++ b/tex/context/interface/keys-nl.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="nl" version="2008.03.11 23:55"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="nl" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> diff --git a/tex/context/interface/keys-ro.xml b/tex/context/interface/keys-ro.xml index 9eeb40772..bdc1a3358 100644 --- a/tex/context/interface/keys-ro.xml +++ b/tex/context/interface/keys-ro.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="ro" version="2008.03.24 23:24"> +<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" name="context" language="ro" version="2008.04.11 00:07"> <cd:variables> <cd:variable name="lesshyphenation" value="lesshyphenation"/> |