diff options
author | Hans Hagen <pragma@wxs.nl> | 2018-03-15 16:04:31 +0100 |
---|---|---|
committer | Context Git Mirror Bot <phg42.2a@gmail.com> | 2018-03-15 16:04:31 +0100 |
commit | a4e07f30e880ab27c2918f81f136e257475b7729 (patch) | |
tree | 02db002d3001a49777a049f9a98fdc872a5e1ad1 /tex/generic | |
parent | cbc37c39432e0ebe38e0922fc6d14c2955ab3ba2 (diff) | |
download | context-a4e07f30e880ab27c2918f81f136e257475b7729.tar.gz |
2018-03-15 15:36:00
Diffstat (limited to 'tex/generic')
-rw-r--r-- | tex/generic/context/luatex/luatex-basics-nod.lua | 54 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-core.lua | 267 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-demo-tt.lua | 136 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua | 21 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-ext.lua | 6 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-merged.lua | 4772 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-mis.lua | 32 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts.lua | 19 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-gadgets.lua | 17 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-math.tex | 1492 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-mplib.lua | 227 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-mplib.tex | 7 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-pdf.tex | 15 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-swiglib.lua | 8 |
14 files changed, 4408 insertions, 2665 deletions
diff --git a/tex/generic/context/luatex/luatex-basics-nod.lua b/tex/generic/context/luatex/luatex-basics-nod.lua index f9a59734b..40fb9ee4e 100644 --- a/tex/generic/context/luatex/luatex-basics-nod.lua +++ b/tex/generic/context/luatex/luatex-basics-nod.lua @@ -48,7 +48,6 @@ end -- Nodes (a subset of context so that we don't get too much unused code): nodes = { } -nodes.pool = { } nodes.handlers = { } local nodecodes = { } @@ -73,7 +72,6 @@ nodes.disccodes = disccodes local flush_node = node.flush_node local remove_node = node.remove -local new_node = node.new local traverse_id = node.traverse_id nodes.handlers.protectglyphs = node.protect_glyphs @@ -108,12 +106,6 @@ function nodes.delete(head,current) return nodes.remove(head,current,true) end -function nodes.pool.kern(k) - local n = new_node("kern",1) - n.kern = k - return n -end - local getfield = node.getfield local setfield = node.setfield @@ -209,6 +201,7 @@ nuts.setchar = direct.setchar nuts.getdisc = direct.getdisc nuts.setdisc = direct.setdisc nuts.setlink = direct.setlink +nuts.setsplit = direct.setsplit nuts.getlist = direct.getlist nuts.setlist = direct.setlist @@ -294,20 +287,11 @@ nuts.traverse_id = direct.traverse_id nuts.traverse_char = direct.traverse_char nuts.ligaturing = direct.ligaturing nuts.kerning = direct.kerning +nuts.new = direct.new nuts.getprop = nuts.getattr nuts.setprop = nuts.setattr -local new_nut = direct.new -nuts.new = new_nut -nuts.pool = { } - -function nuts.pool.kern(k) - local n = new_nut("kern",1) - setfield(n,"kern",k) - return n -end - -- properties as used in the (new) injector: local propertydata = direct.get_properties_table() @@ -462,3 +446,37 @@ function nuts.copy_only_glyphs(current) end return head end + +nuts.uses_font = direct.uses_font + +if not nuts.uses_font then + local getdisc = nuts.getdisc + local getfont = nuts.getfont + function nuts.uses_font(n,font) + local pre, post, replace = getdisc(n) + if pre then + -- traverse_char + for n in traverse_id(glyph_code,pre) do + if getfont(n) == font then + return true + end + end + end + if post then + for n in traverse_id(glyph_code,post) do + if getfont(n) == font then + return true + end + end + end + if replace then + for n in traverse_id(glyph_code,replace) do + if getfont(n) == font then + return true + end + end + end + return false + end +end + diff --git a/tex/generic/context/luatex/luatex-core.lua b/tex/generic/context/luatex/luatex-core.lua index 16df01707..35005d1c8 100644 --- a/tex/generic/context/luatex/luatex-core.lua +++ b/tex/generic/context/luatex/luatex-core.lua @@ -1,22 +1,24 @@ +-- luatex-core security and io overloads ........... + -- if not modules then modules = { } end modules ['luatex-core'] = { --- version = 1.001, +-- version = 1.005, -- comment = 'companion to luatex', -- author = 'Hans Hagen & Luigi Scarso', -- copyright = 'LuaTeX Development Team', -- } -LUATEXCOREVERSION = 1.002 +LUATEXCOREVERSION = 1.005 -- This file overloads some Lua functions. The readline variants provide the same -- functionality as LuaTeX <= 1.04 and doing it this way permits us to keep the -- original io libraries clean. Performance is probably even a bit better now. local type, next, getmetatable, require = type, next, getmetatable, require -local find, gsub = string.find, string.gsub +local find, gsub, format = string.find, string.gsub, string.format local io_open = io.open local io_popen = io.popen -local io_line = io.lines +local io_lines = io.lines local fio_readline = fio.readline local fio_checkpermission = fio.checkpermission @@ -28,8 +30,8 @@ local saferoption = status.safer_option local shellescape = status.shell_escape -- 0 (disabled) 1 (anything) 2 (restricted) local kpseused = status.kpse_used -- 0 1 -io.saved_open = io_open -- can be protected -io.saved_popen = io_popen -- can be protected +local write_nl = texio.write_nl + io.saved_lines = io_lines -- always readonly mt.saved_lines = mt_lines -- always readonly @@ -71,12 +73,41 @@ local function luatex_io_popen(name,...) end end -local function luatex_io_lines(name) - local f = io_open(name,'r') - if f then - return function() - return fio_readline(f) +-- local function luatex_io_lines(name,how) +-- if name then +-- local f = io_open(name,how or 'r') +-- if f then +-- return function() +-- return fio_readline(f) +-- end +-- end +-- else +-- return io_lines() +-- end +-- end + +-- For some reason the gc doesn't kick in so we need to close explitly +-- so that the handle is flushed. + +local error, type = error, type + +local function luatex_io_lines(name,how) + if type(name) == "string" then + local f = io_open(name,how or 'r') + if f then + return function() + local l = fio_readline(f) + if not l then + f:close() + end + return l + end + else + -- for those who like it this way: + error("patched 'io.lines' can't open '" .. name .. "'") end + else + return io_lines() end end @@ -101,29 +132,44 @@ end if saferoption == 1 then - os.execute = nil - os.spawn = nil - os.exec = nil - os.setenv = nil - os.tempdir = nil + local function installdummy(str,f) + local reported = false + return function(...) + if not reported then + write_nl(format("safer option set, function %q is %s", + str,f and "limited" or "disabled")) + reported = true + end + if f then + return f(...) + end + end + end + + local function installlimit(str,f) + local reported = false + end - io.popen = nil - io.open = nil + os.execute = installdummy("os.execute") + os.spawn = installdummy("os.spawn") + os.exec = installdummy("os.exec") + os.setenv = installdummy("os.setenv") + os.tempdir = installdummy("os.tempdir") - os.rename = nil - os.remove = nil + io.popen = installdummy("io.popen") + io.open = installdummy("io.open",luatex_io_open_readonly) - io.tmpfile = nil - io.output = nil + os.rename = installdummy("os.rename") + os.remove = installdummy("os.remove") - lfs.chdir = nil - lfs.lock = nil - lfs.touch = nil - lfs.rmdir = nil - lfs.mkdir = nil + io.tmpfile = installdummy("io.tmpfile") + io.output = installdummy("io.output") - io.saved_popen = nil - io.saved_open = luatex_io_open_readonly + lfs.chdir = installdummy("lfs.chdir") + lfs.lock = installdummy("lfs.lock") + lfs.touch = installdummy("lfs.touch") + lfs.rmdir = installdummy("lfs.rmdir") + lfs.mkdir = installdummy("lfs.mkdir") end @@ -163,12 +209,175 @@ if md5 then end +-- compatibility: this might go away + +if not unpack then + unpack = table.unpack +end + +if not package.loaders then + package.loaders = package.searchers +end + +if not loadstring then + loadstring = load +end + +-- compatibility: this might stay + +if bit32 then + + -- lua 5.2: we're okay + +elseif utf8 then + + -- lua 5.3: bitwise.lua, v 1.24 2014/12/26 17:20:53 roberto + + bit32 = load ( [[ +local select = select -- instead of: arg = { ... } + +bit32 = { + bnot = function (a) + return ~a & 0xFFFFFFFF + end, + band = function (x, y, z, ...) + if not z then + return ((x or -1) & (y or -1)) & 0xFFFFFFFF + else + local res = x & y & z + for i=1,select("#",...) do + res = res & select(i,...) + end + return res & 0xFFFFFFFF + end + end, + bor = function (x, y, z, ...) + if not z then + return ((x or 0) | (y or 0)) & 0xFFFFFFFF + else + local res = x | y | z + for i=1,select("#",...) do + res = res | select(i,...) + end + return res & 0xFFFFFFFF + end + end, + bxor = function (x, y, z, ...) + if not z then + return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF + else + local res = x ~ y ~ z + for i=1,select("#",...) do + res = res ~ select(i,...) + end + return res & 0xFFFFFFFF + end + end, + btest = function (x, y, z, ...) + if not z then + return (((x or -1) & (y or -1)) & 0xFFFFFFFF) ~= 0 + else + local res = x & y & z + for i=1,select("#",...) do + res = res & select(i,...) + end + return (res & 0xFFFFFFFF) ~= 0 + end + end, + lshift = function (a, b) + return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF + end, + rshift = function (a, b) + return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF + end, + arshift = function (a, b) + a = a & 0xFFFFFFFF + if b <= 0 or (a & 0x80000000) == 0 then + return (a >> b) & 0xFFFFFFFF + else + return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF + end + end, + lrotate = function (a ,b) + b = b & 31 + a = a & 0xFFFFFFFF + a = (a << b) | (a >> (32 - b)) + return a & 0xFFFFFFFF + end, + rrotate = function (a, b) + b = -b & 31 + a = a & 0xFFFFFFFF + a = (a << b) | (a >> (32 - b)) + return a & 0xFFFFFFFF + end, + extract = function (a, f, w) + return (a >> f) & ~(-1 << (w or 1)) + end, + replace = function (a, v, f, w) + local mask = ~(-1 << (w or 1)) + return ((a & ~(mask << f)) | ((v & mask) << f)) & 0xFFFFFFFF + end, +} + ]] ) + +elseif bit then + + -- luajit (for now) + + bit32 = load ( [[ +local band, bnot, rshift, lshift = bit.band, bit.bnot, bit.rshift, bit.lshift + +bit32 = { + arshift = bit.arshift, + band = band, + bnot = bnot, + bor = bit.bor, + bxor = bit.bxor, + btest = function(...) + return band(...) ~= 0 + end, + extract = function(a,f,w) + return band(rshift(a,f),2^(w or 1)-1) + end, + lrotate = bit.rol, + lshift = lshift, + replace = function(a,v,f,w) + local mask = 2^(w or 1)-1 + return band(a,bnot(lshift(mask,f)))+lshift(band(v,mask),f) + end, + rrotate = bit.ror, + rshift = rshift, +} + ]] ) + +else + + -- hope for the best or fail + + bit32 = require("bit32") + +end + +-- this is needed for getting require("socket") right + +do + + local loaded = package.loaded + + if not loaded.socket then loaded.socket = loaded["socket.core"] end + if not loaded.mime then loaded.mime = loaded["mime.core"] end + +end + +-- so far + if utilities and utilities.merger and utilities.merger.compact then local byte, format, gmatch = string.byte, string.format, string.gmatch local concat = table.concat local data = gsub(io.loaddata('luatex-core.lua'),'if%s+utilities.*','') + local t = { } local r = { } local n = 0 diff --git a/tex/generic/context/luatex/luatex-fonts-demo-tt.lua b/tex/generic/context/luatex/luatex-fonts-demo-tt.lua new file mode 100644 index 000000000..45c9a84ff --- /dev/null +++ b/tex/generic/context/luatex/luatex-fonts-demo-tt.lua @@ -0,0 +1,136 @@ +if not modules then modules = { } end modules ['luatex-fonts-demo-tt'] = { + version = 1.001, + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- Someone asked on the list if we could fake bad typewriters. Actually there are +-- already enough examples in successive articles and it's not too complex to mess +-- with fonts. There is a nicer way to do this (with a bit of metapost) but I have +-- no time now. After all, features like this are never used in practice, so it's a +-- waste of time to code them. +-- +-- Todo: force emwidth/5 for fonts > 1 ... only when requested. +-- +-- \starttext +-- +-- \raggedright +-- +-- \definefontfeature[badtypewritera][ttgrayness=.5] +-- \definefontfeature[badtypewriterb][ttshift=.5] +-- \definefontfeature[badtypewriterc][ttthickness=2] +-- \definefontfeature[badtypewriterd][ttgrayness=.5,ttshift=.5,ttthickness=2,ttfont={dejavusansmono}] +-- \definefontfeature[badtypewritere][ttgrayness=.5,ttthickness=2,ttstep=2,ttmax=1.5] +-- +-- \definefont[MyFontA][file:luatex-fonts-demo-tt.lua*badtypewritera] +-- \definefont[MyFontB][file:luatex-fonts-demo-tt.lua*badtypewriterb] +-- \definefont[MyFontC][file:luatex-fonts-demo-tt.lua*badtypewriterc] +-- \definefont[MyFontD][file:luatex-fonts-demo-tt.lua*badtypewriterd @ 10pt] +-- \definefont[MyFontE][file:luatex-fonts-demo-tt.lua*badtypewritere @ 10pt] +-- +-- \MyFontA \input tufte \blank {\righttoleft لَيْسَ لَدَيَّ أَيُّ فِكْرَةٍ عَمَّا يَعْنِيهِ هٰذَا.} \page +-- \MyFontB \input tufte \blank {\righttoleft لَيْسَ لَدَيَّ أَيُّ فِكْرَةٍ عَمَّا يَعْنِيهِ هٰذَا.} \page +-- \MyFontC \input tufte \blank {\righttoleft لَيْسَ لَدَيَّ أَيُّ فِكْرَةٍ عَمَّا يَعْنِيهِ هٰذَا.} \page +-- \MyFontD \input tufte \blank {\righttoleft لَيْسَ لَدَيَّ أَيُّ فِكْرَةٍ عَمَّا يَعْنِيهِ هٰذَا.} \page +-- \MyFontE \input tufte \blank {\righttoleft لَيْسَ لَدَيَّ أَيُّ فِكْرَةٍ عَمَّا يَعْنِيهِ هٰذَا.} \page +-- +-- \stoptext + +local random, sin = math.random, math.sin +local formatters = string.formatters + +local now = 0 +local max = 2 * math.pi +local step = max/20 + +-- The sin trick is first shown by Hartmut in 2005 when we had a few more plugs into +-- the backend. His demo was a rather colorful sheet that looked like it was crumpled. +-- There were no virtual fonts involved the, just pdf.print hooked into an always +-- applied Lua function. + +function fonts.helpers.FuzzyFontStart(exheight,ttgrayness,ttthickness,ttshift,ttstep,ttmax) + local grayness = ttgrayness * random(0,5)/10 + local thickness = ttthickness * random(1,2)/10 + local shift = 0 + if ttstep > 0 then + if now > max then + now = 0 + else + now = now + step * ttstep + end + shift = ttmax * sin(now) * exheight/5 + else + shift = ttshift * random(-1,1) * exheight/20 + end + -- We can optimize for one of them being zero or the default but no one will + -- use this in production so efficiency hardly matters. + local template = formatters["pdf:page:q %0.2F g %.2F G %0.2F w 2 Tr %.3F Ts"]( + grayness, grayness, thickness, shift + ) + vf.special(template) + -- will be: + -- local template = formatters["q %0.2F g %.2F G %0.2F w 2 Tr %.3F Ts"]( + -- grayness, grayness, thickness, shift + -- ) + -- vf.pdf("page",template) +end + +function fonts.helpers.FuzzyFontStop() + vf.special("pdf:page:Q") + -- will be: + -- vf.pdf("page","Q") +end + +return function(specification) + local features = specification.features.normal + local list = features.ttfont + if list then + list = utilities.parsers.settings_to_array(list) + else + list = { + 'lmtypewriter10-regular', + 'almfixed', + } + end + local f = { } + local id = { } + for i=1,#list do + f[i], id[i] = fonts.constructors.readanddefine(list[i],specification.size) + end + local f1 = f[1] + if f1 then + f1.name = specification.name -- needs checking (second time used an error) + f1.properties.name = specification.name + f1.properties.virtualized = true + f1.fonts = { } + local target = f1.characters + local exbp = f1.parameters.exheight * number.dimenfactors.bp + local stop = { + "lua", + "fonts.helpers.FuzzyFontStop()", + } + local start = { + "lua", + formatters["fonts.helpers.FuzzyFontStart(%.3F,%.2F,%.2F,%.2F,%.2F,%.2F)"]( + exbp, + tonumber(features.ttgrayness) or 1, + tonumber(features.ttthickness) or 1, + tonumber(features.ttshift) or 1, + tonumber(features.ttstep) or 0, + tonumber(features.ttmax) or 1 + ), + } + for i=1,#list do + f1.fonts[i] = { id = id[i] } + local characters = f[i].characters + for u, v in next, characters do + v.commands = { start, { "slot", i, u }, stop } + if characters ~= target then + target[u] = v + end + end + end + end + return f1 +end diff --git a/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua b/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua index 793526f7b..c4567e446 100644 --- a/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua +++ b/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua @@ -3,11 +3,19 @@ if not modules then modules = { } end modules ['luatex-fonts-demo-vf-1'] = { comment = "companion to luatex-*.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" + license = "see context related readme files", } local identifiers = fonts.hashes.identifiers +local defaults = { [0] = + { "pdf", "origin", "0 g" }, + { "pdf", "origin", "1 0 0 rg" }, + { "pdf", "origin", "0 1 0 rg" }, + { "pdf", "origin", "0 0 1 rg" }, + { "pdf", "origin", "0 0 1 rg" }, +} + return function(specification) local f1, id1 = fonts.constructors.readanddefine('lmroman10-regular', specification.size) local f2, id2 = fonts.constructors.readanddefine('lmsans10-regular', specification.size) @@ -20,13 +28,6 @@ return function(specification) { id = id2 }, { id = id3 }, } - local color = { [0] = - { "special", "pdf:0 g" }, - { "special", "pdf:1 0 0 rg" }, - { "special", "pdf:0 1 0 rg" }, - { "special", "pdf:0 0 1 rg" }, - { "special", "pdf:0 0 1 rg" }, - } local chars = { identifiers[id1].characters, identifiers[id2].characters, @@ -36,9 +37,9 @@ return function(specification) local n = math.floor(math.random(1,3)+0.5) local c = chars[n][u] or v v.commands = { - color[n], + defaults[n] or defaults[0], { 'slot', n, u }, - color[0], + defaults[0], { 'nop' } } v.kerns = nil diff --git a/tex/generic/context/luatex/luatex-fonts-ext.lua b/tex/generic/context/luatex/luatex-fonts-ext.lua index 7d9c58ccb..15762d9ba 100644 --- a/tex/generic/context/luatex/luatex-fonts-ext.lua +++ b/tex/generic/context/luatex/luatex-fonts-ext.lua @@ -13,6 +13,7 @@ end local fonts = fonts local otffeatures = fonts.constructors.features.otf +local getprivate = fonts.constructors.getprivate -- A few generic extensions. @@ -290,15 +291,13 @@ local setmetatableindex = table.setmetatableindex local function additalictowidth(tfmdata,key,value) local characters = tfmdata.characters - local resources = tfmdata.resources local additions = { } - local private = resources.private for unicode, old_c in next, characters do -- maybe check for math local oldwidth = old_c.width local olditalic = old_c.italic if olditalic and olditalic ~= 0 then - private = private + 1 + local private = getprivate(tfmdata) local new_c = { width = oldwidth + olditalic, height = old_c.height, @@ -316,7 +315,6 @@ local function additalictowidth(tfmdata,key,value) for k, v in next, additions do characters[k] = v end - resources.private = private end otffeatures.register { diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index cc9ce7b5f..96398dcc0 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 05/15/17 21:48:19 +-- merge date : 03/15/18 15:27:14 do -- begin closure to overcome local limits and interference @@ -11,14 +11,16 @@ if not modules then modules={} end modules ['l-lua']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -_MAJORVERSION,_MINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") -_MAJORVERSION=tonumber(_MAJORVERSION) or 5 -_MINORVERSION=tonumber(_MINORVERSION) or 1 -_LUAVERSION=_MAJORVERSION+_MINORVERSION/10 -if _LUAVERSION<5.2 and jit then - _MINORVERSION=2 - _LUAVERSION=5.2 -end +local next,type,tonumber=next,type,tonumber +LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5 +LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1 +LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10 +if LUAVERSION<5.2 and jit then + MINORVERSION=2 + LUAVERSION=5.2 +end +_LUAVERSION=LUAVERSION if not lpeg then lpeg=require("lpeg") end @@ -118,6 +120,19 @@ if not FFISUPPORTED then elseif not ffi.number then ffi.number=tonumber end +if not bit32 then + bit32=require("l-bit32") +end +local loaded=package.loaded +if not loaded["socket"] then loaded["socket"]=loaded["socket.core"] end +if not loaded["mime"] then loaded["mime"]=loaded["mime.core"] end +if not socket.mime then socket.mime=package.loaded["mime"] end +if not loaded["socket.mime"] then loaded["socket.mime"]=socket.mime end +if not loaded["socket.http"] then loaded["socket.http"]=socket.http end +if not loaded["socket.ftp"] then loaded["socket.ftp"]=socket.ftp end +if not loaded["socket.smtp"] then loaded["socket.smtp"]=socket.smtp end +if not loaded["socket.tp"] then loaded["socket.tp"]=socket.tp end +if not loaded["socket.url"] then loaded["socket.url"]=socket.url end end -- closure @@ -130,7 +145,8 @@ if not modules then modules={} end modules ['l-lpeg']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -lpeg=require("lpeg") +lpeg=require("lpeg") +local lpeg=lpeg if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end local type,next,tostring=type,next,tostring local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format @@ -152,11 +168,14 @@ patterns.alwaysmatched=alwaysmatched local sign=S('+-') local zero=P('0') local digit=R('09') +local digits=digit^1 local octdigit=R("07") +local octdigits=octdigit^1 local lowercase=R("az") local uppercase=R("AZ") local underscore=P("_") local hexdigit=digit+lowercase+uppercase +local hexdigits=hexdigit^1 local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") local newline=P("\r")*(P("\n")+P(true))+P("\n") local escaped=P("\\")*anything @@ -258,27 +277,30 @@ patterns.singlequoted=squote*patterns.nosquote*squote patterns.doublequoted=dquote*patterns.nodquote*dquote patterns.quoted=patterns.doublequoted+patterns.singlequoted patterns.digit=digit +patterns.digits=digits patterns.octdigit=octdigit +patterns.octdigits=octdigits patterns.hexdigit=hexdigit +patterns.hexdigits=hexdigits patterns.sign=sign -patterns.cardinal=digit^1 -patterns.integer=sign^-1*digit^1 -patterns.unsigned=digit^0*period*digit^1 +patterns.cardinal=digits +patterns.integer=sign^-1*digits +patterns.unsigned=digit^0*period*digits patterns.float=sign^-1*patterns.unsigned -patterns.cunsigned=digit^0*comma*digit^1 -patterns.cpunsigned=digit^0*(period+comma)*digit^1 +patterns.cunsigned=digit^0*comma*digits +patterns.cpunsigned=digit^0*(period+comma)*digits patterns.cfloat=sign^-1*patterns.cunsigned patterns.cpfloat=sign^-1*patterns.cpunsigned patterns.number=patterns.float+patterns.integer patterns.cnumber=patterns.cfloat+patterns.integer patterns.cpnumber=patterns.cpfloat+patterns.integer -patterns.oct=zero*octdigit^1 +patterns.oct=zero*octdigits patterns.octal=patterns.oct patterns.HEX=zero*P("X")*(digit+uppercase)^1 patterns.hex=zero*P("x")*(digit+lowercase)^1 -patterns.hexadecimal=zero*S("xX")*hexdigit^1 -patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigit^1+hexdigit^1*period*hexdigit^0+hexdigit^1)*(S("pP")*sign^-1*hexdigit^1)^-1 -patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^1)*S("eE")*sign^-1*digit^1 +patterns.hexadecimal=zero*S("xX")*hexdigits +patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1 +patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring patterns.somecontent=(anything-newline-space)^1 patterns.beginline=#(1-newline) @@ -525,11 +547,13 @@ function lpeg.balancer(left,right) left,right=P(left),P(right) return P { left*((1-left-right)+V(1))^0*right } end -local nany=utf8char/"" -function lpeg.counter(pattern) - pattern=Cs((P(pattern)/" "+nany)^0) - return function(str) - return #lpegmatch(pattern,str) +function lpeg.counter(pattern,action) + local n=0 + local pattern=(P(pattern)/function() n=n+1 end+anything)^0 + if action then + return function(str) n=0;lpegmatch(pattern,str);action(n) end + else + return function(str) n=0;lpegmatch(pattern,str);return n end end end utf=utf or (unicode and unicode.utf8) or {} @@ -690,7 +714,13 @@ function lpeg.append(list,pp,delayed,checked) end local p_false=P(false) local p_true=P(true) -local function make(t,rest) +local lower=utf and utf.lower or string.lower +local upper=utf and utf.upper or string.upper +function lpeg.setutfcasers(l,u) + lower=l or lower + upper=u or upper +end +local function make1(t,rest) local p=p_false local keys=sortedkeys(t) for i=1,#keys do @@ -701,7 +731,7 @@ local function make(t,rest) p=p+P(k)*p_true elseif v==false then else - p=p+P(k)*make(v,v[""]) + p=p+P(k)*make1(v,v[""]) end end end @@ -710,32 +740,27 @@ local function make(t,rest) end return p end -local function collapse(t,x) - if type(t)~="table" then - return t,x - else - local n=next(t) - if n==nil then - return t,x - elseif next(t,n)==nil then - local k=n +local function make2(t,rest) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then local v=t[k] - if type(v)=="table" then - return collapse(v,x..k) + if v==true then + p=p+(P(lower(k))+P(upper(k)))*p_true + elseif v==false then else - return v,x..k - end - else - local tt={} - for k,v in next,t do - local vv,kk=collapse(v,k) - tt[kk]=vv + p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""]) end - return tt,x end end + if rest then + p=p+p_true + end + return p end -function lpeg.utfchartabletopattern(list) +function lpeg.utfchartabletopattern(list,insensitive) local tree={} local n=#list if n==0 then @@ -806,7 +831,7 @@ function lpeg.utfchartabletopattern(list) end end end - return make(tree) + return (insensitive and make2 or make1)(tree) end patterns.containseol=lpeg.finder(eol) local function nextstep(n,step,result) @@ -840,7 +865,7 @@ end local trailingzeros=zero^0*-digit local case_1=period*trailingzeros/"" local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") -local number=digit^1*(case_1+case_2) +local number=digits*(case_1+case_2) local stripper=Cs((number+1)^0) lpeg.patterns.stripzeros=stripper local byte_to_HEX={} @@ -1021,9 +1046,9 @@ function string.tformat(fmt,...) end string.quote=string.quoted string.unquote=string.unquoted -if not string.bytetable then +if not string.bytetable then local limit=5000 - function string.bytetable(str) + function string.bytetable(str) local n=#str if n>limit then local t={ byte(str,1,limit) } @@ -1048,7 +1073,7 @@ if not modules then modules={} end modules ['l-table']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local type,next,tostring,tonumber,ipairs,select=type,next,tostring,tonumber,ipairs,select +local type,next,tostring,tonumber,select=type,next,tostring,tonumber,select local table,string=table,string local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove local format,lower,dump=string.format,string.lower,string.dump @@ -1057,6 +1082,9 @@ local getinfo=debug.getinfo local lpegmatch,patterns=lpeg.match,lpeg.patterns local floor=math.floor local stripper=patterns.stripper +function table.getn(t) + return t and #t +end function table.strip(tab) local lst,l={},0 for i=1,#tab do @@ -1350,7 +1378,7 @@ function table.tohash(t,value) local h={} if t then if value==nil then value=true end - for _,v in next,t do + for _,v in next,t do h[v]=value end end @@ -1358,7 +1386,7 @@ function table.tohash(t,value) end function table.fromhash(t) local hsh,h={},0 - for k,v in next,t do + for k,v in next,t do if v then h=h+1 hsh[h]=k @@ -1833,7 +1861,9 @@ function table.unnest(t) return unnest(t) end local function are_equal(a,b,n,m) - if a and b and #a==#b then + if a==b then + return true + elseif a and b and #a==#b then n=n or 1 m=m or #a for i=n,m do @@ -1853,15 +1883,17 @@ local function are_equal(a,b,n,m) end end local function identical(a,b) - for ka,va in next,a do - local vb=b[ka] - if va==vb then - elseif type(va)=="table" and type(vb)=="table" then - if not identical(va,vb) then + if a~=b then + for ka,va in next,a do + local vb=b[ka] + if va==vb then + elseif type(va)=="table" and type(vb)=="table" then + if not identical(va,vb) then + return false + end + else return false end - else - return false end end return true @@ -2083,6 +2115,24 @@ function table.filtered(t,pattern,sort,cmp) return nothing end end +if not table.move then + function table.move(a1,f,e,t,a2) + if a2 and a1~=a2 then + for i=f,e do + a2[t]=a1[i] + t=t+1 + end + return a2 + else + t=t+e-f + for i=e,f,-1 do + a1[t]=a1[i] + t=t-1 + end + return a1 + end + end +end end -- closure @@ -2099,16 +2149,15 @@ local io=io local open,flush,write,read=io.open,io.flush,io.write,io.read local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format local concat=table.concat -local floor=math.floor local type=type if string.find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator="\\",";" else io.fileseparator,io.pathseparator="/",":" end -local large=2^24 -local medium=large/16 -local small=medium/8 +local large=0x01000000 +local medium=0x00100000 +local small=0x00020000 local function readall(f) local size=f:seek("end") if size>0 then @@ -2470,6 +2519,9 @@ end function lfs.isfile(name) return attributes(name,"mode")=="file" end +function lfs.isfound(name) + return attributes(name,"mode")=="file" and name or nil +end local colon=P(":") local period=P(".") local periods=P("..") @@ -2808,6 +2860,23 @@ function lfs.mkdirs(path) lfs.mkdir(full) end end +function file.withinbase(path) + local l=0 + if not find(path,"^/") then + path="/"..path + end + for dir in gmatch(path,"/([^/]+)") do + if dir==".." then + l=l-1 + elseif dir~="." then + l=l+1 + end + if l<0 then + return false + end + end + return true +end end -- closure @@ -2887,21 +2956,23 @@ if not modules then modules={} end modules ['l-math']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan if not math.ceiling then math.ceiling=math.ceil end if not math.round then + local floor=math.floor function math.round(x) return floor(x+0.5) end end if not math.div then + local floor=math.floor function math.div(n,m) return floor(n/m) end end if not math.mod then function math.mod(n,m) return n%m end end -local pipi=2*math.pi/360 if not math.sind then + local sin,cos,tan=math.sin,math.cos,math.tan + local pipi=2*math.pi/360 function math.sind(d) return sin(d*pipi) end function math.cosd(d) return cos(d*pipi) end function math.tand(d) return tan(d*pipi) end @@ -2910,6 +2981,60 @@ if not math.odd then function math.odd (n) return n%2~=0 end function math.even(n) return n%2==0 end end +if not math.cosh then + local exp=math.exp + function math.cosh(x) + local xx=exp(x) + return (xx+1/xx)/2 + end + function math.sinh(x) + local xx=exp(x) + return (xx-1/xx)/2 + end + function math.tanh(x) + local xx=exp(x) + return (xx-1/xx)/(xx+1/xx) + end +end +if not math.pow then + function math.pow(x,y) + return x^y + end +end +if not math.atan2 then + math.atan2=math.atan +end +if not math.ldexp then + function math.ldexp(x,e) + return x*2.0^e + end +end +if not math.log10 then + local log=math.log + function math.log10(x) + return log(x,10) + end +end +if not math.type then + function math.type() + return "float" + end +end +if not math.tointeger then + math.mininteger=-0x4FFFFFFFFFFF + math.maxinteger=0x4FFFFFFFFFFF + local floor=math.floor + function math.tointeger(n) + local f=floor(n) + return f==n and f or nil + end +end +if not math.ult then + local floor=math.floor + function math.tointeger(m,n) + return floor(m)<floor(n) + end +end end -- closure @@ -2949,37 +3074,73 @@ if not unicode then unicode={ utf=utf } end if not utf.char then - local floor,char=math.floor,string.char - function utf.char(n) - if n<0x80 then - return char(n) - elseif n<0x800 then - return char( - 0xC0+floor(n/0x40), - 0x80+(n%0x40) - ) - elseif n<0x10000 then - return char( - 0xE0+floor(n/0x1000), - 0x80+(floor(n/0x40)%0x40), - 0x80+(n%0x40) - ) - elseif n<0x200000 then - return char( - 0xF0+floor(n/0x40000), - 0x80+(floor(n/0x1000)%0x40), - 0x80+(floor(n/0x40)%0x40), - 0x80+(n%0x40) - ) + utf.char=string.utfcharacter or (utf8 and utf8.char) + if not utf.char then + local char=string.char + if bit32 then + local rshift=bit32.rshift + function utf.char(n) + if n<0x80 then + return char(n) + elseif n<0x800 then + return char( + 0xC0+rshift(n,6), + 0x80+(n%0x40) + ) + elseif n<0x10000 then + return char( + 0xE0+rshift(n,12), + 0x80+(rshift(n,6)%0x40), + 0x80+(n%0x40) + ) + elseif n<0x200000 then + return char( + 0xF0+rshift(n,18), + 0x80+(rshift(n,12)%0x40), + 0x80+(rshift(n,6)%0x40), + 0x80+(n%0x40) + ) + else + return "" + end + end else - return "" + local floor=math.floor + function utf.char(n) + if n<0x80 then + return char(n) + elseif n<0x800 then + return char( + 0xC0+floor(n/0x40), + 0x80+(n%0x40) + ) + elseif n<0x10000 then + return char( + 0xE0+floor(n/0x1000), + 0x80+(floor(n/0x40)%0x40), + 0x80+(n%0x40) + ) + elseif n<0x200000 then + return char( + 0xF0+floor(n/0x40000), + 0x80+(floor(n/0x1000)%0x40), + 0x80+(floor(n/0x40)%0x40), + 0x80+(n%0x40) + ) + else + return "" + end + end end end end if not utf.byte then - local utf8byte=patterns.utf8byte - function utf.byte(c) - return lpegmatch(utf8byte,c) + utf.byte=string.utfvalue or (utf8 and utf8.codepoint) + if not utf.byte then + local utf8byte=patterns.utf8byte + function utf.byte(c) + return lpegmatch(utf8byte,c) + end end end local utfchar,utfbyte=utf.char,utf.byte @@ -3030,19 +3191,22 @@ function utf.is_valid(str) return type(str)=="string" and lpegmatch(validatedutf,str) or false end if not utf.len then - local n,f=0,1 - local utfcharcounter=patterns.utfbom^-1*Cmt ( - Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1, - function(_,t,d) - n=n+(t-f)/d - f=t - return true + utf.len=string.utflength or (utf8 and utf8.len) + if not utf.len then + local n,f=0,1 + local utfcharcounter=patterns.utfbom^-1*Cmt ( + Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1, + function(_,t,d) + n=n+(t-f)/d + f=t + return true + end + )^0 + function utf.len(str) + n,f=0,1 + lpegmatch(utfcharcounter,str or "") + return n end - )^0 - function utf.len(str) - n,f=0,1 - lpegmatch(utfcharcounter,str or "") - return n end end utf.length=utf.len @@ -3390,20 +3554,22 @@ function utf.utf32_to_utf8_t(t,endian) end local function little(b) if b<0x10000 then - return char(b%256,b/256) + return char(b%256,rshift(b,8)) else b=b-0x10000 - local b1,b2=b/1024+0xD800,b%1024+0xDC00 - return char(b1%256,b1/256,b2%256,b2/256) + local b1=rshift(b,10)+0xD800 + local b2=b%1024+0xDC00 + return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8)) end end local function big(b) if b<0x10000 then - return char(b/256,b%256) + return char(rshift(b,8),b%256) else b=b-0x10000 - local b1,b2=b/1024+0xD800,b%1024+0xDC00 - return char(b1/256,b1%256,b2/256,b2%256) + local b1=rshift(b,10)+0xD800 + local b2=b%1024+0xDC00 + return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256) end end local l_remap=Cs((p_utf8byte/little+P(1)/"")^0) @@ -3524,22 +3690,43 @@ function utf.chrlen(u) (u<0xFC and 5) or (u<0xFE and 6) or 0 end -local extract=bit32.extract -local char=string.char -function unicode.toutf32string(n) - if n<=0xFF then - return - char(n).."\000\000\000" - elseif n<=0xFFFF then - return - char(extract(n,0,8))..char(extract(n,8,8)).."\000\000" - elseif n<=0xFFFFFF then - return - char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8)).."\000" - else - return - char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8))..char(extract(n,24,8)) +if bit32 then + local extract=bit32.extract + local char=string.char + function unicode.toutf32string(n) + if n<=0xFF then + return + char(n).."\000\000\000" + elseif n<=0xFFFF then + return + char(extract(n,0,8))..char(extract(n,8,8)).."\000\000" + elseif n<=0xFFFFFF then + return + char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8)).."\000" + else + return + char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8))..char(extract(n,24,8)) + end + end +end +local len=utf.len +local rep=rep +function string.utfpadd(s,n) + if n and n~=0 then + local l=len(s) + if n>0 then + local d=n-l + if d>0 then + return rep(c or " ",d)..s + end + else + local d=- n-l + if d>0 then + return s..rep(c or " ",d) + end + end end + return s end end -- closure @@ -3558,13 +3745,13 @@ utilities.strings=utilities.strings or {} local strings=utilities.strings local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find local load,dump=load,string.dump -local tonumber,type,tostring=tonumber,type,tostring +local tonumber,type,tostring,next=tonumber,type,tostring,next local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match -local utfchar,utfbyte=utf.char,utf.byte +local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len local loadstripped=nil -if _LUAVERSION<5.2 then +if LUAVERSION<5.2 then loadstripped=function(str,shortcuts) return load(str) end @@ -3652,6 +3839,17 @@ local pattern=Carg(1)/function(t) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end +function string.utfpadding(s,n) + if not n or n==0 then + return "" + end + local l=utflen(s) + if n>0 then + return nspaces[n-l] + else + return nspaces[-n-l] + end +end local space=spacer^0 local nospace=space/"" local endofline=nospace*newline @@ -3758,6 +3956,21 @@ function number.formatted(n,sep1,sep2) return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") end end +local p=Cs( + P("-")^0*(P("0")^1/"")^0*(1-P("."))^0*(P(".")*P("0")^1*P(-1)/""+P(".")^0)*P(1-P("0")^1*P(-1))^0 + ) +function number.compactfloat(n,fmt) + if n==0 then + return "0" + elseif n==1 then + return "1" + end + n=lpegmatch(p,format(fmt or "%0.3f",n)) + if n=="." or n=="" or n=="-" then + return "0" + end + return n +end local zero=P("0")^1/"" local plus=P("+")/"" local minus=P("-") @@ -3810,7 +4023,7 @@ local template=[[ return function(%s) return %s end ]] local preamble,environment="",{} -if _LUAVERSION<5.2 then +if LUAVERSION<5.2 then preamble=[[ local lpeg=lpeg local type=type @@ -3825,6 +4038,7 @@ local utfchar=utf.char local utfbyte=utf.byte local lpegmatch=lpeg.match local nspaces=string.nspaces +local utfpadding=string.utfpadding local tracedchar=string.tracedchar local autosingle=string.autosingle local autodouble=string.autodouble @@ -3849,6 +4063,7 @@ else utfbyte=utf.byte, lpegmatch=lpeg.match, nspaces=string.nspaces, + utfpadding=string.utfpadding, tracedchar=string.tracedchar, autosingle=string.autosingle, autodouble=string.autodouble, @@ -3884,9 +4099,32 @@ local format_S=function(f) return format("tostring(a%s)",n) end end +local format_right=function(f) + n=n+1 + f=tonumber(f) + if not f or f==0 then + return format("(a%s or '')",n) + elseif f>0 then + return format("utfpadding(a%s,%i)..a%s",n,f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,f) + end +end +local format_left=function(f) + n=n+1 + f=tonumber(f) + if not f or f==0 then + return format("(a%s or '')",n) + end + if f<0 then + return format("utfpadding(a%s,%i)..a%s",n,-f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,-f) + end +end local format_q=function() n=n+1 - return format("(a%s and format('%%q',a%s) or '')",n,n) + return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n) end local format_Q=function() n=n+1 @@ -4138,6 +4376,8 @@ local builder=Cs { "start", +V("j")+V("J") +V("m")+V("M") +V("z") ++V(">") ++V("<") )+V("*") )*(P(-1)+Carg(1)) )^0, @@ -4155,7 +4395,7 @@ local builder=Cs { "start", ["X"]=(prefix_any*P("X"))/format_X, ["o"]=(prefix_any*P("o"))/format_o, ["S"]=(prefix_any*P("S"))/format_S, - ["Q"]=(prefix_any*P("Q"))/format_S, + ["Q"]=(prefix_any*P("Q"))/format_Q, ["N"]=(prefix_any*P("N"))/format_N, ["k"]=(prefix_sub*P("k"))/format_k, ["c"]=(prefix_any*P("c"))/format_c, @@ -4181,6 +4421,8 @@ local builder=Cs { "start", ["z"]=(prefix_any*P("z"))/format_z, ["a"]=(prefix_any*P("a"))/format_a, ["A"]=(prefix_any*P("A"))/format_A, + ["<"]=(prefix_any*P("<"))/format_left, + [">"]=(prefix_any*P(">"))/format_right, ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, @@ -4211,7 +4453,7 @@ local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} -if _LUAVERSION<5.2 then +if LUAVERSION<5.2 then function strings.formatters.new(noconcat) local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } setmetatable(t,{ __index=make,__call=use }) @@ -4248,7 +4490,7 @@ patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -if _LUAVERSION<5.2 then +if LUAVERSION<5.2 then add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") @@ -4270,6 +4512,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0) function string.replacenewlines(str) return lpegmatch(pattern,str) end +function strings.newcollector() + local result,r={},0 + return + function(fmt,str,...) + r=r+1 + result[r]=str==nil and fmt or formatters[fmt](str,...) + end, + function(connector) + if result then + local str=concat(result,connector) + result,r={},0 + return str + end + end +end end -- closure @@ -4284,8 +4541,6 @@ if not modules then modules={} end modules ['util-fil']={ } local byte=string.byte local char=string.char -local extract=bit32 and bit32.extract -local floor=math.floor utilities=utilities or {} local files={} utilities.files=files @@ -4302,7 +4557,10 @@ function files.close(f) f:close() end function files.size(f) - return f:seek("end") + local current=f:seek() + local size=f:seek("end") + f:seek("set",current) + return size end files.getsize=files.size function files.setposition(f,n) @@ -4452,7 +4710,7 @@ function files.readfixed4(f) return (0x100*a+b )+(0x100*c+d)/0x10000 end end -if extract then +if bit32 then local extract=bit32.extract local band=bit32.band function files.read2dot14(f) @@ -4472,19 +4730,30 @@ end function files.skiplong(f,n) f:read(4*(n or 1)) end -function files.writecardinal2(f,n) - local a=char(n%256) - n=floor(n/256) - local b=char(n%256) - f:write(b,a) +if bit32 then + local rshift=bit32.rshift + function files.writecardinal2(f,n) + local a=char(n%256) + n=rshift(n,8) + local b=char(n%256) + f:write(b,a) + end +else + local floor=math.floor + function files.writecardinal2(f,n) + local a=char(n%256) + n=floor(n/256) + local b=char(n%256) + f:write(b,a) + end end function files.writecardinal4(f,n) local a=char(n%256) - n=floor(n/256) + n=rshift(n,8) local b=char(n%256) - n=floor(n/256) + n=rshift(n,8) local c=char(n%256) - n=floor(n/256) + n=rshift(n,8) local d=char(n%256) f:write(d,c,b,a) end @@ -4503,6 +4772,8 @@ if fio and fio.readcardinal1 then files.readinteger2=fio.readinteger2 files.readinteger3=fio.readinteger3 files.readinteger4=fio.readinteger4 + files.readfixed2=fio.readfixed2 + files.readfixed4=fio.readfixed4 files.read2dot14=fio.read2dot14 files.setposition=fio.setposition files.getposition=fio.getposition @@ -4962,7 +5233,6 @@ attributes.private=attributes.private or function(name) return number end nodes={} -nodes.pool={} nodes.handlers={} local nodecodes={} local glyphcodes=node.subtypes("glyph") @@ -4983,7 +5253,6 @@ nodes.glyphcodes=glyphcodes nodes.disccodes=disccodes local flush_node=node.flush_node local remove_node=node.remove -local new_node=node.new local traverse_id=node.traverse_id nodes.handlers.protectglyphs=node.protect_glyphs nodes.handlers.unprotectglyphs=node.unprotect_glyphs @@ -5012,11 +5281,6 @@ end function nodes.delete(head,current) return nodes.remove(head,current,true) end -function nodes.pool.kern(k) - local n=new_node("kern",1) - n.kern=k - return n -end local getfield=node.getfield local setfield=node.setfield nodes.getfield=getfield @@ -5088,6 +5352,7 @@ nuts.setchar=direct.setchar nuts.getdisc=direct.getdisc nuts.setdisc=direct.setdisc nuts.setlink=direct.setlink +nuts.setsplit=direct.setsplit nuts.getlist=direct.getlist nuts.setlist=direct.setlist nuts.getoffsets=direct.getoffsets or @@ -5168,16 +5433,9 @@ nuts.traverse_id=direct.traverse_id nuts.traverse_char=direct.traverse_char nuts.ligaturing=direct.ligaturing nuts.kerning=direct.kerning +nuts.new=direct.new nuts.getprop=nuts.getattr nuts.setprop=nuts.setattr -local new_nut=direct.new -nuts.new=new_nut -nuts.pool={} -function nuts.pool.kern(k) - local n=new_nut("kern",1) - setfield(n,"kern",k) - return n -end local propertydata=direct.get_properties_table() nodes.properties={ data=propertydata } direct.set_properties_mode(true,true) @@ -5310,6 +5568,36 @@ function nuts.copy_only_glyphs(current) end return head end +nuts.uses_font=direct.uses_font +if not nuts.uses_font then + local getdisc=nuts.getdisc + local getfont=nuts.getfont + function nuts.uses_font(n,font) + local pre,post,replace=getdisc(n) + if pre then + for n in traverse_id(glyph_code,pre) do + if getfont(n)==font then + return true + end + end + end + if post then + for n in traverse_id(glyph_code,post) do + if getfont(n)==font then + return true + end + end + end + if replace then + for n in traverse_id(glyph_code,replace) do + if getfont(n)==font then + return true + end + end + end + return false + end +end end -- closure @@ -7495,7 +7783,7 @@ if not modules then modules={} end modules ['font-ini']={ local allocate=utilities.storage.allocate fonts=fonts or {} local fonts=fonts -fonts.hashes={ identifiers=allocate() } +fonts.hashes=fonts.hashes or { identifiers=allocate() } fonts.tables=fonts.tables or {} fonts.helpers=fonts.helpers or {} fonts.tracers=fonts.tracers or {} @@ -7504,7 +7792,41 @@ fonts.analyzers={} fonts.readers={} fonts.definers={ methods={} } fonts.loggers={ register=function() end } -fontloader.totable=fontloader.to_table +if context then + fontloader=nil +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-font-mis']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local currentfont=font.current +local hashes=fonts.hashes +local identifiers=hashes.identifiers or {} +local marks=hashes.marks or {} +hashes.identifiers=identifiers +hashes.marks=marks +table.setmetatableindex(marks,function(t,k) + if k==true then + return marks[currentfont()] + else + local resources=identifiers[k].resources or {} + local marks=resources.marks or {} + t[k]=marks + return marks + end +end) end -- closure @@ -7517,12 +7839,14 @@ if not modules then modules={} end modules ['font-con']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local next,tostring,rawget=next,tostring,rawget +local next,tostring,tonumber,rawget=next,tostring,tonumber,rawget local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find local sort,insert,concat=table.sort,table.insert,table.concat local sortedkeys,sortedhash,serialize,fastcopy=table.sortedkeys,table.sortedhash,table.serialize,table.fastcopy local derivetable=table.derive local ioflush=io.flush +local round=math.round +local setmetatable,getmetatable,rawget,rawset=setmetatable,getmetatable,rawget,rawset local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) local report_defining=logs.reporter("fonts","defining") @@ -7568,6 +7892,12 @@ function constructors.scaled(scaledpoints,designsize) return scaledpoints end end +function constructors.getprivate(tfmdata) + local properties=tfmdata.properties + local private=properties.private + properties.private=private+1 + return private +end function constructors.cleanuptable(tfmdata) if constructors.autocleanup and tfmdata.properties.virtualized then for k,v in next,tfmdata.characters do @@ -7644,27 +7974,62 @@ function constructors.trytosharefont(target,tfmdata) end end end +local synonyms={ + exheight="x_height", + xheight="x_height", + ex="x_height", + emwidth="quad", + em="quad", + spacestretch="space_stretch", + stretch="space_stretch", + spaceshrink="space_shrink", + shrink="space_shrink", + extraspace="extra_space", + xspace="extra_space", + slantperpoint="slant", +} function constructors.enhanceparameters(parameters) - local xheight=parameters.x_height - local quad=parameters.quad - local space=parameters.space - local stretch=parameters.space_stretch - local shrink=parameters.space_shrink - local extra=parameters.extra_space - local slant=parameters.slant - parameters.xheight=xheight - parameters.spacestretch=stretch - parameters.spaceshrink=shrink - parameters.extraspace=extra - parameters.em=quad - parameters.ex=xheight - parameters.slantperpoint=slant - parameters.spacing={ - width=space, - stretch=stretch, - shrink=shrink, - extra=extra, - } + local mt=getmetatable(parameters) + local getter=function(t,k) + if not k then + return nil + end + local s=synonyms[k] + if s then + return rawget(t,s) or (mt and mt[s]) or nil + end + if k=="spacing" then + return { + width=t.space, + stretch=t.space_stretch, + shrink=t.space_shrink, + extra=t.extra_space, + } + end + return mt and mt[k] or nil + end + local setter=function(t,k,v) + if not k then + return 0 + end + local s=synonyms[k] + if s then + rawset(t,s,v) + elseif k=="spacing" then + if type(v)=="table" then + rawset(t,"space",v.width or 0) + rawset(t,"space_stretch",v.stretch or 0) + rawset(t,"space_shrink",v.shrink or 0) + rawset(t,"extra_space",v.extra or 0) + end + else + rawset(t,k,v) + end + end + setmetatable(parameters,{ + __index=getter, + __newindex=setter, + }) end local function mathkerns(v,vdelta) local k={} @@ -7683,7 +8048,7 @@ local psfake=0 local function fixedpsname(psname,fallback) local usedname=psname if psname and psname~="" then - if find(psname," ") then + if find(psname," ",1,true) then usedname=gsub(psname,"[%s]+","-") else end @@ -7728,7 +8093,7 @@ function constructors.scale(tfmdata,specification) target.unscaled=tfmdata local mathsize=tonumber(specification.mathsize) or 0 local textsize=tonumber(specification.textsize) or scaledpoints - local forcedsize=tonumber(parameters.mathsize ) or 0 + local forcedsize=tonumber(parameters.mathsize ) or 0 local extrafactor=tonumber(specification.factor ) or 1 if (mathsize==2 or forcedsize==2) and parameters.scriptpercentage then scaledpoints=parameters.scriptpercentage*textsize/100 @@ -7736,6 +8101,7 @@ function constructors.scale(tfmdata,specification) scaledpoints=parameters.scriptscriptpercentage*textsize/100 elseif forcedsize>1000 then scaledpoints=forcedsize + else end targetparameters.mathsize=mathsize targetparameters.textsize=textsize @@ -7746,9 +8112,6 @@ function constructors.scale(tfmdata,specification) local defaultheight=resources.defaultheight or 0 local defaultdepth=resources.defaultdepth or 0 local units=parameters.units or 1000 - if target.fonts then - target.fonts=fastcopy(target.fonts) - end targetproperties.language=properties.language or "dflt" targetproperties.script=properties.script or "dflt" targetproperties.mode=properties.mode or "base" @@ -7790,11 +8153,6 @@ function constructors.scale(tfmdata,specification) target.stretch=expansion.stretch target.shrink=expansion.shrink target.step=expansion.step - target.auto_expand=expansion.auto - end - local protrusion=parameters.protrusion - if protrusion then - target.auto_protrude=protrusion.auto end local extendfactor=parameters.extendfactor or 0 if extendfactor~=0 and extendfactor~=1 then @@ -7816,7 +8174,7 @@ function constructors.scale(tfmdata,specification) targetparameters.units=units targetparameters.scaledpoints=askedscaledpoints local isvirtual=properties.virtualized or tfmdata.type=="virtual" - local hasquality=target.auto_expand or target.auto_protrude + local hasquality=parameters.expansion or parameters.protrusion local hasitalics=properties.hasitalics local autoitalicamount=properties.autoitalicamount local stackmath=not properties.nostackmath @@ -7826,6 +8184,12 @@ function constructors.scale(tfmdata,specification) local realdimensions=properties.realdimensions local writingmode=properties.writingmode or "horizontal" local identity=properties.identity or "horizontal" + local vfonts=target.fonts + if vfonts and #vfonts>0 then + target.fonts=fastcopy(vfonts) + elseif isvirtual then + target.fonts={ { id=0 } } + end if changed and not next(changed) then changed=false end @@ -7901,10 +8265,15 @@ function constructors.scale(tfmdata,specification) local chr,description,index if changed then local c=changed[unicode] - if c then - description=descriptions[c] or descriptions[unicode] or character - character=characters[c] or character - index=description.index or c + if c and c~=unicode then + if c then + description=descriptions[c] or descriptions[unicode] or character + character=characters[c] or character + index=description.index or c + else + description=descriptions[unicode] or character + index=description.index or unicode + end else description=descriptions[unicode] or character index=description.index or unicode @@ -8106,7 +8475,7 @@ function constructors.scale(tfmdata,specification) local ok=false for i=1,#vc do local key=vc[i][1] - if key=="right" or key=="down" then + if key=="right" or key=="down" or key=="rule" then ok=true break end @@ -8138,6 +8507,18 @@ function constructors.scale(tfmdata,specification) properties.setitalics=hasitalics constructors.aftercopyingcharacters(target,tfmdata) constructors.trytosharefont(target,tfmdata) + local vfonts=target.fonts + if isvirtual then + if not vfonts or #vfonts==0 then + target.fonts={ { id=0 } } + end + elseif vfonts then + properties.virtualized=true + target.type="virtual" + if #vfonts==0 then + target.fonts={ { id=0 } } + end + end return target end function constructors.finalize(tfmdata) @@ -8156,15 +8537,9 @@ function constructors.finalize(tfmdata) end if not parameters.expansion then parameters.expansion={ - stretch=tfmdata.stretch or 0, - shrink=tfmdata.shrink or 0, - step=tfmdata.step or 0, - auto=tfmdata.auto_expand or false, - } - end - if not parameters.protrusion then - parameters.protrusion={ - auto=auto_protrude + stretch=tfmdata.stretch or 0, + shrink=tfmdata.shrink or 0, + step=tfmdata.step or 0, } end if not parameters.size then @@ -8250,8 +8625,6 @@ function constructors.finalize(tfmdata) tfmdata.stretch=nil tfmdata.shrink=nil tfmdata.step=nil - tfmdata.auto_expand=nil - tfmdata.auto_protrude=nil tfmdata.extend=nil tfmdata.slant=nil tfmdata.units=nil @@ -8307,13 +8680,15 @@ function constructors.hashinstance(specification,force) specification.hash=hash end if size<1000 and designsizes[hash] then - size=math.round(constructors.scaled(size,designsizes[hash])) - specification.size=size + size=round(constructors.scaled(size,designsizes[hash])) + else + size=round(size) end + specification.size=size if fallbacks then - return hash..' @ '..tostring(size)..' @ '..fallbacks + return hash..' @ '..size..' @ '..fallbacks else - return hash..' @ '..tostring(size) + return hash..' @ '..size end end function constructors.setname(tfmdata,specification) @@ -8494,10 +8869,12 @@ do local handler=handlers[format] local enhancers=handler.enhancers if not enhancers then - local actions=allocate() + local actions=allocate() local before=allocate() local after=allocate() local order=allocate() + local known={} + local nofsteps=0 local patches={ before=before,after=after } local trace=false local report=logs.reporter("fonts",format.." enhancing") @@ -8519,7 +8896,7 @@ do report("%s enhancing file %a","start",filename) end ioflush() - for e=1,#order do + for e=1,nofsteps do local enhancer=order[e] local b=before[enhancer] if b then @@ -8529,7 +8906,7 @@ do end end end - enhance(enhancer,data,filename,raw) + enhance(enhancer,data,filename,raw) local a=after[enhancer] if a then for pattern,action in next,a do @@ -8549,7 +8926,9 @@ do if action then if actions[what] then else - order[#order+1]=what + nofsteps=nofsteps+1 + order[nofsteps]=what + known[what]=nofsteps end actions[what]=action else @@ -8563,7 +8942,12 @@ do if ww then ww[pattern]=action else - pw[where]={ [pattern]=action} + pw[where]={ [pattern]=action } + if not known[where] then + nofsteps=nofsteps+1 + order[nofsteps]=where + known[where]=nofsteps + end end end end @@ -8571,7 +8955,11 @@ do register=register, apply=apply, patch=patch, - patches={ register=patch }, + report=report, + patches={ + register=patch, + report=report, + }, } handler.enhancers=enhancers end @@ -8797,7 +9185,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-cid']={ version=1.001, - comment="companion to font-otf.lua (cidmaps)", + comment="companion to font-ini.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" @@ -8958,9 +9346,9 @@ if not modules then modules={} end modules ['font-map']={ local tonumber,next,type=tonumber,next,type local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower local P,R,S,C,Ct,Cc,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.match -local floor=math.floor local formatters=string.formatters local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys +local rshift=bit32.rshift local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end) local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_mapping=v end) local report_fonts=logs.reporter("fonts","loading") @@ -8998,7 +9386,7 @@ local function tounicode16(unicode) return f_single(unicode) else unicode=unicode-0x10000 - return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00) + return f_double(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) end end local function tounicode16sequence(unicodes) @@ -9009,12 +9397,12 @@ local function tounicode16sequence(unicodes) t[l]=f_single(u) else u=u-0x10000 - t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00) + t[l]=f_double(rshift(u,10)+0xD800,u%1024+0xDC00) end end return concat(t) end -local function tounicode(unicode,name) +local function tounicode(unicode) if type(unicode)=="table" then local t={} for l=1,#unicode do @@ -9023,7 +9411,7 @@ local function tounicode(unicode,name) t[l]=f_single(u) else u=u-0x10000 - t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00) + t[l]=f_double(rshift(u,10)+0xD800,u%1024+0xDC00) end end return concat(t) @@ -9032,7 +9420,7 @@ local function tounicode(unicode,name) return f_single(unicode) else unicode=unicode-0x10000 - return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00) + return f_double(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) end end end @@ -9053,7 +9441,7 @@ local ligseparator=P("_") local varseparator=P(".") local namesplitter=Ct(C((1-ligseparator-varseparator)^1)*(ligseparator*C((1-ligseparator-varseparator)^1))^0) do - local overloads=allocate { + local overloads={ IJ={ name="I_J",unicode={ 0x49,0x4A },mess=0x0132 }, ij={ name="i_j",unicode={ 0x69,0x6A },mess=0x0133 }, ff={ name="f_f",unicode={ 0x66,0x66 },mess=0xFB00 }, @@ -9064,7 +9452,7 @@ do fj={ name="f_j",unicode={ 0x66,0x6A } }, fk={ name="f_k",unicode={ 0x66,0x6B } }, } - local o={} + local o=allocate {} for k,v in next,overloads do local name=v.name local mess=v.mess @@ -9554,11 +9942,12 @@ if not modules then modules={} end modules ['font-otr']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local next,type=next,type +local next,type,tonumber=next,type,tonumber local byte,lower,char,gsub=string.byte,string.lower,string.char,string.gsub local floor,round=math.floor,math.round local P,R,S,C,Cs,Cc,Ct,Carg,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Carg,lpeg.Cmt local lpegmatch=lpeg.match +local rshift=bit32.rshift local setmetatableindex=table.setmetatableindex local formatters=string.formatters local sortedkeys=table.sortedkeys @@ -10319,7 +10708,7 @@ formatreaders[4]=function(f,fontdata,offset) for i=1,nofsegments do offsets[i]=readushort(f) end - local size=(length-2*2-5*2-4*nofsegments*2)/2 + local size=(length-2*2-5*2-4*2*nofsegments)/2 for i=1,size-1 do indices[i]=readushort(f) end @@ -10721,7 +11110,7 @@ function readers.kern(f,fontdata,specification) local version=readushort(f) local length=readushort(f) local coverage=readushort(f) - local format=bit32.rshift(coverage,8) + local format=rshift(coverage,8) if format==0 then local nofpairs=readushort(f) local searchrange=readushort(f) @@ -11003,9 +11392,6 @@ local function readdata(f,offset,specification) if factors then specification.factors=factors fontdata.factors=factors - report("factors: % t",factors) - else - report("bad factors") end else end @@ -13179,7 +13565,7 @@ if not modules then modules={} end modules ['font-ttf']={ license="see context related readme files" } local next,type,unpack=next,type,unpack -local bittest,band,rshift=bit32.btest,bit32.band,bit32.rshift +local band,rshift=bit32.band,bit32.rshift local sqrt,round=math.sqrt,math.round local char=string.char local concat=table.concat @@ -13771,7 +14157,7 @@ local function readglyph(f,nofcontours) while i<=nofpoints do local flag=readbyte(f) flags[i]=flag - if bittest(flag,0x08) then + if band(flag,0x08)~=0 then for j=1,readbyte(f) do i=i+1 flags[i]=flag @@ -13782,8 +14168,8 @@ local function readglyph(f,nofcontours) local x=0 for i=1,nofpoints do local flag=flags[i] - local short=bittest(flag,0x02) - local same=bittest(flag,0x10) + local short=band(flag,0x02)~=0 + local same=band(flag,0x10)~=0 if short then if same then x=x+readbyte(f) @@ -13794,13 +14180,13 @@ local function readglyph(f,nofcontours) else x=x+readshort(f) end - points[i]={ x,0,bittest(flag,0x01) } + points[i]={ x,0,band(flag,0x01)~=0 } end local y=0 for i=1,nofpoints do local flag=flags[i] - local short=bittest(flag,0x04) - local same=bittest(flag,0x20) + local short=band(flag,0x04)~=0 + local same=band(flag,0x20)~=0 if short then if same then y=y+readbyte(f) @@ -13827,8 +14213,8 @@ local function readcomposite(f) while true do local flags=readushort(f) local index=readushort(f) - local f_xyarg=bittest(flags,0x0002) - local f_offset=bittest(flags,0x0800) + local f_xyarg=band(flags,0x0002)~=0 + local f_offset=band(flags,0x0800)~=0 local xscale=1 local xrotate=0 local yrotate=0 @@ -13838,7 +14224,7 @@ local function readcomposite(f) local base=false local reference=false if f_xyarg then - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then xoffset=readshort(f) yoffset=readshort(f) else @@ -13846,7 +14232,7 @@ local function readcomposite(f) yoffset=readchar(f) end else - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then base=readshort(f) reference=readshort(f) else @@ -13854,21 +14240,21 @@ local function readcomposite(f) reference=readchar(f) end end - if bittest(flags,0x0008) then + if band(flags,0x0008)~=0 then xscale=read2dot14(f) yscale=xscale if f_xyarg and f_offset then xoffset=xoffset*xscale yoffset=yoffset*yscale end - elseif bittest(flags,0x0040) then + elseif band(flags,0x0040)~=0 then xscale=read2dot14(f) yscale=read2dot14(f) if f_xyarg and f_offset then xoffset=xoffset*xscale yoffset=yoffset*yscale end - elseif bittest(flags,0x0080) then + elseif band(flags,0x0080)~=0 then xscale=read2dot14(f) xrotate=read2dot14(f) yrotate=read2dot14(f) @@ -13881,16 +14267,16 @@ local function readcomposite(f) nofcomponents=nofcomponents+1 components[nofcomponents]={ index=index, - usemine=bittest(flags,0x0200), - round=bittest(flags,0x0006), + usemine=band(flags,0x0200)~=0, + round=band(flags,0x0006)~=0, base=base, reference=reference, matrix={ xscale,xrotate,yrotate,yscale,xoffset,yoffset }, } - if bittest(flags,0x0100) then + if band(flags,0x0100)~=0 then instructions=true end - if not bittest(flags,0x0020) then + if not band(flags,0x0020)~=0 then break end end @@ -13997,7 +14383,7 @@ local function readpoints(f) return nil,0 else if count<128 then - elseif bittest(count,0x80) then + elseif band(count,0x80)~=0 then count=band(count,0x7F)*256+readbyte(f) else end @@ -14006,7 +14392,7 @@ local function readpoints(f) local n=1 while p<count do local control=readbyte(f) - local runreader=bittest(control,0x80) and readushort or readbyte + local runreader=band(control,0x80)~=0 and readushort or readbyte local runlength=band(control,0x7F) for i=1,runlength+1 do n=n+runreader(f) @@ -14026,12 +14412,12 @@ local function readdeltas(f,nofpoints) if not control then break end - local allzero=bittest(control,0x80) + local allzero=band(control,0x80)~=0 local runlength=band(control,0x3F)+1 if allzero then z=z+runlength else - local runreader=bittest(control,0x40) and readshort or readinteger + local runreader=band(control,0x40)~=0 and readshort or readinteger if z>0 then for i=1,z do p=p+1 @@ -14057,7 +14443,7 @@ local function readdeltas(f,nofpoints) while nofpoints>0 do local control=readbyte(f) if control then - local allzero=bittest(control,0x80) + local allzero=band(control,0x80)~=0 local runlength=band(control,0x3F)+1 if allzero then for i=1,runlength do @@ -14065,7 +14451,7 @@ local function readdeltas(f,nofpoints) deltas[p]=0 end else - local runreader=bittest(control,0x40) and readshort or readinteger + local runreader=band(control,0x40)~=0 and readshort or readinteger for i=1,runlength do p=p+1 deltas[p]=runreader(f) @@ -14103,7 +14489,7 @@ function readers.gvar(f,fontdata,specification,glyphdata,shapedata) local tuples={} local glyphdata=fontdata.glyphs local dowidth=not fontdata.variabledata.hvarwidths - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then for i=1,nofglyphs+1 do data[i]=dataoffset+readulong(f) end @@ -14144,7 +14530,7 @@ function readers.gvar(f,fontdata,specification,glyphdata,shapedata) local allpoints=(shape.nofpoints or 0) local shared=false local nofshared=0 - if bittest(flags,0x8000) then + if band(flags,0x8000)~=0 then local current=getposition(f) setposition(f,offset) shared,nofshared=readpoints(f) @@ -14155,9 +14541,9 @@ function readers.gvar(f,fontdata,specification,glyphdata,shapedata) local size=readushort(f) local flags=readushort(f) local index=band(flags,0x0FFF) - local haspeak=bittest(flags,0x8000) - local intermediate=bittest(flags,0x4000) - local private=bittest(flags,0x2000) + local haspeak=band(flags,0x8000)~=0 + local intermediate=band(flags,0x4000)~=0 + local private=band(flags,0x2000)~=0 local peak=nil local start=nil local stop=nil @@ -14252,7 +14638,6 @@ if not modules then modules={} end modules ['font-dsp']={ license="see context related readme files" } local next,type=next,type -local bittest=bit32.btest local band=bit32.band local extract=bit32.extract local bor=bit32.bor @@ -14401,10 +14786,10 @@ local lookupnames={ } local lookupflags=setmetatableindex(function(t,k) local v={ - bittest(k,0x0008) and true or false, - bittest(k,0x0004) and true or false, - bittest(k,0x0002) and true or false, - bittest(k,0x0001) and true or false, + band(k,0x0008)~=0 and true or false, + band(k,0x0004)~=0 and true or false, + band(k,0x0002)~=0 and true or false, + band(k,0x0001)~=0 and true or false, } t[k]=v return v @@ -14737,12 +15122,12 @@ local function readvariation(f,offset) end local function readposition(f,format,mainoffset,getdelta) if format==0 then - return + return false end if format==0x04 then local h=readshort(f) if h==0 then - return + return true else return { 0,0,h,0 } end @@ -14751,7 +15136,7 @@ local function readposition(f,format,mainoffset,getdelta) local x=readshort(f) local h=readshort(f) if x==0 and h==0 then - return + return true else return { x,0,h,0 } end @@ -14770,20 +15155,20 @@ local function readposition(f,format,mainoffset,getdelta) skipshort(f,1) end if h==0 then - return + return true else return { 0,0,h,0 } end end - local x=bittest(format,0x01) and readshort(f) or 0 - local y=bittest(format,0x02) and readshort(f) or 0 - local h=bittest(format,0x04) and readshort(f) or 0 - local v=bittest(format,0x08) and readshort(f) or 0 + local x=band(format,0x1)~=0 and readshort(f) or 0 + local y=band(format,0x2)~=0 and readshort(f) or 0 + local h=band(format,0x4)~=0 and readshort(f) or 0 + local v=band(format,0x8)~=0 and readshort(f) or 0 if format>=0x10 then - local X=bittest(format,0x10) and skipshort(f) or 0 - local Y=bittest(format,0x20) and skipshort(f) or 0 - local H=bittest(format,0x40) and skipshort(f) or 0 - local V=bittest(format,0x80) and skipshort(f) or 0 + local X=band(format,0x10)~=0 and skipshort(f) or 0 + local Y=band(format,0x20)~=0 and skipshort(f) or 0 + local H=band(format,0x40)~=0 and skipshort(f) or 0 + local V=band(format,0x80)~=0 and skipshort(f) or 0 local s=skips[extract(format,4,4)] if s>0 then skipshort(f,s) @@ -14816,7 +15201,7 @@ local function readposition(f,format,mainoffset,getdelta) end return { x,y,h,v } elseif x==0 and y==0 and h==0 and v==0 then - return + return true else return { x,y,h,v } end @@ -15365,12 +15750,14 @@ function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffs before=readcoveragearray(f,tableoffset,before,true) after=readcoveragearray(f,tableoffset,after,true) return { - coverage={ - format="reversecoverage", - before=before, - current=current, - after=after, - replacements=replacements, + format="reversecoverage", + rules={ + { + before=before, + current=current, + after=after, + replacements=replacements, + } } },"reversechainedcontextsingle" else @@ -15428,10 +15815,10 @@ function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg local value=readposition(f,format,tableoffset,getdelta) local coverage=readcoverage(f,tableoffset+coverage) for index,newindex in next,coverage do - coverage[index]=value + coverage[index]=value end return { - format="pair", + format="single", coverage=coverage, } elseif subtype==2 then @@ -15447,7 +15834,7 @@ function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg coverage[index]=values[newindex+1] end return { - format="pair", + format="single", coverage=coverage, } else @@ -15476,9 +15863,9 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first=value[2] local second=value[3] if first or second then - hash[other]={ first,second } + hash[other]={ first,second or nil } else - hash[other]=nil + hash[other]=nil end end end @@ -15512,7 +15899,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first=offsets[1] local second=offsets[2] if first or second then - hash[paired]={ first,second } + hash[paired]={ first,second or nil } else end end @@ -15544,17 +15931,20 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof local entry=readushort(f) local exit=readushort(f) records[i]={ - entry=entry~=0 and (tableoffset+entry) or false, - exit=exit~=0 and (tableoffset+exit ) or false, + entry~=0 and (tableoffset+entry) or false, + exit~=0 and (tableoffset+exit ) or nil, } end + local cc=(fontdata.temporary.cursivecount or 0)+1 + fontdata.temporary.cursivecount=cc + cc="cc-"..cc coverage=readcoverage(f,coverage) for i=1,nofrecords do local r=records[i] records[i]={ - 1, - readanchor(f,r.entry,getdelta) or nil, - readanchor(f,r.exit,getdelta) or nil, + cc, + readanchor(f,r[1],getdelta) or false, + readanchor(f,r[2],getdelta) or nil, } end for index,newindex in next,coverage do @@ -15918,7 +16308,7 @@ do for j=1,nofsubtables do subtables[j]=offset+readushort(f) end - local markclass=bittest(flagbits,0x0010) + local markclass=band(flagbits,0x0010)~=0 if markclass then markclass=readushort(f) end @@ -15940,7 +16330,7 @@ do end local f_lookupname=formatters["%s_%s_%s"] local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset) - local sequences=fontdata.sequences or {} + local sequences=fontdata.sequences or {} local sublookuplist=fontdata.sublookups or {} fontdata.sequences=sequences fontdata.sublookups=sublookuplist @@ -15955,6 +16345,7 @@ do local nofglyphs=fontdata.nofglyphs or #glyphs local noflookups=#lookups local lookupprefix=sub(what,2,2) + local usedlookups=false for lookupid=1,noflookups do local lookup=lookups[lookupid] local lookuptype=lookup.type @@ -15994,6 +16385,7 @@ do local before=rule.before local current=rule.current local after=rule.after + local replacements=rule.replacements if before then for i=1,#before do before[i]=tohash(before[i]) @@ -16001,15 +16393,41 @@ do rule.before=reversed(before) end if current then - for i=1,#current do - current[i]=tohash(current[i]) + if replacements then + local first=current[1] + local hash={} + local repl={} + for i=1,#first do + local c=first[i] + hash[c]=true + repl[c]=replacements[i] + end + rule.current={ hash } + rule.replacements=repl + else + for i=1,#current do + current[i]=tohash(current[i]) + end end + else end if after then for i=1,#after do after[i]=tohash(after[i]) end end + if usedlookups then + local lookups=rule.lookups + if lookups then + for k,v in next,lookups do + if v then + for k,v in next,v do + usedlookups[v]=usedlookups[v]+1 + end + end + end + end + end end end end @@ -16056,6 +16474,9 @@ do report("no handler for lookup %a with type %a",lookupid,lookuptype) end end + if usedlookups then + report("used %s lookups: % t",what,sortedkeys(usedlookups)) + end local reported={} local function report_issue(i,what,sequence,kind) local name=sequence.name @@ -16097,7 +16518,7 @@ do local d=lookup.done if d then nofsublookups=nofsublookups+1 - h={ + local l={ index=nofsublookups, name=f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), derived=true, @@ -16107,7 +16528,7 @@ do markclass=d.markclass or nil, flags=d.flags, } - sublookuplist[nofsublookups]=copy(h) + sublookuplist[nofsublookups]=copy(l) sublookuphash[lookupid]=nofsublookups sublookupcheck[lookupid]=1 h=nofsublookups @@ -16281,7 +16702,7 @@ do local version=readushort(f) local length=readushort(f) local coverage=readushort(f) - local format=bit32.rshift(coverage,8) + local format=rshift(coverage,8) if format==0 then local nofpairs=readushort(f) local searchrange=readushort(f) @@ -16350,12 +16771,12 @@ function readers.gdef(f,fontdata,specification) local tableoffset=datatable.offset setposition(f,tableoffset) local version=readulong(f) - local classoffset=tableoffset+readushort(f) - local attachmentoffset=tableoffset+readushort(f) - local ligaturecarets=tableoffset+readushort(f) - local markclassoffset=tableoffset+readushort(f) - local marksetsoffset=version>=0x00010002 and (tableoffset+readushort(f)) - local varsetsoffset=version>=0x00010003 and (tableoffset+readulong(f)) + local classoffset=readushort(f) + local attachmentoffset=readushort(f) + local ligaturecarets=readushort(f) + local markclassoffset=readushort(f) + local marksetsoffset=version>=0x00010002 and readushort(f) or 0 + local varsetsoffset=version>=0x00010003 and readulong(f) or 0 local glyphs=fontdata.glyphs local marks={} local markclasses=setmetatableindex("table") @@ -16363,54 +16784,59 @@ function readers.gdef(f,fontdata,specification) fontdata.marks=marks fontdata.markclasses=markclasses fontdata.marksets=marksets - setposition(f,classoffset) - local classformat=readushort(f) - if classformat==1 then - local firstindex=readushort(f) - local lastindex=firstindex+readushort(f)-1 - for index=firstindex,lastindex do - local class=classes[readushort(f)] - if class=="mark" then - marks[index]=true - end - glyphs[index].class=class - end - elseif classformat==2 then - local nofranges=readushort(f) - for i=1,nofranges do + if classoffset~=0 then + setposition(f,tableoffset+classoffset) + local classformat=readushort(f) + if classformat==1 then local firstindex=readushort(f) - local lastindex=readushort(f) - local class=classes[readushort(f)] - if class then - for index=firstindex,lastindex do - glyphs[index].class=class - if class=="mark" then - marks[index]=true + local lastindex=firstindex+readushort(f)-1 + for index=firstindex,lastindex do + local class=classes[readushort(f)] + if class=="mark" then + marks[index]=true + end + glyphs[index].class=class + end + elseif classformat==2 then + local nofranges=readushort(f) + for i=1,nofranges do + local firstindex=readushort(f) + local lastindex=readushort(f) + local class=classes[readushort(f)] + if class then + for index=firstindex,lastindex do + glyphs[index].class=class + if class=="mark" then + marks[index]=true + end end end end end end - setposition(f,markclassoffset) - local classformat=readushort(f) - if classformat==1 then - local firstindex=readushort(f) - local lastindex=firstindex+readushort(f)-1 - for index=firstindex,lastindex do - markclasses[readushort(f)][index]=true - end - elseif classformat==2 then - local nofranges=readushort(f) - for i=1,nofranges do + if markclassoffset~=0 then + setposition(f,tableoffset+markclassoffset) + local classformat=readushort(f) + if classformat==1 then local firstindex=readushort(f) - local lastindex=readushort(f) - local class=markclasses[readushort(f)] + local lastindex=firstindex+readushort(f)-1 for index=firstindex,lastindex do - class[index]=true + markclasses[readushort(f)][index]=true + end + elseif classformat==2 then + local nofranges=readushort(f) + for i=1,nofranges do + local firstindex=readushort(f) + local lastindex=readushort(f) + local class=markclasses[readushort(f)] + for index=firstindex,lastindex do + class[index]=true + end end end end - if marksetsoffset and marksetsoffset>tableoffset then + if marksetsoffset~=0 then + marksetsoffset=tableoffset+marksetsoffset setposition(f,marksetsoffset) local format=readushort(f) if format==1 then @@ -16428,8 +16854,8 @@ function readers.gdef(f,fontdata,specification) end end local factors=specification.factors - if (specification.variable or factors) and varsetsoffset and varsetsoffset>tableoffset then - local regions,deltas=readvariationdata(f,varsetsoffset,factors) + if (specification.variable or factors) and varsetsoffset~=0 then + local regions,deltas=readvariationdata(f,tableoffset+varsetsoffset,factors) if factors then fontdata.temporary.getdelta=function(outer,inner) local delta=deltas[outer+1] @@ -16696,7 +17122,7 @@ local function readmathvariants(f,fontdata,offset) advance=readushort(f), } local flags=readushort(f) - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then p.extender=1 end parts[i]=p @@ -16937,9 +17363,10 @@ function readers.stat(f,fontdata,specification) local values={} setposition(f,tableoffset+axisoffset) for i=1,nofaxis do + local tag=readtag(f) axis[i]={ - tag=readtag(f), - name=lower(extras[readushort(f)]), + tag=tag, + name=lower(extras[readushort(f)] or tag), ordering=readushort(f), variants={} } @@ -16953,7 +17380,7 @@ function readers.stat(f,fontdata,specification) local format=readushort(f) local index=readushort(f)+1 local flags=readushort(f) - local name=lower(extras[readushort(f)]) + local name=lower(extras[readushort(f)] or "no name") local value=readfixed(f) local variant if format==1 then @@ -17240,6 +17667,7 @@ local formatters=string.formatters local sortedkeys=table.sortedkeys local sortedhash=table.sortedhash local tohash=table.tohash +local setmetatableindex=table.setmetatableindex local report=logs.reporter("otf reader") local trace_markwidth=false trackers.register("otf.markwidth",function(v) trace_markwidth=v end) local readers=fonts.handlers.otf.readers @@ -17250,7 +17678,7 @@ local f_index=formatters["I%05X"] local f_character_y=formatters["%C"] local f_character_n=formatters["[ %C ]"] local check_duplicates=true -local check_soft_hyphen=false +local check_soft_hyphen=true directives.register("otf.checksofthyphen",function(v) check_soft_hyphen=v end) @@ -17800,7 +18228,7 @@ local function checklookups(fontdata,missing,nofmissing) end end if next(done) then - report("not unicoded: % t",table.sortedkeys(done)) + report("not unicoded: % t",sortedkeys(done)) end end end @@ -17810,7 +18238,6 @@ local function unifymissing(fontdata) require("font-agl") end local unicodes={} - local private=fontdata.private local resources=fontdata.resources resources.unicodes=unicodes for unicode,d in next,fontdata.descriptions do @@ -18189,13 +18616,14 @@ function readers.pack(data) return nt end end - local function pack_flat(v) - local tag=tabstr_flat(v) + local function pack_normal_cc(v) + local tag=tabstr_normal(v) local ht=h[tag] if ht then c[ht]=c[ht]+1 return ht else + v[1]=0 nt=nt+1 t[nt]=v h[tag]=nt @@ -18203,8 +18631,8 @@ function readers.pack(data) return nt end end - local function pack_boolean(v) - local tag=tabstr_boolean(v) + local function pack_flat(v) + local tag=tabstr_flat(v) local ht=h[tag] if ht then c[ht]=c[ht]+1 @@ -18245,6 +18673,20 @@ function readers.pack(data) return nt end end + local function pack_boolean(v) + local tag=tabstr_boolean(v) + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + nt=nt+1 + t[nt]=v + h[tag]=nt + c[nt]=1 + return nt + end + end local function pack_final(v) if c[v]<=criterium then return t[v] @@ -18261,6 +18703,22 @@ function readers.pack(data) end end end + local function pack_final_cc(v) + if c[v]<=criterium then + return t[v] + else + local hv=hh[v] + if hv then + return hv + else + ntt=ntt+1 + tt[ntt]=t[v] + hh[v]=ntt + cc[ntt]=c[v] + return ntt + end + end + end local function success(stage,pass) if nt==0 then if trace_loading or trace_packing then @@ -18306,9 +18764,9 @@ function readers.pack(data) end local function packers(pass) if pass==1 then - return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed + return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc else - return pack_final,pack_final,pack_final,pack_final,pack_final + return pack_final,pack_final,pack_final,pack_final,pack_final,pack_final_cc end end local resources=data.resources @@ -18326,7 +18784,7 @@ function readers.pack(data) if trace_packing then report_otf("start packing: stage 1, pass %s",pass) end - local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass) for unicode,description in next,descriptions do local boundingbox=description.boundingbox if boundingbox then @@ -18356,28 +18814,30 @@ function readers.pack(data) if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then + if step.format=="pair" then for g1,d1 in next,c do - c[g1]=pack_normal(d1) + for g2,d2 in next,d1 do + local f=d2[1] if f and f~=true then d2[1]=pack_indexed(f) end + local s=d2[2] if s and s~=true then d2[2]=pack_indexed(s) end + end end else for g1,d1 in next,c do - for g2,d2 in next,d1 do - local f=d2[1] if f then d2[1]=pack_indexed(f) end - local s=d2[2] if s then d2[2]=pack_indexed(s) end - end + c[g1]=pack_normal(d1) end end end elseif kind=="gpos_single" then local c=step.coverage if c then - if step.format=="kern" then - step.coverage=pack_normal(c) - else + if step.format=="single" then for g1,d1 in next,c do - c[g1]=pack_indexed(d1) + if d1 and d1~=true then + c[g1]=pack_indexed(d1) + end end + else + step.coverage=pack_normal(c) end end elseif kind=="gpos_cursive" then @@ -18484,7 +18944,6 @@ function readers.pack(data) for i=1,#deltas do local di=deltas[i] local d=di.deltas - local r=di.regions for j=1,#d do d[j]=pack_indexed(d[j]) end @@ -18516,7 +18975,7 @@ function readers.pack(data) if trace_packing then report_otf("start packing: stage 2, pass %s",pass) end - local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass) for unicode,description in next,descriptions do local math=description.math if math then @@ -18538,8 +18997,7 @@ function readers.pack(data) if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then - else + if step.format=="pair" then for g1,d1 in next,c do for g2,d2 in next,d1 do d1[g2]=pack_normal(d2) @@ -18547,6 +19005,15 @@ function readers.pack(data) end end end + elseif kind=="gpos_mark2ligature" then + local c=step.baseclasses + if c then + for g1,d1 in next,c do + for g2,d2 in next,d1 do + d1[g2]=pack_normal(d2) + end + end + end end local rules=step.rules if rules then @@ -18589,7 +19056,7 @@ function readers.pack(data) if trace_packing then report_otf("start packing: stage 3, pass %s",pass) end - local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass) local function packthem(sequences) for i=1,#sequences do local sequence=sequences[i] @@ -18602,13 +19069,19 @@ function readers.pack(data) if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then - else + if step.format=="pair" then for g1,d1 in next,c do c[g1]=pack_normal(d1) end end end + elseif kind=="gpos_cursive" then + local c=step.coverage + if c then + for g1,d1 in next,c do + c[g1]=pack_normal_cc(d1) + end + end end end end @@ -18677,20 +19150,26 @@ function readers.unpack(data) local features=sequence.features local flags=sequence.flags local markclass=sequence.markclass + if features then + local tv=tables[features] + if tv then + sequence.features=tv + features=tv + end + for script,feature in next,features do + local tv=tables[feature] + if tv then + features[script]=tv + end + end + end if steps then for i=1,#steps do local step=steps[i] if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then - for g1,d1 in next,c do - local tv=tables[d1] - if tv then - c[g1]=tv - end - end - else + if step.format=="pair" then for g1,d1 in next,c do local tv=tables[d1] if tv then @@ -18707,29 +19186,41 @@ function readers.unpack(data) local s=tables[d2[2]] if s then d2[2]=s end end end + else + for g1,d1 in next,c do + local tv=tables[d1] + if tv then + c[g1]=tv + end + end end end elseif kind=="gpos_single" then local c=step.coverage if c then - if step.format=="kern" then - local tv=tables[c] - if tv then - step.coverage=tv - end - else + if step.format=="single" then for g1,d1 in next,c do local tv=tables[d1] if tv then c[g1]=tv end end + else + local tv=tables[c] + if tv then + step.coverage=tv + end end end elseif kind=="gpos_cursive" then local c=step.coverage if c then for g1,d1 in next,c do + local tv=tables[d1] + if tv then + d1=tv + c[g1]=d1 + end local f=tables[d1[2]] if f then d1[2]=f end local s=tables[d1[3]] if s then d1[3]=s end end @@ -18760,6 +19251,11 @@ function readers.unpack(data) if c then for g1,d1 in next,c do for g2,d2 in next,d1 do + local tv=tables[d2] + if tv then + d2=tv + d1[g2]=d2 + end for g3,d3 in next,d2 do local tv=tables[d2[g3]] if tv then @@ -18836,19 +19332,6 @@ function readers.unpack(data) end end end - if features then - local tv=tables[features] - if tv then - sequence.features=tv - features=tv - end - for script,feature in next,features do - local tv=tables[feature] - if tv then - features[script]=tv - end - end - end if order then local tv=tables[order] if tv then @@ -18996,7 +19479,7 @@ local function mergesteps_1(lookup,strict) lookup.steps={ first } return nofsteps-1 end -local function mergesteps_2(lookup,strict) +local function mergesteps_2(lookup) local steps=lookup.steps local nofsteps=lookup.nofsteps local first=steps[1] @@ -19015,9 +19498,9 @@ local function mergesteps_2(lookup,strict) for k,v in next,steps[i].coverage do local tk=target[k] if tk then - for k,v in next,v do - if not tk[k] then - tk[k]=v + for kk,vv in next,v do + if tk[kk]==nil then + tk[kk]=vv end end else @@ -19026,55 +19509,42 @@ local function mergesteps_2(lookup,strict) end end lookup.nofsteps=1 + lookup.merged=true lookup.steps={ first } return nofsteps-1 end local function mergesteps_3(lookup,strict) local steps=lookup.steps local nofsteps=lookup.nofsteps - local first=steps[1] report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name) - local baseclasses={} local coverage={} - local used={} for i=1,nofsteps do - local offset=i*10 - local step=steps[i] - for k,v in sortedhash(step.baseclasses) do - baseclasses[offset+k]=v - end - for k,v in next,step.coverage do - local tk=coverage[k] + for k,v in next,steps[i].coverage do + local tk=coverage[k] if tk then - for k,v in next,v do - if not tk[k] then - tk[k]=v - local c=offset+v[1] - v[1]=c - if not used[c] then - used[c]=true - end - end - end + report("quitting merge due to multiple checks") + return nofsteps else coverage[k]=v - local c=offset+v[1] - v[1]=c - if not used[c] then - used[c]=true - end end end end - for k,v in next,baseclasses do - if not used[k] then - baseclasses[k]=nil - report("discarding not used baseclass %i",k) + local first=steps[1] + local baseclasses={} + for i=1,nofsteps do + local offset=i*10 + local step=steps[i] + for k,v in sortedhash(step.baseclasses) do + baseclasses[offset+k]=v + end + for k,v in next,step.coverage do + v[1]=offset+v[1] end end first.baseclasses=baseclasses first.coverage=coverage lookup.nofsteps=1 + lookup.merged=true lookup.steps={ first } return nofsteps-1 end @@ -19114,67 +19584,136 @@ local function mergesteps_4(lookup) lookup.steps={ first } return nofsteps-1 end +local function mergesteps_5(lookup) + local steps=lookup.steps + local nofsteps=lookup.nofsteps + local first=steps[1] + report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name) + local target=first.coverage + local hash=nil + for k,v in next,target do + hash=v[1] + break + end + for i=2,nofsteps do + for k,v in next,steps[i].coverage do + local tk=target[k] + if tk then + if not tk[2] then + tk[2]=v[2] + end + if not tk[3] then + tk[3]=v[3] + end + else + target[k]=v + v[1]=hash + end + end + end + lookup.nofsteps=1 + lookup.merged=true + lookup.steps={ first } + return nofsteps-1 +end local function checkkerns(lookup) local steps=lookup.steps local nofsteps=lookup.nofsteps + local kerned=0 for i=1,nofsteps do local step=steps[i] if step.format=="pair" then local coverage=step.coverage local kerns=true for g1,d1 in next,coverage do - if d1[1]~=0 or d1[2]~=0 or d1[4]~=0 then + if d1==true then + elseif not d1 then + elseif d1[1]~=0 or d1[2]~=0 or d1[4]~=0 then kerns=false break end end if kerns then report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name) + local c={} for g1,d1 in next,coverage do - coverage[g1]=d1[3] + if d1 and d1~=true then + c[g1]=d1[3] + end end - step.format="kern" + step.coverage=c + step.format="move" + kerned=kerned+1 end end end + return kerned end local function checkpairs(lookup) local steps=lookup.steps local nofsteps=lookup.nofsteps local kerned=0 - for i=1,nofsteps do - local step=steps[i] - if step.format=="pair" then - local coverage=step.coverage - local kerns=true - for g1,d1 in next,coverage do - for g2,d2 in next,d1 do - if d2[2] then - kerns=false - break - else - local v=d2[1] - if v[1]~=0 or v[2]~=0 or v[4]~=0 then - kerns=false - break - end + local function onlykerns(step) + local coverage=step.coverage + for g1,d1 in next,coverage do + for g2,d2 in next,d1 do + if d2[2] then + return false + else + local v=d2[1] + if v==true then + elseif v and (v[1]~=0 or v[2]~=0 or v[4]~=0) then + return false end end end - if kerns then + end + return coverage + end + for i=1,nofsteps do + local step=steps[i] + if step.format=="pair" then + local coverage=onlykerns(step) + if coverage then report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name) for g1,d1 in next,coverage do + local d={} for g2,d2 in next,d1 do - d1[g2]=d2[1][3] + local v=d2[1] + if v==true then + elseif v then + d[g2]=v[3] + end end + coverage[g1]=d end - step.format="kern" + step.format="move" kerned=kerned+1 end end end return kerned end +local compact_pairs=true +local compact_singles=true +local merge_pairs=true +local merge_singles=true +local merge_substitutions=true +local merge_alternates=true +local merge_multiples=true +local merge_ligatures=true +local merge_cursives=true +local merge_marks=true +directives.register("otf.compact.pairs",function(v) compact_pairs=v end) +directives.register("otf.compact.singles",function(v) compact_singles=v end) +directives.register("otf.merge.pairs",function(v) merge_pairs=v end) +directives.register("otf.merge.singles",function(v) merge_singles=v end) +directives.register("otf.merge.substitutions",function(v) merge_substitutions=v end) +directives.register("otf.merge.alternates",function(v) merge_alternates=v end) +directives.register("otf.merge.multiples",function(v) merge_multiples=v end) +directives.register("otf.merge.ligatures",function(v) merge_ligatures=v end) +directives.register("otf.merge.cursives",function(v) merge_cursives=v end) +directives.register("otf.merge.marks",function(v) merge_marks=v end) function readers.compact(data) if not data or data.compacted then return @@ -19191,23 +19730,64 @@ function readers.compact(data) for i=1,#lookups do local lookup=lookups[i] local nofsteps=lookup.nofsteps + local kind=lookup.type allsteps=allsteps+nofsteps if nofsteps>1 then - local kind=lookup.type - if kind=="gsub_single" or kind=="gsub_alternate" or kind=="gsub_multiple" then - merged=merged+mergesteps_1(lookup) + local merg=merged + if kind=="gsub_single" then + if merge_substitutions then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_alternate" then + if merge_alternates then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_multiple" then + if merge_multiples then + merged=merged+mergesteps_1(lookup) + end elseif kind=="gsub_ligature" then - merged=merged+mergesteps_4(lookup) + if merge_ligatures then + merged=merged+mergesteps_4(lookup) + end elseif kind=="gpos_single" then - merged=merged+mergesteps_1(lookup,true) - checkkerns(lookup) + if merge_singles then + merged=merged+mergesteps_1(lookup,true) + end + if compact_singles then + kerned=kerned+checkkerns(lookup) + end elseif kind=="gpos_pair" then - merged=merged+mergesteps_2(lookup,true) - kerned=kerned+checkpairs(lookup) + if merge_pairs then + merged=merged+mergesteps_2(lookup) + end + if compact_pairs then + kerned=kerned+checkpairs(lookup) + end elseif kind=="gpos_cursive" then - merged=merged+mergesteps_2(lookup) + if merge_cursives then + merged=merged+mergesteps_5(lookup) + end elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then - merged=merged+mergesteps_3(lookup) + if merge_marks then + merged=merged+mergesteps_3(lookup) + end + end + if merg~=merged then + lookup.merged=true + end + elseif nofsteps==1 then + local kern=kerned + if kind=="gpos_single" then + if compact_singles then + kerned=kerned+checkkerns(lookup) + end + elseif kind=="gpos_pair" then + if compact_pairs then + kerned=kerned+checkpairs(lookup) + end + end + if kern~=kerned then end end end @@ -19224,6 +19804,72 @@ function readers.compact(data) report("%i steps of %i steps turned from pairs into kerns",kerned,allsteps) end end +local function mergesteps(t,k) + if k=="merged" then + local merged={} + for i=1,#t do + local step=t[i] + local coverage=step.coverage + for k in next,coverage do + local m=merged[k] + if m then + m[2]=i + else + merged[k]={ i,i } + end + end + end + t.merged=merged + return merged + end +end +local function checkmerge(sequence) + local steps=sequence.steps + if steps then + setmetatableindex(steps,mergesteps) + end +end +local function checkflags(sequence,resources) + if not sequence.skiphash then + local flags=sequence.flags + if flags then + local skipmark=flags[1] + local skipligature=flags[2] + local skipbase=flags[3] + local markclass=sequence.markclass + local skipsome=skipmark or skipligature or skipbase or markclass or false + if skipsome then + sequence.skiphash=setmetatableindex(function(t,k) + local c=resources.classes[k] + local v=c==skipmark + or (markclass and c=="mark" and not markclass[k]) + or c==skipligature + or c==skipbase + or false + t[k]=v + return v + end) + else + sequence.skiphash=false + end + else + sequence.skiphash=false + end + end +end +local function checksteps(sequence) + local steps=sequence.steps + if steps then + for i=1,#steps do + steps[i].index=i + end + end +end +if fonts.helpers then + fonts.helpers.checkmerge=checkmerge + fonts.helpers.checkflags=checkflags + fonts.helpers.checksteps=checksteps +end function readers.expand(data) if not data or data.expanded then return @@ -19268,6 +19914,7 @@ function readers.expand(data) local sequence=sequences[i] local steps=sequence.steps if steps then + local nofsteps=sequence.nofsteps local kind=sequence.type local markclass=sequence.markclass if markclass then @@ -19278,7 +19925,7 @@ function readers.expand(data) sequence.markclass=markclasses[markclass] end end - for i=1,sequence.nofsteps do + for i=1,nofsteps do local step=steps[i] local baseclasses=step.baseclasses if baseclasses then @@ -19294,13 +19941,14 @@ function readers.expand(data) end local rules=step.rules if rules then - local rulehash={} + local rulehash={ n=0 } local rulesize=0 local coverage={} local lookuptype=sequence.type + local nofrules=#rules step.coverage=coverage - for nofrules=1,#rules do - local rule=rules[nofrules] + for currentrule=1,nofrules do + local rule=rules[currentrule] local current=rule.current local before=rule.before local after=rule.after @@ -19331,7 +19979,7 @@ function readers.expand(data) for i=1,#lookups do local lookups=lookups[i] if lookups then - for k,v in next,lookups do + for k,v in next,lookups do local lookup=sublookups[v] if lookup then lookups[k]=lookup @@ -19345,9 +19993,9 @@ function readers.expand(data) end end if sequence[1] then - rulesize=rulesize+1 - rulehash[rulesize]={ - nofrules, + sequence.n=#sequence + local ruledata={ + currentrule, lookuptype, sequence, start, @@ -19356,16 +20004,36 @@ function readers.expand(data) replacements, subtype, } - for unic in next,sequence[start] do - local cu=coverage[unic] - if not cu then - coverage[unic]=rulehash + rulesize=rulesize+1 + rulehash[rulesize]=ruledata + rulehash.n=rulesize + if true then + for unic in next,sequence[start] do + local cu=coverage[unic] + if cu then + local n=#cu+1 + cu[n]=ruledata + cu.n=n + else + coverage[unic]={ ruledata,n=1 } + end + end + else + for unic in next,sequence[start] do + local cu=coverage[unic] + if cu then + else + coverage[unic]=rulehash + end end end end end end end + checkmerge(sequence) + checkflags(sequence,resources) + checksteps(sequence) end end end @@ -19404,7 +20072,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf -otf.version=3.029 +otf.version=3.103 otf.cache=containers.define("fonts","otl",otf.version,true) otf.svgcache=containers.define("fonts","svg",otf.version,true) otf.sbixcache=containers.define("fonts","sbix",otf.version,true) @@ -19424,6 +20092,7 @@ local forceload=false local cleanup=0 local syncspace=true local forcenotdef=false +local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes local wildcard="*" local default="dflt" @@ -19435,7 +20104,25 @@ registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or registerdirective("fonts.otf.loader.force",function(v) forceload=v end) registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end) registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end) -registerotfenhancer("check extra features",function() end) +registerotfenhancer("check extra features",function() end) +local checkmemory=utilities.lua and utilities.lua.checkmemory +local threshold=100 +local tracememory=false +registertracker("fonts.otf.loader.memory",function(v) tracememory=v end) +if not checkmemory then + local collectgarbage=collectgarbage + checkmemory=function(previous,threshold) + local current=collectgarbage("count") + if previous then + local checked=(threshold or 64)*1024 + if current-previous>checked then + collectgarbage("collect") + current=collectgarbage("count") + end + end + return current + end +end function otf.load(filename,sub,instance) local base=file.basename(file.removesuffix(filename)) local name=file.removesuffix(base) @@ -19464,9 +20151,13 @@ function otf.load(filename,sub,instance) starttiming(otfreaders) data=otfreaders.loadfont(filename,sub or 1,instance) if data then + local used=checkmemory() local resources=data.resources local svgshapes=resources.svgshapes local sbixshapes=resources.sbixshapes + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end if svgshapes then resources.svgshapes=nil if otf.svgenabled then @@ -19480,6 +20171,11 @@ function otf.load(filename,sub,instance) timestamp=timestamp, } end + if cleanup>1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end end if sbixshapes then resources.sbixshapes=nil @@ -19494,17 +20190,30 @@ function otf.load(filename,sub,instance) timestamp=timestamp, } end + if cleanup>1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end end otfreaders.compact(data) + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end otfreaders.rehash(data,"unicodes") otfreaders.addunicodetable(data) otfreaders.extend(data) + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end otfreaders.pack(data) report_otf("loading done") report_otf("saving %a in cache",filename) data=containers.write(otf.cache,hash,data) if cleanup>1 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end stoptiming(otfreaders) if elapsedtime then @@ -19512,10 +20221,14 @@ function otf.load(filename,sub,instance) end if cleanup>3 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end data=containers.read(otf.cache,hash) if cleanup>2 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end else data=nil @@ -19531,7 +20244,7 @@ function otf.load(filename,sub,instance) otfreaders.addunicodetable(data) otfenhancers.apply(data,filename,data) if applyruntimefixes then - applyruntimefixes(filename,data) + applyruntimefixes(filename,data) end data.metadata.math=data.resources.mathconstants local classes=data.resources.classes @@ -19588,7 +20301,7 @@ local function copytotfm(data,cache_id) end if mathspecs then for unicode,character in next,characters do - local d=descriptions[unicode] + local d=descriptions[unicode] local m=d.math if m then local italic=m.italic @@ -19728,6 +20441,7 @@ local function copytotfm(data,cache_id) properties.fullname=fullname properties.psname=psname properties.name=filename or fullname + properties.private=properties.private or data.private or privateoffset return { characters=characters, descriptions=descriptions, @@ -19820,7 +20534,7 @@ local function checkmathsize(tfmdata,mathsize) local parameters=tfmdata.parameters parameters.scriptpercentage=mathdata.ScriptPercentScaleDown parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown - parameters.mathsize=mathsize + parameters.mathsize=mathsize end end registerotffeature { @@ -20002,6 +20716,7 @@ otf.coverup={ multiple=justset, kern=justset, pair=justset, + single=justset, ligature=function(coverage,unicode,ligature) local first=ligature[1] local tree=coverage[first] @@ -20057,6 +20772,7 @@ local otf=fonts.handlers.otf local otffeatures=otf.features local registerotffeature=otffeatures.register otf.defaultbasealternate="none" +local getprivate=fonts.constructors.getprivate local wildcard="*" local default="dflt" local formatters=string.formatters @@ -20090,19 +20806,31 @@ end local function cref(feature,sequence) return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name) end -local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment) - report_prepare("%s: base alternate %s => %s (%S => %S)", - cref(feature,sequence), - gref(descriptions,unicode), - replacement and gref(descriptions,replacement), - value, - comment) -end local function report_substitution(feature,sequence,descriptions,unicode,substitution) - report_prepare("%s: base substitution %s => %S", - cref(feature,sequence), - gref(descriptions,unicode), - gref(descriptions,substitution)) + if unicode==substitution then + report_prepare("%s: base substitution %s maps onto itself", + cref(feature,sequence), + gref(descriptions,unicode)) + else + report_prepare("%s: base substitution %s => %S", + cref(feature,sequence), + gref(descriptions,unicode), + gref(descriptions,substitution)) + end +end +local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment) + if unicode==replacement then + report_prepare("%s: base alternate %s maps onto itself", + cref(feature,sequence), + gref(descriptions,unicode)) + else + report_prepare("%s: base alternate %s => %s (%S => %S)", + cref(feature,sequence), + gref(descriptions,unicode), + replacement and gref(descriptions,replacement), + value, + comment) + end end local function report_ligature(feature,sequence,descriptions,unicode,ligature) report_prepare("%s: base ligature %s => %S", @@ -20135,13 +20863,11 @@ local function registerbasefeature(feature,value) applied[#applied+1]=feature.."="..tostring(value) end local function makefake(tfmdata,name,present) - local resources=tfmdata.resources - local private=resources.private + local private=getprivate(tfmdata) local character={ intermediate=true,ligatures={} } resources.unicodes[name]=private tfmdata.characters[private]=character tfmdata.descriptions[private]={ name=name } - resources.private=private+1 present[name]=private return character end @@ -20207,37 +20933,44 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis if kind=="gsub_single" then for i=1,#steps do for unicode,data in next,steps[i].coverage do - if trace_singles then - report_substitution(feature,sequence,descriptions,unicode,data) - end + if unicode~=data then changed[unicode]=data + end + if trace_singles then + report_substitution(feature,sequence,descriptions,unicode,data) + end end end elseif kind=="gsub_alternate" then for i=1,#steps do for unicode,data in next,steps[i].coverage do - if not changed[unicode] then - local replacement=data[alternate] - if replacement then + local replacement=data[alternate] + if replacement then + if unicode~=replacement then changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal") - end - elseif defaultalt=="first" then - replacement=data[1] + end + if trace_alternatives then + report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal") + end + elseif defaultalt=="first" then + replacement=data[1] + if unicode~=replacement then changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) - end - elseif defaultalt=="last" then - replacement=data[#data] - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) - end - else - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,"unknown") - end + end + if trace_alternatives then + report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) + end + elseif defaultalt=="last" then + replacement=data[#data] + if unicode~=replacement then + changed[unicode]=replacement + end + if trace_alternatives then + report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) + end + else + if trace_alternatives then + report_alternate(feature,sequence,descriptions,unicode,replacement,value,"unknown") end end end @@ -20284,7 +21017,8 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist if kind=="gpos_pair" then for i=1,#steps do local step=steps[i] - if step.format=="kern" then + local format=step.format + if format=="kern" or format=="move" then for unicode,data in next,steps[i].coverage do local character=characters[unicode] local kerns=character.kerns @@ -20312,9 +21046,11 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist local character=characters[unicode] local kerns=character.kerns for otherunicode,kern in next,data do - if not kern[2] and not (kerns and kerns[otherunicode]) then + local other=kern[2] + if other==true or (not other and not (kerns and kerns[otherunicode])) then local kern=kern[1] - if kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then + if kern==true then + elseif kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then else kern=kern[3] if kern~=0 then @@ -20339,6 +21075,51 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist end local function initializehashes(tfmdata) end +local function checkmathreplacements(tfmdata,fullname,fixitalics) + if tfmdata.mathparameters then + local characters=tfmdata.characters + local changed=tfmdata.changed + if next(changed) then + if trace_preparing or trace_baseinit then + report_prepare("checking math replacements for %a",fullname) + end + for unicode,replacement in next,changed do + local u=characters[unicode] + local r=characters[replacement] + local n=u.next + local v=u.vert_variants + local h=u.horiz_variants + if fixitalics then + local ui=u.italic + if ui and not r.italic then + if trace_preparing then + report_prepare("using %i units of italic correction from %C for %U",ui,unicode,replacement) + end + r.italic=ui + end + end + if n and not r.next then + if trace_preparing then + report_prepare("forcing %s for %C substituted by %U","incremental step",unicode,replacement) + end + r.next=n + end + if v and not r.vert_variants then + if trace_preparing then + report_prepare("forcing %s for %C substituted by %U","vertical variants",unicode,replacement) + end + r.vert_variants=v + end + if h and not r.horiz_variants then + if trace_preparing then + report_prepare("forcing %s for %C substituted by %U","horizontal variants",unicode,replacement) + end + r.horiz_variants=h + end + end + end + end +end local function featuresinitializer(tfmdata,value) if true then local starttime=trace_preparing and os.clock() @@ -20355,6 +21136,8 @@ local function featuresinitializer(tfmdata,value) local rawfeatures=rawresources and rawresources.features local basesubstitutions=rawfeatures and rawfeatures.gsub local basepositionings=rawfeatures and rawfeatures.gpos + local substitutionsdone=false + local positioningsdone=false if basesubstitutions or basepositionings then local sequences=tfmdata.resources.sequences for s=1,#sequences do @@ -20375,12 +21158,14 @@ local function featuresinitializer(tfmdata,value) end preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) registerbasefeature(feature,value) + substitutionsdone=true elseif basepositionings and basepositionings[feature] then if trace_preparing then report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value) end preparepositionings(tfmdata,feature,value,validlookups,lookuplist) registerbasefeature(feature,value) + positioningsdone=true end end end @@ -20388,6 +21173,9 @@ local function featuresinitializer(tfmdata,value) end end end + if substitutionsdone then + checkmathreplacements(tfmdata,fullname,features.fixitalics) + end registerbasehash(tfmdata) end if trace_preparing then @@ -20430,6 +21218,7 @@ local attributes,nodes,node=attributes,nodes,node fonts=fonts local hashes=fonts.hashes local fontdata=hashes.identifiers +local fontmarks=hashes.marks nodes.injections=nodes.injections or {} local injections=nodes.injections local tracers=nodes.tracers @@ -20442,10 +21231,8 @@ local kern_code=nodecodes.kern local glue_code=nodecodes.glue local nuts=nodes.nuts local nodepool=nuts.pool -local newkern=nodepool.kern local tonode=nuts.tonode local tonut=nuts.tonut -local getfield=nuts.getfield local setfield=nuts.setfield local getnext=nuts.getnext local getprev=nuts.getprev @@ -20468,11 +21255,36 @@ local traverse_char=nuts.traverse_char local insert_node_before=nuts.insert_before local insert_node_after=nuts.insert_after local properties=nodes.properties.data -function injections.installnewkern(nk) - newkern=nk or newkern -end +local fontkern=nuts.pool and nuts.pool.fontkern +local italickern=nuts.pool and nuts.pool.italickern +local useitalickerns=false +directives.register("fonts.injections.useitalics",function(v) + if v then + report_injections("using italics for space kerns (tracing only)") + end + useitalickerns=v +end) +do if not fontkern then + local thekern=nuts.new("kern",0) + local setkern=nuts.setkern + local copy_node=nuts.copy_node + fontkern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end + local thekern=nuts.new("kern",3) + local setkern=nuts.setkern + local copy_node=nuts.copy_node + italickern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end +function injections.installnewkern() end local nofregisteredkerns=0 -local nofregisteredpairs=0 +local nofregisteredpositions=0 local nofregisteredmarks=0 local nofregisteredcursives=0 local keepregisteredcounts=false @@ -20481,7 +21293,7 @@ function injections.keepcounts() end function injections.resetcounts() nofregisteredkerns=0 - nofregisteredpairs=0 + nofregisteredpositions=0 nofregisteredmarks=0 nofregisteredcursives=0 keepregisteredcounts=false @@ -20522,7 +21334,7 @@ function injections.copy(target,source) end end end -function injections.setligaindex(n,index) +function injections.setligaindex(n,index) local p=rawget(properties,n) if p then local i=p.injections @@ -20551,7 +21363,7 @@ function injections.getligaindex(n,default) end return default end -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag) local dx=factor*(exit[1]-entry[1]) local dy=-factor*(exit[2]-entry[2]) local ws=tfmstart.width @@ -20604,7 +21416,7 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne end return dx,dy,nofregisteredcursives end -function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) +function injections.setposition(kind,current,factor,rlmode,spec,injection) local x=factor*spec[1] local y=factor*spec[2] local w=factor*spec[3] @@ -20614,7 +21426,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) local leftkern=x local rightkern=w-x if leftkern~=0 or rightkern~=0 or yoffset~=0 then - nofregisteredpairs=nofregisteredpairs+1 + nofregisteredpositions=nofregisteredpositions+1 if rlmode and rlmode<0 then leftkern,rightkern=rightkern,leftkern end @@ -20623,7 +21435,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) end local p=rawget(properties,current) if p then - local i=rawget(p,injection) + local i=p[injection] if i then if leftkern~=0 then i.leftkern=(i.leftkern or 0)+leftkern @@ -20660,7 +21472,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) }, } end - return x,y,w,h,nofregisteredpairs + return x,y,w,h,nofregisteredpositions end end return x,y,w,h @@ -20674,7 +21486,7 @@ function injections.setkern(current,factor,rlmode,x,injection) injection="injections" end if p then - local i=rawget(p,injection) + local i=p[injection] if i then i.leftkern=dx+(i.leftkern or 0) else @@ -20694,6 +21506,54 @@ function injections.setkern(current,factor,rlmode,x,injection) return 0,0 end end +function injections.setmove(current,factor,rlmode,x,injection) + local dx=factor*x + if dx~=0 then + nofregisteredkerns=nofregisteredkerns+1 + local p=rawget(properties,current) + if not injection then + injection="injections" + end + if rlmode and rlmode<0 then + if p then + local i=p[injection] + if i then + i.rightkern=dx+(i.rightkern or 0) + else + p[injection]={ + rightkern=dx, + } + end + else + properties[current]={ + [injection]={ + rightkern=dx, + }, + } + end + else + if p then + local i=p[injection] + if i then + i.leftkern=dx+(i.leftkern or 0) + else + p[injection]={ + leftkern=dx, + } + end + else + properties[current]={ + [injection]={ + leftkern=dx, + }, + } + end + end + return dx,nofregisteredkerns + else + return 0,0 + end +end function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) nofregisteredmarks=nofregisteredmarks+1 @@ -20706,9 +21566,15 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmar if i then if i.markmark then else - i.markx=dx - i.marky=dy - i.markdir=rlmode or 0 + if dx~=0 then + i.markx=dx + end + if y~=0 then + i.marky=dy + end + if rlmode then + i.markdir=rlmode + end i.markbase=nofregisteredmarks i.markbasenode=base i.markmark=mkmk @@ -20751,7 +21617,7 @@ local function show(n,what,nested,symbol) if n then local p=rawget(properties,n) if p then - local i=rawget(p,what) + local i=p[what] if i then local leftkern=i.leftkern or 0 local rightkern=i.rightkern or 0 @@ -20791,15 +21657,16 @@ local function show(n,what,nested,symbol) end local function showsub(n,what,where) report_injections("begin subrun: %s",where) - for n in traverse_id(glyph_code,n) do + for n in traverse_char(n) do showchar(n,where) show(n,what,where," ") end report_injections("end subrun") end local function trace(head,where) - report_injections("begin run %s: %s kerns, %s pairs, %s marks and %s cursives registered", - where or "",nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives) + report_injections() + report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered", + where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives) local n=head while n do local id=getid(n) @@ -20846,6 +21713,7 @@ local function show_result(head) end current=getnext(current) end + report_injections() end local function inject_kerns_only(head,where) head=tonut(head) @@ -20856,7 +21724,6 @@ local function inject_kerns_only(head,where) local prev=nil local next=nil local prevdisc=nil - local prevglyph=nil local pre=nil local post=nil local replace=nil @@ -20873,7 +21740,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - head=insert_node_before(head,current,newkern(leftkern)) + head=insert_node_before(head,current,fontkern(leftkern)) end end if prevdisc then @@ -20883,7 +21750,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setlink(posttail,newkern(leftkern)) + setlink(posttail,fontkern(leftkern)) done=true end end @@ -20893,7 +21760,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setlink(replacetail,newkern(leftkern)) + setlink(replacetail,fontkern(leftkern)) done=true end end @@ -20902,7 +21769,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setfield(prev,"replace",newkern(leftkern)) + setfield(prev,"replace",fontkern(leftkern)) end end end @@ -20912,10 +21779,8 @@ local function inject_kerns_only(head,where) end end prevdisc=nil - prevglyph=current elseif char==false then prevdisc=nil - prevglyph=current elseif id==disc_code then pre,post,replace,pretail,posttail,replacetail=getdisc(current,true) local done=false @@ -20927,7 +21792,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - pre=insert_node_before(pre,n,newkern(leftkern)) + pre=insert_node_before(pre,n,fontkern(leftkern)) done=true end end @@ -20942,7 +21807,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - post=insert_node_before(post,n,newkern(leftkern)) + post=insert_node_before(post,n,fontkern(leftkern)) done=true end end @@ -20957,7 +21822,7 @@ local function inject_kerns_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - replace=insert_node_before(replace,n,newkern(leftkern)) + replace=insert_node_before(replace,n,fontkern(leftkern)) done=true end end @@ -20967,10 +21832,8 @@ local function inject_kerns_only(head,where) if done then setdisc(current,pre,post,replace) end - prevglyph=nil prevdisc=current else - prevglyph=nil prevdisc=nil end prev=current @@ -20981,12 +21844,15 @@ local function inject_kerns_only(head,where) else nofregisteredkerns=0 end + if trace_injections then + show_result(head) + end return tonode(head),true end -local function inject_pairs_only(head,where) +local function inject_positions_only(head,where) head=tonut(head) if trace_injections then - trace(head,"pairs") + trace(head,"positions") end local current=head local prev=nil @@ -21012,12 +21878,17 @@ local function inject_pairs_only(head,where) setoffsets(current,false,yoffset) end local leftkern=i.leftkern + local rightkern=i.rightkern if leftkern and leftkern~=0 then - head=insert_node_before(head,current,newkern(leftkern)) + if rightkern and leftkern==-rightkern then + setoffsets(current,leftkern,false) + rightkern=0 + else + head=insert_node_before(head,current,fontkern(leftkern)) + end end - local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(head,current,newkern(rightkern)) + insert_node_after(head,current,fontkern(rightkern)) end else local i=p.emptyinjections @@ -21027,7 +21898,7 @@ local function inject_pairs_only(head,where) if next and getid(next)==disc_code then if replace then else - setfield(next,"replace",newkern(rightkern)) + setfield(next,"replace",fontkern(rightkern)) end end end @@ -21040,7 +21911,7 @@ local function inject_pairs_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setlink(posttail,newkern(leftkern)) + setlink(posttail,fontkern(leftkern)) done=true end end @@ -21050,7 +21921,7 @@ local function inject_pairs_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setlink(replacetail,newkern(leftkern)) + setlink(replacetail,fontkern(leftkern)) done=true end end @@ -21059,7 +21930,7 @@ local function inject_pairs_only(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setfield(prev,"replace",newkern(leftkern)) + setfield(prev,"replace",fontkern(leftkern)) end end end @@ -21088,12 +21959,12 @@ local function inject_pairs_only(head,where) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - pre=insert_node_before(pre,n,newkern(leftkern)) + pre=insert_node_before(pre,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(pre,n,newkern(rightkern)) + insert_node_after(pre,n,fontkern(rightkern)) done=true end end @@ -21112,12 +21983,12 @@ local function inject_pairs_only(head,where) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - post=insert_node_before(post,n,newkern(leftkern)) + post=insert_node_before(post,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(post,n,newkern(rightkern)) + insert_node_after(post,n,fontkern(rightkern)) done=true end end @@ -21136,12 +22007,12 @@ local function inject_pairs_only(head,where) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - replace=insert_node_before(replace,n,newkern(leftkern)) + replace=insert_node_before(replace,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(replace,n,newkern(rightkern)) + insert_node_after(replace,n,fontkern(rightkern)) done=true end end @@ -21156,7 +22027,7 @@ local function inject_pairs_only(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - pre=insert_node_before(pre,pre,newkern(rightkern)) + pre=insert_node_before(pre,pre,fontkern(rightkern)) done=true end end @@ -21169,7 +22040,7 @@ local function inject_pairs_only(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - replace=insert_node_before(replace,replace,newkern(rightkern)) + replace=insert_node_before(replace,replace,fontkern(rightkern)) done=true end end @@ -21191,16 +22062,17 @@ local function inject_pairs_only(head,where) if keepregisteredcounts then keepregisteredcounts=false else - nofregisteredkerns=0 + nofregisteredpositions=0 + end + if trace_injections then + show_result(head) end return tonode(head),true end local function showoffset(n,flag) local x,y=getoffsets(n) if x~=0 or y~=0 then - setcolor(n,flag and "darkred" or "darkgreen") - else - resetcolor(n) + setcolor(n,"darkgray") end end local function inject_everything(head,where) @@ -21212,8 +22084,6 @@ local function inject_everything(head,where) local hasmarks=nofregisteredmarks>0 local current=head local last=nil - local font=font - local markdata=nil local prev=nil local next=nil local prevdisc=nil @@ -21242,23 +22112,31 @@ local function inject_everything(head,where) rightkern=pp.rightkern end end + local markdir=pn.markdir if rightkern then - if pn.markdir<0 then - ox=px-pn.markx-rightkern + ox=px-(pn.markx or 0)-rightkern + if markdir and markdir<0 then + if not pn.markmark then + ox=ox+(pn.leftkern or 0) + end else if false then local leftkern=pp.leftkern if leftkern then - ox=px-pn.markx-leftkern - else - ox=px-pn.markx + ox=ox-leftkern end - else - ox=px-pn.markx-rightkern end end else - ox=px-pn.markx + ox=px-(pn.markx or 0) + if markdir and markdir<0 then + if not pn.markmark then + local leftkern=pn.leftkern + if leftkern then + ox=ox+leftkern + end + end + end if pn.checkmark then local wn=getwidth(n) if wn and wn~=0 then @@ -21266,26 +22144,81 @@ local function inject_everything(head,where) if trace_injections then report_injections("correcting non zero width mark %C",getchar(n)) end - insert_node_before(n,n,newkern(-wn)) - insert_node_after(n,n,newkern(-wn)) + insert_node_before(n,n,fontkern(-wn)) + insert_node_after(n,n,fontkern(-wn)) end end end - local oy=ny+py+pn.marky + local oy=ny+py+(pn.marky or 0) + if not pn.markmark then + local yoffset=pn.yoffset + if yoffset then + oy=oy+yoffset + end + end setoffsets(n,ox,oy) if trace_marks then showoffset(n,true) end end + local base=nil while current do local next=getnext(current) local char,id=ischar(current) if char then local p=rawget(properties,current) + if hascursives then + if not p then + local m=fontmarks[getfont(current)] + if m and m[char] then + if base then + p={ injections={ markbasenode=base } } + nofmarks=nofmarks+1 + marks[nofmarks]=current + properties[current]=p + hasmarks=true + end + else + base=current + end + end + end if p then local i=p.injections + if hascursives then + if not i then + local m=fontmarks[getfont(current)] + if m and m[char] then + if base then + i={ markbasenode=base } + nofmarks=nofmarks+1 + marks[nofmarks]=current + p.injections=i + hasmarks=true + end + else + base=current + end + end + end if i then local pm=i.markbasenode + if hascursives then + if not pm then + local m=fontmarks[getfont(current)] + if m and m[char] then + if base then + pm=base + i.markbasenode=pm + hasmarks=true + end + else + base=current + end + else + base=current + end + end if pm then nofmarks=nofmarks+1 marks[nofmarks]=current @@ -21346,12 +22279,17 @@ local function inject_everything(head,where) end end local leftkern=i.leftkern + local rightkern=i.rightkern if leftkern and leftkern~=0 then - head=insert_node_before(head,current,newkern(leftkern)) + if rightkern and leftkern==-rightkern then + setoffsets(current,leftkern,false) + rightkern=0 + else + head=insert_node_before(head,current,fontkern(leftkern)) + end end - local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(head,current,newkern(rightkern)) + insert_node_after(head,current,fontkern(rightkern)) end end else @@ -21362,7 +22300,7 @@ local function inject_everything(head,where) if next and getid(next)==disc_code then if replace then else - setfield(next,"replace",newkern(rightkern)) + setfield(next,"replace",fontkern(rightkern)) end end end @@ -21376,7 +22314,7 @@ local function inject_everything(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setlink(posttail,newkern(leftkern)) + setlink(posttail,fontkern(leftkern)) done=true end end @@ -21386,7 +22324,7 @@ local function inject_everything(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setlink(replacetail,newkern(leftkern)) + setlink(replacetail,fontkern(leftkern)) done=true end end @@ -21395,7 +22333,7 @@ local function inject_everything(head,where) if i then local leftkern=i.leftkern if leftkern and leftkern~=0 then - setfield(prev,"replace",newkern(leftkern)) + setfield(prev,"replace",fontkern(leftkern)) end end end @@ -21437,12 +22375,12 @@ local function inject_everything(head,where) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - pre=insert_node_before(pre,n,newkern(leftkern)) + pre=insert_node_before(pre,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(pre,n,newkern(rightkern)) + insert_node_after(pre,n,fontkern(rightkern)) done=true end if hasmarks then @@ -21467,12 +22405,12 @@ local function inject_everything(head,where) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - post=insert_node_before(post,n,newkern(leftkern)) + post=insert_node_before(post,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(post,n,newkern(rightkern)) + insert_node_after(post,n,fontkern(rightkern)) done=true end if hasmarks then @@ -21497,12 +22435,12 @@ local function inject_everything(head,where) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - replace=insert_node_before(replace,n,newkern(leftkern)) + replace=insert_node_before(replace,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(replace,n,newkern(rightkern)) + insert_node_after(replace,n,fontkern(rightkern)) done=true end if hasmarks then @@ -21523,7 +22461,7 @@ local function inject_everything(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - pre=insert_node_before(pre,pre,newkern(rightkern)) + pre=insert_node_before(pre,pre,fontkern(rightkern)) done=true end end @@ -21536,7 +22474,7 @@ local function inject_everything(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - replace=insert_node_before(replace,replace,newkern(rightkern)) + replace=insert_node_before(replace,replace,fontkern(rightkern)) done=true end end @@ -21551,6 +22489,7 @@ local function inject_everything(head,where) else prevglyph=nil prevdisc=nil +base=nil end prev=current current=next @@ -21580,10 +22519,13 @@ local function inject_everything(head,where) keepregisteredcounts=false else nofregisteredkerns=0 - nofregisteredpairs=0 + nofregisteredpositions=0 nofregisteredmarks=0 nofregisteredcursives=0 end + if trace_injections then + show_result(head) + end return tonode(head),true end local triggers=false @@ -21641,6 +22583,7 @@ local function injectspaces(head) local threshold=0 local leftkern=false local rightkern=false + local nuthead=tonut(head) local function updatefont(font,trig) leftkerns=trig.left rightkerns=trig.right @@ -21648,7 +22591,7 @@ local function injectspaces(head) threshold, factor=getthreshold(font) end - for n in traverse_id(glue_code,tonut(head)) do + for n in traverse_id(glue_code,nuthead) do local prev,next=getspaceboth(n) local prevchar=prev and ischar(prev) local nextchar=next and ischar(next) @@ -21680,29 +22623,59 @@ local function injectspaces(head) local old=getwidth(n) if old>threshold then if rightkern then - local new=old+(leftkern+rightkern)*factor - if trace_spaces then - report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar) + if useitalickerns then + local lnew=leftkern*factor + local rnew=rightkern*factor + if trace_spaces then + report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar) + end + local h=insert_node_before(nuthead,n,italickern(lnew)) + if h==nuthead then + head=tonode(h) + nuthead=h + end + insert_node_after(nuthead,n,italickern(rnew)) + else + local new=old+(leftkern+rightkern)*factor + if trace_spaces then + report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar) + end + setwidth(n,new) end - setwidth(n,new) - leftkern=false + rightkern=false else - local new=old+leftkern*factor - if trace_spaces then - report_spaces("%C [%p -> %p]",prevchar,old,new) + if useitalickerns then + local new=leftkern*factor + if trace_spaces then + report_spaces("%C [%p + %p]",prevchar,old,new) + end + insert_node_after(nuthead,n,italickern(new)) + else + local new=old+leftkern*factor + if trace_spaces then + report_spaces("%C [%p -> %p]",prevchar,old,new) + end + setwidth(n,new) end - setwidth(n,new) end end leftkern=false elseif rightkern then local old=getwidth(n) if old>threshold then - local new=old+rightkern*factor - if trace_spaces then - report_spaces("[%p -> %p] %C",nextchar,old,new) + if useitalickerns then + local new=rightkern*factor + if trace_spaces then + report_spaces("%C [%p + %p]",nextchar,old,new) + end + insert_node_after(nuthead,n,italickern(new)) + else + local new=old+rightkern*factor + if trace_spaces then + report_spaces("[%p -> %p] %C",nextchar,old,new) + end + setwidth(n,new) end - setwidth(n,new) end rightkern=false end @@ -21719,11 +22692,11 @@ function injections.handler(head,where) report_injections("injection variant %a","everything") end return inject_everything(head,where) - elseif nofregisteredpairs>0 then + elseif nofregisteredpositions>0 then if trace_injections then - report_injections("injection variant %a","pairs") + report_injections("injection variant %a","positions") end - return inject_pairs_only(head,where) + return inject_positions_only(head,where) elseif nofregisteredkerns>0 then if trace_injections then report_injections("injection variant %a","kerns") @@ -21740,7 +22713,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-ota']={ version=1.001, - comment="companion to font-otf.lua (analysing)", + comment="companion to font-ini.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" @@ -21783,7 +22756,7 @@ local s_fina=3 local s_pref=9 local s_isol=4 local s_blwf=10 local s_mark=5 local s_pstf=11 local s_rest=6 -local states={ +local states=allocate { init=s_init, medi=s_medi, med2=s_medi, @@ -21799,7 +22772,7 @@ local states={ blwf=s_blwf, pstf=s_pstf, } -local features={ +local features=allocate { init=s_init, medi=s_medi, med2=s_medi, @@ -21931,7 +22904,7 @@ local function warning(current,what) arab_warned[char]=true end end -local mappers={ +local mappers=allocate { l=s_init, d=s_medi, c=s_medi, @@ -22151,29 +23124,34 @@ local trace_bugs=false registertracker("otf.bugs",function(v) trace_bugs=v end) local trace_details=false registertracker("otf.details",function(v) trace_details=v end) local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end) local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end) -local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end) local trace_plugins=false registertracker("otf.plugins",function(v) trace_plugins=v end) +local trace_chains=false registertracker("otf.chains",function(v) trace_chains=v end) local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end) -local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end) local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end) local trace_testruns=false registertracker("otf.testruns",function(v) trace_testruns=v end) -local optimizekerns=true +local forcediscretionaries=false +local forcepairadvance=false +directives.register("otf.forcediscretionaries",function(v) + forcediscretionaries=v +end) +directives.register("otf.forcepairadvance",function(v) + forcepairadvance=v +end) local report_direct=logs.reporter("fonts","otf direct") local report_subchain=logs.reporter("fonts","otf subchain") local report_chain=logs.reporter("fonts","otf chain") local report_process=logs.reporter("fonts","otf process") local report_warning=logs.reporter("fonts","otf warning") local report_run=logs.reporter("fonts","otf run") -registertracker("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures") -registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") -registertracker("otf.actions","otf.replacements,otf.positions") -registertracker("otf.injections","nodes.injections") -registertracker("otf.sample","otf.steps,otf.actions,otf.analyzing") +registertracker("otf.substitutions","otf.singles","otf.multiples","otf.alternatives","otf.ligatures") +registertracker("otf.positions","otf.marks","otf.kerns","otf.cursive") +registertracker("otf.actions","otf.substitutions","otf.positions") +registertracker("otf.sample","otf.steps","otf.substitutions","otf.positions","otf.analyzing") +registertracker("otf.sample.silent","otf.steps=silent","otf.substitutions","otf.positions","otf.analyzing") local nuts=nodes.nuts local tonode=nuts.tonode local tonut=nuts.tonut local getfield=nuts.getfield -local setfield=nuts.setfield local getnext=nuts.getnext local setnext=nuts.setnext local getprev=nuts.getprev @@ -22198,20 +23176,22 @@ local setcomponents=nuts.setcomponents local getdir=nuts.getdir local getwidth=nuts.getwidth local ischar=nuts.is_char +local usesfont=nuts.uses_font local insert_node_after=nuts.insert_after local copy_node=nuts.copy local copy_node_list=nuts.copy_list +local remove_node=nuts.remove local find_node_tail=nuts.tail local flush_node_list=nuts.flush_list local flush_node=nuts.flush_node local end_of_math=nuts.end_of_math local traverse_nodes=nuts.traverse -local traverse_id=nuts.traverse_id local set_components=nuts.set_components local take_components=nuts.take_components local count_components=nuts.count_components local copy_no_components=nuts.copy_no_components local copy_only_glyphs=nuts.copy_only_glyphs +local setmetatable=setmetatable local setmetatableindex=table.setmetatableindex local nodecodes=nodes.nodecodes local glyphcodes=nodes.glyphcodes @@ -22230,7 +23210,8 @@ local injections=nodes.injections local setmark=injections.setmark local setcursive=injections.setcursive local setkern=injections.setkern -local setpair=injections.setpair +local setmove=injections.setmove +local setposition=injections.setposition local resetinjection=injections.reset local copyinjection=injections.copy local setligaindex=injections.setligaindex @@ -22251,80 +23232,86 @@ local currentfont=false local factor=0 local threshold=0 local checkmarks=false +local discs=false +local spaces=false local sweepnode=nil -local sweepprev=nil -local sweepnext=nil -local sweephead={} -local notmatchpre={} -local notmatchpost={} -local notmatchreplace={} +local sweephead={} +local notmatchpre={} +local notmatchpost={} +local notmatchreplace={} local handlers={} local isspace=injections.isspace local getthreshold=injections.getthreshold local checkstep=(tracers and tracers.steppers.check) or function() end local registerstep=(tracers and tracers.steppers.register) or function() end local registermessage=(tracers and tracers.steppers.message) or function() end -local function checkdisccontent(d) - local pre,post,replace=getdisc(d) - if pre then for n in traverse_id(glue_code,pre) do print("pre",nodes.idstostring(pre)) break end end - if post then for n in traverse_id(glue_code,post) do print("pos",nodes.idstostring(post)) break end end - if replace then for n in traverse_id(glue_code,replace) do print("rep",nodes.idstostring(replace)) break end end -end local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end end report_direct(...) end local function logwarning(...) report_direct(...) end -local f_unicode=formatters["%U"] -local f_uniname=formatters["%U (%s)"] -local f_unilist=formatters["% t (% t)"] -local function gref(n) - if type(n)=="number" then - local description=descriptions[n] - local name=description and description.name - if name then - return f_uniname(n,name) - else - return f_unicode(n) - end - elseif n then - local num,nam={},{} - for i=1,#n do - local ni=n[i] - if tonumber(ni) then - local di=descriptions[ni] - num[i]=f_unicode(ni) - nam[i]=di and di.name or "-" +local gref do + local f_unicode=formatters["U+%X"] + local f_uniname=formatters["U+%X (%s)"] + local f_unilist=formatters["% t"] + gref=function(n) + if type(n)=="number" then + local description=descriptions[n] + local name=description and description.name + if name then + return f_uniname(n,name) + else + return f_unicode(n) end + elseif n then + local t={} + for i=1,#n do + local ni=n[i] + if tonumber(ni) then + local di=descriptions[ni] + local nn=di and di.name + if nn then + t[#t+1]=f_uniname(ni,nn) + else + t[#t+1]=f_unicode(ni) + end + end + end + return f_unilist(t) + else + return "<error in node mode tracing>" end - return f_unilist(num,nam) - else - return "<error in node mode tracing>" end end local function cref(dataset,sequence,index) if not dataset then return "no valid dataset" - elseif index then - return formatters["feature %a, type %a, chain lookup %a, index %a"](dataset[4],sequence.type,sequence.name,index) + end + local merged=sequence.merged and "merged " or "" + if index then + return formatters["feature %a, type %a, %schain lookup %a, index %a"]( + dataset[4],sequence.type,merged,sequence.name,index) else - return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name) + return formatters["feature %a, type %a, %schain lookup %a"]( + dataset[4],sequence.type,merged,sequence.name) end end local function pref(dataset,sequence) - return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name) + return formatters["feature %a, type %a, %slookup %a"]( + dataset[4],sequence.type,sequence.merged and "merged " or "",sequence.name) end local function mref(rlmode) - if not rlmode or rlmode==0 then - return "---" - elseif rlmode==-1 or rlmode=="+TRT" then - return "r2l" - else + if not rlmode or rlmode>=0 then return "l2r" + else + return "r2l" end end local function flattendisk(head,disc) @@ -22348,7 +23335,6 @@ local function flattendisk(head,disc) elseif next then return next,next else - return end else if replace then @@ -22418,7 +23404,7 @@ local function markstoligature(head,start,stop,char) return head,base end end -local function toligature(head,start,stop,char,dataset,sequence,markflag,discfound) +local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) if getattr(start,a_noligature)==1 then return head,start end @@ -22442,7 +23428,7 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou set_components(base,comp) setlink(prev,base,next) if not discfound then - local deletemarks=markflag~="mark" + local deletemarks=not skiphash or hasmarks local components=start local baseindex=0 local componentindex=0 @@ -22453,13 +23439,13 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou if not marks[char] then baseindex=baseindex+componentindex componentindex=count_components(start,marks) - elseif not deletemarks then + elseif not deletemarks then setligaindex(start,baseindex+getligaindex(start,componentindex)) if trace_marks then logwarning("%s: keep mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start)) end local n=copy_node(start) - copyinjection(n,start) + copyinjection(n,start) head,current=insert_node_after(head,current,n) elseif trace_marks then logwarning("%s: delete mark %s",pref(dataset,sequence),gref(char)) @@ -22508,14 +23494,18 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou setboth(base) set_components(base,copied) replace=base - setdisc(discfound,pre,post,replace) + if forcediscretionaries then + setdisc(discfound,pre,post,replace,discretionary_code) + else + setdisc(discfound,pre,post,replace) + end base=prev end end end return head,base end -local function multiple_glyphs(head,start,multiple,ignoremarks,what) +local function multiple_glyphs(head,start,multiple,skiphash,what) local nofmultiples=#multiple if nofmultiples>0 then resetinjection(start) @@ -22608,20 +23598,20 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative) end return head,start,true end -function handlers.gsub_multiple(head,start,dataset,sequence,multiple) +function handlers.gsub_multiple(head,start,dataset,sequence,multiple,rlmode,skiphash) if trace_multiples then logprocess("%s: replacing %s by multiple %s",pref(dataset,sequence),gref(getchar(start)),gref(multiple)) end - return multiple_glyphs(head,start,multiple,sequence.flags[1],dataset[1]) + return multiple_glyphs(head,start,multiple,skiphash,dataset[1]) end -function handlers.gsub_ligature(head,start,dataset,sequence,ligature) +function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skiphash) local current=getnext(start) if not current then return head,start,false,nil end local stop=nil local startchar=getchar(start) - if marks[startchar] then + if skiphash and skiphash[startchar] then while current do local char=ischar(current,currentfont) if char then @@ -22651,14 +23641,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) else end end - else - local skipmark=sequence.flags[1] + else local discfound=false local lastdisc=nil + local hasmarks=marks[startchar] while current do local char,id=ischar(current,currentfont) if char then - if skipmark and marks[char] then + if skiphash and skiphash[char] then current=getnext(current) else local lg=ligature[char] @@ -22667,6 +23657,9 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) discfound=lastdisc lastdisc=nil end + if marks[char] then + hasmarks=true + end stop=current ligature=lg current=getnext(current) @@ -22677,13 +23670,16 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) elseif char==false then break elseif id==disc_code then - local replace=getfield(current,"replace") + local replace=getfield(current,"replace") if replace then while replace do local char,id=ischar(replace,currentfont) if char then local lg=ligature[char] if lg then + if marks[char] then + hasmarks=true + end ligature=lg replace=getnext(replace) else @@ -22706,10 +23702,10 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) if stop then if trace_ligatures then local stopchar=getchar(stop) - head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound) + head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) else - head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound) + head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) end else resetinjection(start) @@ -22724,22 +23720,23 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) end return head,start,false,discfound end -function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,injection) +function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) local startchar=getchar(start) - if step.format=="pair" then - local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns,injection) + local format=step.format + if format=="single" or type(kerns)=="table" then + local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns,injection) if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(dataset,sequence),gref(startchar),dx,dy,w,h) + logprocess("%s: shifting single %s by %s xy (%p,%p) and wh (%p,%p)",pref(dataset,sequence),gref(startchar),format,dx,dy,w,h) end else - local k=setkern(start,factor,rlmode,kerns,injection) + local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection) if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(startchar),k) + logprocess("%s: shifting single %s by %s %p",pref(dataset,sequence),gref(startchar),format,k) end end - return head,start,false + return head,start,true end -function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,injection) +function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) local snext=getnext(start) if not snext then return head,start,false @@ -22748,46 +23745,47 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje while snext do local nextchar=ischar(snext,currentfont) if nextchar then - local krn=kerns[nextchar] - if not krn and marks[nextchar] then + if skiphash and skiphash[nextchar] then prev=snext snext=getnext(snext) - elseif not krn then - break - elseif step.format=="pair" then - local a,b=krn[1],krn[2] - if optimizekerns then - if not b and a[1]==0 and a[2]==0 and a[4]==0 then - local k=setkern(snext,factor,rlmode,a[3],injection) + else + local krn=kerns[nextchar] + if not krn then + break + end + local format=step.format + if format=="pair" then + local a,b=krn[1],krn[2] + if a==true then + elseif a then + local x,y,w,h=setposition(1,start,factor,rlmode,a,injection) if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) + local startchar=getchar(start) + logprocess("%s: shifting first of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") end - return head,start,true end - end - if a and #a>0 then - local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection) - if trace_kerns then - local startchar=getchar(start) - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + if b==true then + start=snext + elseif b then + local x,y,w,h=setposition(2,snext,factor,rlmode,b,injection) + if trace_kerns then + local startchar=getchar(snext) + logprocess("%s: shifting second of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + end + start=snext + elseif forcepairadvance then + start=snext end - end - if b and #b>0 then - local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection) + return head,start,true + elseif krn~=0 then + local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn,injection) if trace_kerns then - local startchar=getchar(snext) - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + logprocess("%s: inserting %s %p between %s and %s as %s",pref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar),injection or "injections") end + return head,start,true + else + break end - return head,start,true - elseif krn~=0 then - local k=setkern(snext,factor,rlmode,krn,injection) - if trace_kerns then - logprocess("%s: inserting kern %p between %s and %s as %s",pref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar),injection or "injections") - end - return head,start,true - else - break end else break @@ -22796,7 +23794,7 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje return head,start,false end end -function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar=getchar(start) if marks[markchar] then local base=getprev(start) @@ -22831,10 +23829,12 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) local ma=markanchors[2] local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", - pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) + logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head,start,true + elseif trace_bugs then + logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar)) end elseif trace_bugs then logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1) @@ -22847,7 +23847,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) end return head,start,false end -function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar=getchar(start) if marks[markchar] then local base=getprev(start) @@ -22886,8 +23886,8 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm if ba then local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then - logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", - pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) + logprocess("%s, index %s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(dataset,sequence),index,bound,gref(markchar),gref(basechar),index,dx,dy) end return head,start,true else @@ -22910,7 +23910,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm end return head,start,false end -function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar=getchar(start) if marks[markchar] then local base=getprev(start) @@ -22933,8 +23933,8 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) local ma=markanchors[2] local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", - pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) + logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head,start,true end @@ -22945,7 +23945,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) end return head,start,false end -function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,step,i) +function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,skiphash,step) local startchar=getchar(start) if marks[startchar] then if trace_cursive then @@ -22957,7 +23957,7 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st local nextchar=ischar(nxt,currentfont) if not nextchar then break - elseif marks[nextchar] then + elseif marks[nextchar] then nxt=getnext(nxt) else local exit=exitanchors[3] @@ -22966,9 +23966,10 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st if entry then entry=entry[2] if entry then - local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local r2lflag=sequence.flags[4] + local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) if trace_cursive then - logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,anchor,bound,mref(rlmode)) + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) end return head,start,true end @@ -22984,6 +23985,9 @@ local chainprocs={} local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end end report_subchain(...) end @@ -22991,11 +23995,14 @@ local logwarning=report_subchain local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end end report_chain(...) end local logwarning=report_chain -local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode) +local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode,skiphash) local char=getchar(start) local replacement=replacements[char] if replacement then @@ -23016,17 +24023,30 @@ end local function reportmoresteps(dataset,sequence) logwarning("%s: more than 1 step",cref(dataset,sequence)) end -function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,chainindex) +local function getmapping(dataset,sequence,currentlookup) local steps=currentlookup.steps local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) - end if nofsteps==0 then reportzerosteps(dataset,sequence) + currentlookup.mapping=false + return false else - local current=start + if nofsteps>1 then + reportmoresteps(dataset,sequence) + end local mapping=steps[1].coverage + currentlookup.mapping=mapping + currentlookup.format=steps[1].format + return mapping + end +end +function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local current=start while current do local currentchar=ischar(current) if currentchar then @@ -23054,44 +24074,16 @@ function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,c end return head,start,false end -function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) - end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else - local startchar=getchar(start) - local replacement=steps[1].coverage[startchar] - if not replacement or replacement=="" then - if trace_bugs then - logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar)) - end - else - if trace_multiples then - logprocess("%s: replacing %s by multiple characters %s",cref(dataset,sequence),gref(startchar),gref(replacement)) - end - return multiple_glyphs(head,start,replacement,sequence.flags[1],dataset[1]) - end - end - return head,start,false -end -function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local kind=dataset[4] local what=dataset[1] local value=what==true and tfmdata.shared.features[kind] or what local current=start - local mapping=steps[1].coverage while current do local currentchar=ischar(current) if currentchar then @@ -23100,13 +24092,13 @@ function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlooku local choice,comment=get_alternative_glyph(current,alternatives,value) if choice then if trace_alternatives then - logprocess("%s: replacing %s by alternative %a to %s, %s",cref(dataset,sequence),gref(char),choice,gref(choice),comment) + logprocess("%s: replacing %s by alternative %a to %s, %s",cref(dataset,sequence),gref(currentchar),choice,gref(choice),comment) end resetinjection(start) setchar(start,choice) else if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",cref(dataset,sequence),value,gref(char),comment) + logwarning("%s: no variant %a for %s, %s",cref(dataset,sequence),value,gref(currentchar),comment) end end end @@ -23122,27 +24114,45 @@ function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlooku end return head,start,false end -function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,chainindex) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local startchar=getchar(start) - local ligatures=steps[1].coverage[startchar] + local replacement=mapping[startchar] + if not replacement or replacement=="" then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar)) + end + else + if trace_multiples then + logprocess("%s: replacing %s by multiple characters %s",cref(dataset,sequence),gref(startchar),gref(replacement)) + end + return multiple_glyphs(head,start,replacement,skiphash,dataset[1]) + end + end + return head,start,false +end +function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local startchar=getchar(start) + local ligatures=mapping[startchar] if not ligatures then if trace_bugs then logwarning("%s: no ligatures starting with %s",cref(dataset,sequence,chainindex),gref(startchar)) end else + local hasmarks=marks[startchar] local current=getnext(start) local discfound=false local last=stop local nofreplacements=1 - local skipmark=currentlookup.flags[1] while current do local id=getid(current) if id==disc_code then @@ -23156,7 +24166,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup end else local schar=getchar(current) - if skipmark and marks[schar] then + if skiphash and skiphash[schar] then current=getnext(current) else local lg=ligatures[schar] @@ -23164,6 +24174,9 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup ligatures=lg last=current nofreplacements=nofreplacements+1 + if marks[char] then + hasmarks=true + end if current==stop then break else @@ -23187,7 +24200,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)),gref(ligature)) end end - head,start=toligature(head,start,stop,ligature,dataset,sequence,skipmark,discfound) + head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) return head,start,true,nofreplacements,discfound elseif trace_bugs then if start==stop then @@ -23200,47 +24213,42 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup end return head,start,false,0,false end -function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local startchar=getchar(start) - local step=steps[1] - local kerns=step.coverage[startchar] - if not kerns then - elseif step.format=="pair" then - local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),dx,dy,w,h) - end - else - local k=setkern(start,factor,rlmode,kerns,injection) - if trace_kerns then - logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) + local kerns=mapping[startchar] + if kerns then + local format=currentlookup.format + if format=="single" then + local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns) + if trace_kerns then + logprocess("%s: shifting single %s by %s (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),format,dx,dy,w,h) + end + else + local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection) + if trace_kerns then + logprocess("%s: shifting single %s by %s %p",cref(dataset,sequence),gref(startchar),format,k) + end end + return head,start,true end end return head,start,false end -function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local snext=getnext(start) if snext then local startchar=getchar(start) - local step=steps[1] - local kerns=step.coverage[startchar] + local kerns=mapping[startchar] if kerns then local prev=start while snext do @@ -23248,46 +24256,47 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm if not nextchar then break end - local krn=kerns[nextchar] - if not krn and marks[nextchar] then + if skiphash and skiphash[nextchar] then prev=snext snext=getnext(snext) - elseif not krn then - break - elseif step.format=="pair" then - local a,b=krn[1],krn[2] - if optimizekerns then - if not b and a[1]==0 and a[2]==0 and a[4]==0 then - local k=setkern(snext,factor,rlmode,a[3],"injections") + else + local krn=kerns[nextchar] + if not krn then + break + end + local format=currentlookup.format + if format=="pair" then + local a,b=krn[1],krn[2] + if a==true then + elseif a then + local x,y,w,h=setposition(1,start,factor,rlmode,a,"injections") if trace_kerns then - logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) + local startchar=getchar(start) + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h) end - return head,start,true end - end - if a and #a>0 then - local startchar=getchar(start) - local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,"injections") - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h) + if b==true then + start=snext + elseif b then + local x,y,w,h=setposition(2,snext,factor,rlmode,b,"injections") + if trace_kerns then + local startchar=getchar(start) + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h) + end + start=snext + elseif forcepairadvance then + start=snext end - end - if b and #b>0 then - local startchar=getchar(start) - local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,"injections") + return head,start,true + elseif krn~=0 then + local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn) if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h) + logprocess("%s: inserting %s %p between %s and %s",cref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar)) end + return head,start,true + else + break end - return head,start,true - elseif krn~=0 then - local k=setkern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",cref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar)) - end - return head,start,true - else - break end end end @@ -23295,18 +24304,15 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm end return head,start,false end -function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local markchar=getchar(start) if marks[markchar] then - local markanchors=steps[1].coverage[markchar] + local markanchors=mapping[markchar] if markanchors then local base=getprev(start) if base then @@ -23341,8 +24347,8 @@ function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlooku if ma then local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", - cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) + logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head,start,true end @@ -23362,18 +24368,15 @@ function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlooku end return head,start,false end -function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local markchar=getchar(start) if marks[markchar] then - local markanchors=steps[1].coverage[markchar] + local markanchors=mapping[markchar] if markanchors then local base=getprev(start) if base then @@ -23411,8 +24414,8 @@ function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentl if ba then local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", - cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) + logprocess("%s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(dataset,sequence),a or bound,gref(markchar),gref(basechar),index,dx,dy) end return head,start,true end @@ -23433,18 +24436,15 @@ function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentl end return head,start,false end -function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local markchar=getchar(start) if marks[markchar] then - local markanchors=steps[1].coverage[markchar] + local markanchors=mapping[markchar] if markanchors then local base=getprev(start) local slc=getligaindex(start) @@ -23467,8 +24467,8 @@ function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlooku if ma then local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", - cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy) + logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head,start,true end @@ -23488,17 +24488,14 @@ function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlooku end return head,start,false end -function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode) - local steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - if nofsteps==0 then - reportzerosteps(dataset,sequence) - else + if mapping then local startchar=getchar(start) - local exitanchors=steps[1].coverage[startchar] + local exitanchors=mapping[startchar] if exitanchors then if marks[startchar] then if trace_cursive then @@ -23519,9 +24516,10 @@ function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup, if entry then entry=entry[2] if entry then - local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local r2lflag=sequence.flags[4] + local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) if trace_cursive then - logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,anchor,bound,mref(rlmode)) + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) end return head,start,true end @@ -23542,12 +24540,21 @@ end local function show_skip(dataset,sequence,char,ck,class) logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8] or ck[2]) end -local new_kern=nuts.pool.kern +local userkern=nuts.pool and nuts.pool.newkern +do if not userkern then + local thekern=nuts.new("kern",1) + local setkern=nuts.setkern + userkern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end local function checked(head) local current=head while current do if getid(current)==glue_code then - local kern=new_kern(getwidth(current)) + local kern=userkern(getwidth(current)) if head==current then local next=getnext(current) if next then @@ -23575,14 +24582,11 @@ local function setdiscchecked(d,pre,post,replace) setdisc(d,pre,post,replace) end local noflags={ false,false,false,false } -local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) +local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) local size=ck[5]-ck[4]+1 - local flags=sequence.flags or noflags - local done=false - local skipmark=flags[1] local chainlookups=ck[6] + local done=false if chainlookups then - local nofchainlookups=#chainlookups if size==1 then local chainlookup=chainlookups[1] for j=1,#chainlookup do @@ -23591,7 +24595,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) local chainproc=chainprocs[chainkind] if chainproc then local ok - head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,1) + head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) if ok then done=true end @@ -23601,13 +24605,14 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) end else local i=1 + local laststart=start + local nofchainlookups=#chainlookups while start do - if skipped then + if skiphash then while start do - local char=getchar(start) - local class=classes[char] - if class then - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then + local char=ischar(start,currentfont) + if char then + if skiphash and skiphash[char] then start=getnext(start) else break @@ -23625,10 +24630,11 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) local chainproc=chainprocs[chainkind] if chainproc then local ok,n - head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,i) + head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i) if ok then done=true if n and n>1 and i+n>nofchainlookups then + i=size break end end @@ -23641,14 +24647,18 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) if i>size or not start then break elseif start then + laststart=start start=getnext(start) end end + if not start then + start=laststart + end end else local replacements=ck[7] if replacements then - head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode) + head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode,skiphash) else done=true if trace_contexts then @@ -23658,7 +24668,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) end return head,start,done end -local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) +local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) if not start then return head,start,false end @@ -23851,7 +24861,7 @@ local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) head=lookaheaddisc end local pre,post,replace=getdisc(lookaheaddisc) - local new=copy_node_list(cf) + local new=copy_node_list(cf) local cnew=new if pre then setlink(find_node_tail(cf),pre) @@ -23871,14 +24881,14 @@ local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) end if not notmatchpre[lookaheaddisc] then local ok=false - cf,start,ok=chainrun(cf,start,cl,dataset,sequence,rlmode,ck,skipped) + cf,start,ok=chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck) if ok then done=true end end if not notmatchreplace[lookaheaddisc] then local ok=false - new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,ck,skipped) + new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) if ok then done=true end @@ -23889,8 +24899,8 @@ local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) setdisc(lookaheaddisc,cf,post,new) end start=getprev(lookaheaddisc) - sweephead[cf]=getnext(clast) - sweephead[new]=getnext(cl) + sweephead[cf]=getnext(clast) or false + sweephead[new]=getnext(cl) or false elseif backtrackdisc then local cf=getnext(backtrackdisc) local cl=start @@ -23906,10 +24916,7 @@ local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) break end end - if cnext then - setprev(cnext,backtrackdisc) - end - setnext(backtrackdisc,cnext) + setlink(backtrackdisc,cnext) setprev(cf) setnext(cl) local pre,post,replace,pretail,posttail,replacetail=getdisc(backtrackdisc,true) @@ -23924,14 +24931,14 @@ local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) end if not notmatchpost[backtrackdisc] then local ok=false - cf,start,ok=chainrun(cf,start,last,dataset,sequence,rlmode,ck,skipped) + cf,start,ok=chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck) if ok then done=true end end if not notmatchreplace[backtrackdisc] then local ok=false - new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,ck,skipped) + new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) if ok then done=true end @@ -23952,449 +24959,413 @@ local function chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) setdisc(backtrackdisc,pre,post,replace) end start=getprev(backtrackdisc) - sweephead[post]=getnext(clast) - sweephead[replace]=getnext(last) + sweephead[post]=getnext(clast) or false + sweephead[replace]=getnext(last) or false else local ok=false - head,start,ok=chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) + head,start,ok=chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) if ok then done=true end end return head,start,done end -local function chaintrac(head,start,dataset,sequence,rlmode,ck,skipped) +local function chaintrac(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode) local rule=ck[1] local lookuptype=ck[8] or ck[2] local nofseq=#ck[3] local first=ck[4] local last=ck[5] local char=getchar(start) - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", - cref(dataset,sequence),rule,gref(char),first-1,last-first+1,nofseq-last,lookuptype) + logwarning("%s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping", + cref(dataset,sequence),rule,match and "matches" or "nomatch", + gref(char),first-1,last-first+1,nofseq-last,lookuptype, + discseen and "" or "no ",sweepnode and "" or "not ") end -local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) +local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash) local sweepnode=sweepnode local sweeptype=sweeptype + local postreplace + local prereplace + local checkdisc + local discseen + if sweeptype then + if sweeptype=="replace" then + postreplace=true + prereplace=true + else + postreplace=sweeptype=="post" + prereplace=sweeptype=="pre" + end + checkdisc=getprev(head) + end local currentfont=currentfont - local diskseen=false - local checkdisc=sweeptype and getprev(head) - local flags=sequence.flags or noflags - local done=false - local skipmark=flags[1] - local skipligature=flags[2] - local skipbase=flags[3] - local markclass=sequence.markclass - local skipped=false + local skipped local startprev, startnext=getboth(start) - for k=1,#contexts do - local match=true - local current=start - local last=start + local done + local nofcontexts=contexts.n + local startchar=nofcontext==1 or ischar(start,currentfont) + for k=1,nofcontexts do local ck=contexts[k] local seq=ck[3] - local s=#seq - local size=1 - if s==1 then - local char=ischar(current,currentfont) - if char then - if not seq[1][char] then - match=false + local f=ck[4] + if not startchar or not seq[f][startchar] then + goto next + end + local s=seq.n + local l=ck[5] + local current=start + local last=start + if l>f then + local discfound + local n=f+1 + last=startnext + while n<=l do + if postreplace and not last then + last=getnext(sweepnode) + sweeptype=nil end - end - else - local f=ck[4] - local l=ck[5] - size=l-f+1 - if size>1 then - local discfound - local n=f+1 - last=startnext - while n<=l do - if not last and (sweeptype=="post" or sweeptype=="replace") then - last=getnext(sweepnode) - sweeptype=nil - end - if last then - local char,id=ischar(last,currentfont) - if char then - local class=classes[char] - if class then - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(dataset,sequence,char,ck,class) - end - last=getnext(last) - elseif seq[n][char] then - if n<l then - last=getnext(last) - end - n=n+1 - else - if discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - match=false - end - else - match=false - end - break - end + if last then + local char,id=ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + last=getnext(last) + elseif seq[n][char] then + if n<l then + last=getnext(last) + end + n=n+1 + elseif discfound then + notmatchreplace[discfound]=true + if notmatchpre[discfound] then + goto next else - if discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - match=false - end - else - match=false - end break end - elseif char==false then - if discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - match=false - end + else + goto next + end + elseif char==false then + if discfound then + notmatchreplace[discfound]=true + if notmatchpre[discfound] then + goto next else - match=false + break end - break - elseif id==disc_code then - diskseen=true - discfound=last - notmatchpre[last]=nil - notmatchpost[last]=true - notmatchreplace[last]=nil - local pre,post,replace=getdisc(last) - if pre then - local n=n - while pre do - if seq[n][getchar(pre)] then - n=n+1 - pre=getnext(pre) - if n>l then - break - end - else - notmatchpre[last]=true + else + goto next + end + elseif id==disc_code then + discseen=true + discfound=last + notmatchpre[last]=nil + notmatchpost[last]=true + notmatchreplace[last]=nil + local pre,post,replace=getdisc(last) + if pre then + local n=n + while pre do + if seq[n][getchar(pre)] then + n=n+1 + if n>l then break end - end - if n<=l then + pre=getnext(pre) + else notmatchpre[last]=true + break end - else - notmatchpre[last]=true end - if replace then - while replace do - if seq[n][getchar(replace)] then - n=n+1 - replace=getnext(replace) - if n>l then - break - end + else + notmatchpre[last]=true + end + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + if n>l then + break + end + replace=getnext(replace) + else + notmatchreplace[last]=true + if notmatchpre[last] then + goto next else - notmatchreplace[last]=true - if notmatchpre[last] then - match=false - end break end end - if notmatchpre[last] then - match=false - end end - last=getnext(last) - else - match=false - break + if notmatchpre[last] then + goto next + end end + last=getnext(last) else - match=false - break + goto next end + else + goto next end end - if match and f>1 then - if startprev then - local prev=startprev - if prev==checkdisc and (sweeptype=="pre" or sweeptype=="replace") then - prev=getprev(sweepnode) - end - if prev then - local discfound - local n=f-1 - while n>=1 do - if prev then - local char,id=ischar(prev,currentfont) - if char then - local class=classes[char] - if class then - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(dataset,sequence,char,ck,class) - end - prev=getprev(prev) - elseif seq[n][char] then - if n>1 then - prev=getprev(prev) - end - n=n-1 - else - if discfound then - notmatchreplace[discfound]=true - if notmatchpost[discfound] then - match=false - end - else - match=false - end - break - end + end + if f>1 then + if startprev then + local prev=startprev + if prereplace and prev==checkdisc then + prev=getprev(sweepnode) + end + if prev then + local discfound + local n=f-1 + while n>=1 do + if prev then + local char,id=ischar(prev,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev=getprev(prev) + elseif seq[n][char] then + if n>1 then + prev=getprev(prev) + end + n=n-1 + elseif discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next else - if discfound then - notmatchreplace[discfound]=true - if notmatchpost[discfound] then - match=false - end - else - match=false - end break end - elseif char==false then - if discfound then - notmatchreplace[discfound]=true - if notmatchpost[discfound] then - match=false - end - else - match=false + else + goto next + end + elseif char==false then + if discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next end - break - elseif id==disc_code then - diskseen=true - discfound=prev - notmatchpre[prev]=true - notmatchpost[prev]=nil - notmatchreplace[prev]=nil - local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true) - if pre~=start and post~=start and replace~=start then - if post then - local n=n - while posttail do - if seq[n][getchar(posttail)] then - n=n-1 - if posttail==post then - break - else - posttail=getprev(posttail) - if n<1 then - break - end - end - else - notmatchpost[prev]=true + else + goto next + end + break + elseif id==disc_code then + discseen=true + discfound=prev + notmatchpre[prev]=true + notmatchpost[prev]=nil + notmatchreplace[prev]=nil + local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true) + if pre~=start and post~=start and replace~=start then + if post then + local n=n + while posttail do + if seq[n][getchar(posttail)] then + n=n-1 + if posttail==post or n<1 then break + else + posttail=getprev(posttail) end - end - if n>=1 then + else notmatchpost[prev]=true + break end - else + end + if n>=1 then notmatchpost[prev]=true end - if replace then - while replacetail do - if seq[n][getchar(replacetail)] then - n=n-1 - if replacetail==replace then - break - else - replacetail=getprev(replacetail) - if n<1 then - break - end - end + else + notmatchpost[prev]=true + end + if replace then + while replacetail do + if seq[n][getchar(replacetail)] then + n=n-1 + if replacetail==replace or n<1 then + break + else + replacetail=getprev(replacetail) + end + else + notmatchreplace[prev]=true + if notmatchpost[prev] then + goto next else - notmatchreplace[prev]=true - if notmatchpost[prev] then - match=false - end break end end - if not match then - break - end end + else + notmatchreplace[prev]=true end - prev=getprev(prev) - elseif id==glue_code and seq[n][32] and isspace(prev,threshold,id) then + end + prev=getprev(prev) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then n=n-1 prev=getprev(prev) else - match=false - break + goto next end + elseif seq[n][0xFFFC] then + n=n-1 + prev=getprev(prev) else - match=false - break + goto next end + else + goto next end - else - match=false end else - match=false + goto next end + else + goto next end - if match and s>l then - local current=last and getnext(last) - if not current and (sweeptype=="post" or sweeptype=="replace") then - current=getnext(sweepnode) - end - if current then - local discfound - local n=l+1 - while n<=s do - if current then - local char,id=ischar(current,currentfont) - if char then - local class=classes[char] - if class then - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(dataset,sequence,char,ck,class) - end - current=getnext(current) - elseif seq[n][char] then - if n<s then - current=getnext(current) - end - n=n+1 - else - if discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - match=false - end - else - match=false - end - break - end + end + if s>l then + local current=last and getnext(last) + if not current and postreplace then + current=getnext(sweepnode) + end + if current then + local discfound + local n=l+1 + while n<=s do + if current then + local char,id=ischar(current,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + current=getnext(current) + elseif seq[n][char] then + if n<s then + current=getnext(current) + end + n=n+1 + elseif discfound then + notmatchreplace[discfound]=true + if notmatchpre[discfound] then + goto next else - if discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - match=false - end - else - match=false - end break end - elseif char==false then - if discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - match=false - end + else + goto next + end + elseif char==false then + if discfound then + notmatchreplace[discfound]=true + if notmatchpre[discfound] then + goto next else - match=false + break end - break - elseif id==disc_code then - diskseen=true - discfound=current - notmatchpre[current]=nil - notmatchpost[current]=true - notmatchreplace[current]=nil - local pre,post,replace=getdisc(current) - if pre then - local n=n - while pre do - if seq[n][getchar(pre)] then - n=n+1 - pre=getnext(pre) - if n>s then - break - end - else - notmatchpre[current]=true + else + goto next + end + elseif id==disc_code then + discseen=true + discfound=current + notmatchpre[current]=nil + notmatchpost[current]=true + notmatchreplace[current]=nil + local pre,post,replace=getdisc(current) + if pre then + local n=n + while pre do + if seq[n][getchar(pre)] then + n=n+1 + if n>s then break + else + pre=getnext(pre) end - end - if n<=s then + else notmatchpre[current]=true + break end - else + end + if n<=s then notmatchpre[current]=true end - if replace then - while replace do - if seq[n][getchar(replace)] then - n=n+1 + else + notmatchpre[current]=true + end + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + if n>s then + break + else replace=getnext(replace) - if n>s then - break - end + end + else + notmatchreplace[current]=true + if notmatchpre[current] then + goto next else - notmatchreplace[current]=true - if not notmatchpre[current] then - match=false - end break end end - if not match then - break - end - else end - current=getnext(current) - elseif id==glue_code and seq[n][32] and isspace(current,threshold,id) then + else + notmatchreplace[current]=true + end + current=getnext(current) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[current]) or sn[0xFFFC] then n=n+1 current=getnext(current) else - match=false - break + goto next end + elseif seq[n][0xFFFC] then + n=n+1 + current=getnext(current) else - match=false - break + goto next end + else + goto next end - else - match=false end - end - end - if match then - if trace_contexts then - chaintrac(head,start,dataset,sequence,rlmode,ck,skipped) - end - if diskseen or sweepnode then - head,start,done=chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) else - head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) - end - if done then - break + goto next end end + if trace_contexts then + chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode) + end + if discseen or sweepnode then + head,start,done=chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck) + else + head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck) + end + if done then + break + end + ::next:: end - if diskseen then + if discseen then notmatchpre={} notmatchpost={} notmatchreplace={} @@ -24406,13 +25377,18 @@ handlers.gsub_contextchain=handle_contextchain handlers.gsub_reversecontextchain=handle_contextchain handlers.gpos_contextchain=handle_contextchain handlers.gpos_context=handle_contextchain -local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode) +local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) local steps=currentlookup.steps local nofsteps=currentlookup.nofsteps if nofsteps>1 then reportmoresteps(dataset,sequence) end - return handle_contextchain(head,start,dataset,sequence,currentlookup,rlmode) + local l=steps[1].coverage[getchar(start)] + if l then + return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + else + return head,start,false + end end chainprocs.gsub_context=chained_contextchain chainprocs.gsub_contextchain=chained_contextchain @@ -24420,22 +25396,17 @@ chainprocs.gsub_reversecontextchain=chained_contextchain chainprocs.gpos_contextchain=chained_contextchain chainprocs.gpos_context=chained_contextchain local missing=setmetatableindex("table") +local logwarning=report_process +local resolved={} local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end end report_process(...) end -local logwarning=report_process -local function report_missing_coverage(dataset,sequence) - local t=missing[currentfont] - if not t[sequence] then - t[sequence]=true - logwarning("missing coverage for feature %a, lookup %a, type %a, font %a, name %a", - dataset[4],sequence.name,sequence.type,currentfont,tfmdata.properties.fullname) - end -end -local resolved={} local sequencelists=setmetatableindex(function(t,font) local sequences=fontdata[font].resources.sequences if not sequences or not next(sequences) then @@ -24549,13 +25520,12 @@ local function kernrun(disc,k_run,font,attr,...) done=true end if prev then - local nest=getprev(pre) setlink(prev,pre) if k_run(prevmarks,"preinjections",pre,font,attr,...) then done=true end - setprev(pre,nest) - setnext(prev,disc) + setprev(pre) + setlink(prev,disc) end end if post then @@ -24568,7 +25538,7 @@ local function kernrun(disc,k_run,font,attr,...) done=true end setnext(posttail) - setprev(next,disc) + setlink(disc,next) end end if replace then @@ -24576,13 +25546,12 @@ local function kernrun(disc,k_run,font,attr,...) done=true end if prev then - local nest=getprev(replace) setlink(prev,replace) if k_run(prevmarks,"replaceinjections",replace,font,attr,...) then done=true end - setprev(replace,nest) - setnext(prev,disc) + setprev(replace) + setlink(prev,disc) end if next then setlink(replacetail,next) @@ -24590,7 +25559,7 @@ local function kernrun(disc,k_run,font,attr,...) done=true end setnext(replacetail) - setprev(next,disc) + setlink(disc,next) end elseif prev and next then setlink(prev,next) @@ -24599,6 +25568,9 @@ local function kernrun(disc,k_run,font,attr,...) end setlink(prev,disc,next) end + if done and trace_testruns then + report_disc("done",disc) + end return nextstart,done end local function comprun(disc,c_run,...) @@ -24637,6 +25609,9 @@ local function comprun(disc,c_run,...) sweepnode=nil sweeptype=nil if renewed then + if trace_testruns then + report_disc("done",disc) + end setdisc(disc,pre,post,replace) end return getnext(disc),renewed @@ -24650,7 +25625,7 @@ local function testrun(disc,t_run,c_run,...) return end local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true) - local done=false + local renewed=false if (post or replace) and prev then if post then setlink(posttail,next) @@ -24664,19 +25639,22 @@ local function testrun(disc,t_run,c_run,...) end local d_post=t_run(post,next,...) local d_replace=t_run(replace,next,...) - if (d_post and d_post>0) or (d_replace and d_replace>0) then - local d=d_replace or d_post - if d_post and d<d_post then - d=d_post - end - local head,tail=getnext(disc),disc + if d_post>0 or d_replace>0 then + local d=d_replace>d_post and d_replace or d_post + local head=getnext(disc) + local tail=head for i=1,d do - tail=getnext(tail) - if getid(tail)==disc_code then - head,tail=flattendisk(head,tail) + local nx=getnext(tail) + local id=getid(nx) + if id==disc_code then + head,tail=flattendisk(head,nx) + elseif id==glyph_code then + tail=nx + else + break end end - local next=getnext(tail) + next=getnext(tail) setnext(tail) setprev(head) local new=copy_node_list(head) @@ -24690,23 +25668,23 @@ local function testrun(disc,t_run,c_run,...) else replace=new end - setlink(disc,next) else if posttail then setnext(posttail) else post=nil end - setnext(replacetail) if replacetail then setnext(replacetail) else replace=nil end - setprev(next,disc) end + setlink(disc,next) + end + if trace_testruns then + report_disc("more",disc) end - local renewed=false if pre then sweepnode=disc sweeptype="pre" @@ -24738,23 +25716,24 @@ local function testrun(disc,t_run,c_run,...) sweeptype=nil if renewed then setdisc(disc,pre,post,replace) - return next,true - else - return next,done + if trace_testruns then + report_disc("done",disc) + end end + return getnext(disc),renewed end local nesting=0 -local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) local done=false local sweep=sweephead[head] if sweep then start=sweep - sweephead[head]=nil + sweephead[head]=false else start=head end while start do - local char=ischar(start,font) + local char,id=ischar(start,font) if char then local a if attr then @@ -24764,7 +25743,7 @@ local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlm local lookupmatch=lookupcache[char] if lookupmatch then local ok - head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) if ok then done=true end @@ -24816,35 +25795,41 @@ local function t_run_single(start,stop,font,attr,lookupcache) local l=nil local d=0 while s do - local lg=lookupmatch[getchar(s)] - if lg then - if sstop then - d=1 - elseif d>0 then - d=d+1 - end - l=lg - s=getnext(s) - sstop=s==stop - if not s then - s=ss - ss=nil - end - while getid(s)==disc_code do - ss=getnext(s) - s=getfield(s,"replace") + local char=ischar(s,font) + if char then + local lg=lookupmatch[char] + if lg then + if sstop then + d=1 + elseif d>0 then + d=d+1 + end + l=lg + s=getnext(s) + sstop=s==stop if not s then s=ss ss=nil end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + else + break end else break end end - if l and l.ligature then + if l and l.ligature then lastd=d end + else end else end @@ -24856,8 +25841,9 @@ local function t_run_single(start,stop,font,attr,lookupcache) break end end + return 0 end -local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) local a if attr then a=getattr(sub,0) @@ -24871,7 +25857,7 @@ local function k_run_single(sub,injection,last,font,attr,lookupcache,step,datase if char then local lookupmatch=lookupcache[char] if lookupmatch then - local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection) + local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) if ok then return true end @@ -24880,12 +25866,12 @@ local function k_run_single(sub,injection,last,font,attr,lookupcache,step,datase end end end -local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) +local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) local done=false local sweep=sweephead[head] if sweep then start=sweep - sweephead[head]=nil + sweephead[head]=false else start=head end @@ -24900,20 +25886,16 @@ local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlm for i=1,nofsteps do local step=steps[i] local lookupcache=step.coverage - if lookupcache then - local lookupmatch=lookupcache[char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done=true - break - elseif not start then - break - end + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done=true + break + elseif not start then + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -24946,28 +25928,29 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) for i=1,nofsteps do local step=steps[i] local lookupcache=step.coverage - if lookupcache then - local lookupmatch=lookupcache[char] - if lookupmatch then - local s=startnext - local ss=nil - local sstop=s==stop + local lookupmatch=lookupcache[char] + if lookupmatch then + local s=startnext + local ss=nil + local sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") if not s then s=ss ss=nil end - while getid(s)==disc_code do - ss=getnext(s) - s=getfield(s,"replace") - if not s then - s=ss - ss=nil - end - end - local l=nil - local d=0 - while s do - local lg=lookupmatch[getchar(s)] + end + local l=nil + local d=0 + while s do + local char=ischar(s) + if char then + local lg=lookupmatch[char] if lg then if sstop then d=1 @@ -24992,13 +25975,13 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) else break end - end - if l and l.ligature then - lastd=d + else + break end end - else - report_missing_coverage(dataset,sequence) + if l and l.ligature then + lastd=d + end end end else @@ -25011,8 +25994,9 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) break end end + return 0 end -local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) +local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) local a if attr then a=getattr(sub,0) @@ -25027,16 +26011,12 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase for i=1,nofsteps do local step=steps[i] local lookupcache=step.coverage - if lookupcache then - local lookupmatch=lookupcache[char] - if lookupmatch then - local h,d,ok=handler(head,n,dataset,sequence,lookupmatch,step,rlmode,i,injection) - if ok then - return true - end + local lookupmatch=lookupcache[char] + if lookupmatch then + local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) + if ok then + return true end - else - report_missing_coverage(dataset,sequence) end end end @@ -25044,182 +26024,209 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase end end local function txtdirstate(start,stack,top,rlparmode) + local nxt=getnext(start) local dir=getdir(start) - local new=1 if dir=="+TRT" then top=top+1 stack[top]=dir - new=-1 + return nxt,top,-1 elseif dir=="+TLT" then top=top+1 stack[top]=dir + return nxt,top,1 elseif dir=="-TRT" or dir=="-TLT" then - top=top-1 - if stack[top]=="+TRT" then - new=-1 + if top==1 then + return nxt,0,rlparmode + else + top=top-1 + if stack[top]=="+TRT" then + return nxt,top,-1 + else + return nxt,top,1 + end end else - new=rlparmode + return nxt,top,rlparmode end - if trace_directions then - report_process("directions after txtdir %a: parmode %a, txtmode %a, level %a",dir,mref(rlparmode),mref(new),top) - end - return getnext(start),top,new end local function pardirstate(start) + local nxt=getnext(start) local dir=getdir(start) - local new=0 if dir=="TLT" then - new=1 + return nxt,1,1 elseif dir=="TRT" then - new=-1 - end - if trace_directions then - report_process("directions after pardir %a: parmode %a",dir,mref(new)) + return nxt,-1,-1 + else + return nxt,0,0 end - return getnext(start),new,new end otf.helpers=otf.helpers or {} otf.helpers.txtdirstate=txtdirstate otf.helpers.pardirstate=pardirstate -local function featuresprocessor(head,font,attr,direction) - local sequences=sequencelists[font] - if not sequencelists then - return head,false - end - nesting=nesting+1 - if nesting==1 then - currentfont=font - tfmdata=fontdata[font] - descriptions=tfmdata.descriptions - characters=tfmdata.characters - local resources=tfmdata.resources - marks=resources.marks - classes=resources.classes - threshold, - factor=getthreshold(font) - checkmarks=tfmdata.properties.checkmarks - elseif currentfont~=font then - report_warning("nested call with a different font, level %s, quitting",nesting) - nesting=nesting-1 - return head,false - end - head=tonut(head) - if trace_steps then - checkstep(head) - end - local initialrl=direction=="TRT" and -1 or 0 - local done=false - local datasets=otf.dataset(tfmdata,font,attr) - local dirstack={} - sweephead={} - for s=1,#datasets do - local dataset=datasets[s] - local attribute=dataset[2] - local sequence=dataset[3] - local rlparmode=initialrl - local topstack=0 - local typ=sequence.type - local gpossing=typ=="gpos_single" or typ=="gpos_pair" - local handler=handlers[typ] - local steps=sequence.steps - local nofsteps=sequence.nofsteps - if not steps then - local h,d,ok=handler(head,head,dataset,sequence,nil,nil,nil,0,font,attr) - if ok then - done=true - if h then +do + local fastdisc=true + local testdics=false + directives.register("otf.fastdisc",function(v) fastdisc=v end) + local otfdataset=nil + local getfastdisc={ __index=function(t,k) + local v=usesfont(k,currentfont) + t[k]=v + return v + end } + local getfastspace={ __index=function(t,k) + local v=isspace(k,threshold) or false + t[k]=v + return v + end } + function otf.featuresprocessor(head,font,attr,direction,n) + local sequences=sequencelists[font] + nesting=nesting+1 + if nesting==1 then + currentfont=font + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + local resources=tfmdata.resources + marks=resources.marks + classes=resources.classes + threshold, + factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks + if not otfdataset then + otfdataset=otf.dataset + end + discs=fastdisc and n and n>1 and setmetatable({},getfastdisc) + spaces=setmetatable({},getfastspace) + elseif currentfont~=font then + report_warning("nested call with a different font, level %s, quitting",nesting) + nesting=nesting-1 + return head,false + end + local head=tonut(head) + if trace_steps then + checkstep(head) + end + local initialrl=direction=="TRT" and -1 or 0 + local done=false + local datasets=otfdataset(tfmdata,font,attr) + local dirstack={} + sweephead={} + for s=1,#datasets do + local dataset=datasets[s] + local attribute=dataset[2] + local sequence=dataset[3] + local rlparmode=initialrl + local topstack=0 + local typ=sequence.type + local gpossing=typ=="gpos_single" or typ=="gpos_pair" + local forcetestrun=typ=="gsub_ligature" + local handler=handlers[typ] + local steps=sequence.steps + local nofsteps=sequence.nofsteps + local skiphash=sequence.skiphash + if not steps then + local h,ok=handler(head,dataset,sequence,initialrl,font,attr) + if ok then + done=true + end + if h and h~=head then head=h end - end - elseif typ=="gsub_reversecontextchain" then - local start=find_node_tail(head) - local rlmode=0 - while start do - local char=ischar(start,font) - if char then - local a - if attr then - a=getattr(start,0) - end - if not a or (a==attr) then - for i=1,nofsteps do - local step=steps[i] - local lookupcache=step.coverage - if lookupcache then - local lookupmatch=lookupcache[char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done=true - break + elseif typ=="gsub_reversecontextchain" then + local start=find_node_tail(head) + local rlmode=0 + local merged=steps.merged + while start do + local char=ischar(start,font) + if char then + local m=merged[char] + if m then + local a + if attr then + a=getattr(start,0) + end + if not a or (a==attr) then + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done=true + break + end end end + if start then + start=getprev(start) + end else - report_missing_coverage(dataset,sequence) + start=getprev(start) end - end - if start then + else start=getprev(start) end else start=getprev(start) end - else - start=getprev(start) end - end - else - local start=head - local rlmode=initialrl - if nofsteps==1 then - local step=steps[1] - local lookupcache=step.coverage - if not lookupcache then - report_missing_coverage(dataset,sequence) - else + else + local start=head + local rlmode=initialrl + if nofsteps==1 then + local step=steps[1] + local lookupcache=step.coverage while start do local char,id=ischar(start,font) if char then - local a - if attr then - if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then - a=true - end - elseif not attribute or getprop(start,a_state)==attribute then - a=true - end - if a then + if skiphash and skiphash[char] then + start=getnext(start) + else local lookupmatch=lookupcache[char] if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) - if ok then - done=true + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then + a=true + end + elseif not attribute or getprop(start,a_state)==attribute then + a=true end - end - if start then + if a then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done=true + end + if start then + start=getnext(start) + end + else + start=getnext(start) + end + else start=getnext(start) end - else - start=getnext(start) end - elseif char==false then - start=getnext(start) - elseif id==glue_code then + elseif char==false or id==glue_code then start=getnext(start) elseif id==disc_code then - local ok - if gpossing then - start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - elseif typ=="gsub_ligature" then - start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) + if not discs or discs[start]==true then + local ok + if gpossing then + start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + else + start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + end + if ok then + done=true + end else - start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - end - if ok then - done=true + start=getnext(start) end elseif id==math_code then start=getnext(end_of_math(start)) @@ -25231,80 +26238,161 @@ local function featuresprocessor(head,font,attr,direction) start=getnext(start) end end - end - else - while start do - local char,id=ischar(start,font) - if char then - local a - if attr then - if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then - a=true - end - elseif not attribute or getprop(start,a_state)==attribute then - a=true - end - if a then - for i=1,nofsteps do - local step=steps[i] - local lookupcache=step.coverage - if lookupcache then - local lookupmatch=lookupcache[char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done=true - break - elseif not start then - break + else + local merged=steps.merged + while start do + local char,id=ischar(start,font) + if char then + if skiphash and skiphash[char] then + start=getnext(start) + else + local m=merged[char] + if m then + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then + a=true end + elseif not attribute or getprop(start,a_state)==attribute then + a=true + end + if a then + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done=true + break + elseif not start then + break + end + end + end + if start then + start=getnext(start) + end + else + start=getnext(start) end else - report_missing_coverage(dataset,sequence) + start=getnext(start) end end - if start then + elseif char==false or id==glue_code then + start=getnext(start) + elseif id==disc_code then + if not discs or discs[start]==true then + local ok + if gpossing then + start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + else + start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + end + if ok then + done=true + end + else start=getnext(start) end + elseif id==math_code then + start=getnext(end_of_math(start)) + elseif id==dir_code then + start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode) + elseif id==localpar_code then + start,rlparmode,rlmode=pardirstate(start) else start=getnext(start) end - elseif char==false then - start=getnext(start) - elseif id==glue_code then + end + end + end + if trace_steps then + registerstep(head) + end + end + nesting=nesting-1 + head=tonode(head) + return head,done + end + function otf.datasetpositionprocessor(head,font,direction,dataset) + currentfont=font + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + local resources=tfmdata.resources + marks=resources.marks + classes=resources.classes + threshold, + factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks + if type(dataset)=="number" then + dataset=otfdataset(tfmdata,font,0)[dataset] + end + local sequence=dataset[3] + local typ=sequence.type + local handler=handlers[typ] + local steps=sequence.steps + local nofsteps=sequence.nofsteps + local head=tonut(head) + local done=false + local dirstack={} + local start=head + local initialrl=direction=="TRT" and -1 or 0 + local rlmode=initialrl + local rlparmode=initialrl + local topstack=0 + local merged=steps.merged + local position=0 + while start do + local char,id=ischar(start,font) + if char then + position=position+1 + local m=merged[char] + if m then + if skiphash and skiphash[char] then start=getnext(start) - elseif id==disc_code then - local ok - if gpossing then - start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) - elseif typ=="gsub_ligature" then - start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) - else - start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) + else + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + elseif not start then + break + end + end end - if ok then - done=true + if start then + start=getnext(start) end - elseif id==math_code then - start=getnext(end_of_math(start)) - elseif id==dir_code then - start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode) - elseif id==localpar_code then - start,rlparmode,rlmode=pardirstate(start) - else - start=getnext(start) end + else + start=getnext(start) end + elseif char==false or id==glue_code then + start=getnext(start) + elseif id==math_code then + start=getnext(end_of_math(start)) + elseif id==dir_code then + start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode) + elseif id==localpar_code then + start,rlparmode,rlmode=pardirstate(start) + else + start=getnext(start) end end - if trace_steps then - registerstep(head) - end + return tonode(head) end - nesting=nesting-1 - head=tonode(head) - return head,done end local plugins={} otf.plugins=plugins @@ -25313,24 +26401,24 @@ function otf.registerplugin(name,f) plugins[name]={ name,f } end end -local function plugininitializer(tfmdata,value) +function otf.plugininitializer(tfmdata,value) if type(value)=="string" then tfmdata.shared.plugin=plugins[value] end end -local function pluginprocessor(head,font) +function otf.pluginprocessor(head,font,attr,direction) local s=fontdata[font].shared local p=s and s.plugin if p then if trace_plugins then report_process("applying plugin %a",p[1]) end - return p[2](head,font) + return p[2](head,font,attr,direction) else return head,false end end -local function featuresinitializer(tfmdata,value) +function otf.featuresinitializer(tfmdata,value) end registerotffeature { name="features", @@ -25338,53 +26426,87 @@ registerotffeature { default=true, initializers={ position=1, - node=featuresinitializer, - plug=plugininitializer, + node=otf.featuresinitializer, + plug=otf.plugininitializer, }, processors={ - node=featuresprocessor, - plug=pluginprocessor, + node=otf.featuresprocessor, + plug=otf.pluginprocessor, } } -otf.nodemodeinitializer=featuresinitializer -otf.featuresprocessor=featuresprocessor otf.handlers=handlers local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.exit() end +local tag="kern" if fontfeatures then - function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) local features=fontfeatures[font] - local enabled=features and features.spacekern and features.kern + local enabled=features and features.spacekern and features[tag] if enabled then setspacekerns(font,sequence) end - return head,start,enabled + return head,enabled end else - function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) local shared=fontdata[font].shared local features=shared and shared.features - local enabled=features and features.spacekern and features.kern + local enabled=features and features.spacekern and features[tag] if enabled then setspacekerns(font,sequence) end - return head,start,enabled + return head,enabled end end local function hasspacekerns(data) - local sequences=data.resources.sequences - for i=1,#sequences do - local sequence=sequences[i] - local steps=sequence.steps - if steps and sequence.features.kern then - for i=1,#steps do - local coverage=steps[i].coverage - if not coverage then - elseif coverage[32] then - return true - else - for k,v in next,coverage do - if v[32] then - return true + local resources=data.resources + local sequences=resources.sequences + local validgpos=resources.features.gpos + if validgpos and sequences then + for i=1,#sequences do + local sequence=sequences[i] + local steps=sequence.steps + if steps and sequence.features[tag] then + local kind=sequence.type + if kind=="gpos_pair" or kind=="gpos_single" then + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + local rules=step.rules + if rules then + elseif not coverage then + elseif kind=="gpos_single" then + elseif kind=="gpos_pair" then + local format=step.format + if format=="move" or format=="kern" then + local kerns=coverage[32] + if kerns then + return true + end + for k,v in next,coverage do + if v[32] then + return true + end + end + elseif format=="pair" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + local one=v[1] + if one and one~=true then + return true + end + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + local one=kern[1] + if one and one~=true then + return true + end + end + end + end end end end @@ -25402,103 +26524,110 @@ otf.readers.registerextender { local function spaceinitializer(tfmdata,value) local resources=tfmdata.resources local spacekerns=resources and resources.spacekerns - local properties=tfmdata.properties if value and spacekerns==nil then + local rawdata=tfmdata.shared and tfmdata.shared.rawdata + local properties=rawdata.properties if properties and properties.hasspacekerns then local sequences=resources.sequences - local left={} - local right={} - local last=0 - local feat=nil - for i=1,#sequences do - local sequence=sequences[i] - local steps=sequence.steps - if steps then - local kern=sequence.features.kern - if kern then - if feat then - for script,languages in next,kern do - local f=feat[script] - if f then - for l in next,languages do - f[l]=true - end - else - feat[script]=languages - end - end - else - feat=kern - end - for i=1,#steps do - local step=steps[i] - local coverage=step.coverage - local rules=step.rules - local format=step.format - if rules then - elseif coverage then - local single=format==gpos_single - local kerns=coverage[32] - if kerns then - for k,v in next,kerns do - if type(v)~="table" then - right[k]=v - elseif single then - right[k]=v[3] - else - local one=v[1] - if one then - right[k]=one[3] + local validgpos=resources.features.gpos + if validgpos and sequences then + local left={} + local right={} + local last=0 + local feat=nil + for i=1,#sequences do + local sequence=sequences[i] + local steps=sequence.steps + if steps then + local kern=sequence.features[tag] + if kern then + local kind=sequence.type + if kind=="gpos_pair" or kind=="gpos_single" then + if feat then + for script,languages in next,kern do + local f=feat[script] + if f then + for l in next,languages do + f[l]=true end + else + feat[script]=languages end end + else + feat=kern end - for k,v in next,coverage do - local kern=v[32] - if kern then - if type(kern)~="table" then - left[k]=kern - elseif single then - left[k]=kern[3] - else - local one=kern[1] - if one then - left[k]=one[3] + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + local rules=step.rules + if rules then + elseif not coverage then + elseif kind=="gpos_single" then + elseif kind=="gpos_pair" then + local format=step.format + if format=="move" or format=="kern" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + right[k]=v + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + left[k]=kern + end + end + elseif format=="pair" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + local one=v[1] + if one and one~=true then + right[k]=one[3] + end + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + local one=kern[1] + if one and one~=true then + left[k]=one[3] + end + end end end end end + last=i end + else end - last=i end - else end - end - left=next(left) and left or false - right=next(right) and right or false - if left or right then - spacekerns={ - left=left, - right=right, - } - if last>0 then - local triggersequence={ - features={ kern=feat or { dflt={ dflt=true,} } }, - flags=noflags, - name="trigger_space_kerns", - order={ "kern" }, - type="trigger_space_kerns", + left=next(left) and left or false + right=next(right) and right or false + if left or right then + spacekerns={ left=left, right=right, } - insert(sequences,last,triggersequence) + if last>0 then + local triggersequence={ + features={ [tag]=feat or { dflt={ dflt=true,} } }, + flags=noflags, + name="trigger_space_kerns", + order={ tag }, + type="trigger_space_kerns", + left=left, + right=right, + } + insert(sequences,last,triggersequence) + end end - else - spacekerns=false end - else - spacekerns=false end resources.spacekerns=spacekerns end @@ -25803,7 +26932,6 @@ local sequence_reorder_matras={ nofsteps=1, steps={ { - osdstep=true, coverage=pre_mark, } } @@ -25817,7 +26945,6 @@ local sequence_reorder_reph={ nofsteps=1, steps={ { - osdstep=true, coverage={}, } } @@ -25831,7 +26958,6 @@ local sequence_reorder_pre_base_reordering_consonants={ nofsteps=1, steps={ { - osdstep=true, coverage={}, } } @@ -25844,7 +26970,7 @@ local sequence_remove_joiners={ type="devanagari_remove_joiners", nofsteps=1, steps={ - { osdstep=true, + { coverage=both_joiners_true, }, } @@ -25953,24 +27079,21 @@ local function initializedevanagi(tfmdata) if coverage then local reph=false if kind=="rphf" then - if true then - for k,v in next,ra do - local r=coverage[k] - if r then - local h=false - for k,v in next,halant do - local h=r[k] - if h then - reph=h.ligature or false - break - end - end - if reph then + for k,v in next,ra do + local r=coverage[k] + if r then + local h=false + for k,v in next,halant do + local h=r[k] + if h then + reph=h.ligature or false break end end + if reph then + break + end end - else end end seqsubset[#seqsubset+1]={ kind,coverage,reph } @@ -26729,7 +27852,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) next=getnext(current) local tmp=getnext(next) local changestop=next==stop - setnext(next,nil) + setnext(next) setprop(current,a_state,s_pref) current=processcharacters(current,font) setprop(current,a_state,s_blwf) @@ -27489,20 +28612,20 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-ocl']={ version=1.001, - comment="companion to font-otf.lua (context)", + comment="companion to font-ini.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local tostring,next,format=tostring,next,string.format +local tostring,tonumber,next=tostring,tonumber,next local round,max=math.round,math.round local sortedkeys,sortedhash=table.sortedkeys,table.sortedhash local setmetatableindex=table.setmetatableindex local formatters=string.formatters local tounicode=fonts.mappings.tounicode local otf=fonts.handlers.otf -local f_color=formatters["pdf:direct:%f %f %f rg"] -local f_gray=formatters["pdf:direct:%f g"] +local f_color=formatters["%.3f %.3f %.3f rg"] +local f_gray=formatters["%.3f g"] if context then local startactualtext=nil local stopactualtext=nil @@ -27523,7 +28646,7 @@ else end local sharedpalettes={} local hash=setmetatableindex(function(t,k) - local v={ "special",k } + local v={ "pdf","direct",k } t[k]=v return v end) @@ -27547,11 +28670,11 @@ if context then t=transparencies[v] end if c and t then - values[i]=hash["pdf:direct:"..lpdf.color(1,c).." "..lpdf.transparency(t)] + values[i]=hash[lpdf.color(1,c).." "..lpdf.transparency(t)] elseif c then - values[i]=hash["pdf:direct:"..lpdf.color(1,c)] + values[i]=hash[lpdf.color(1,c)] elseif t then - values[i]=hash["pdf:direct:"..lpdf.color(1,t)] + values[i]=hash[lpdf.color(1,t)] end end end @@ -27582,6 +28705,12 @@ local function convert(t,k) t[k]=v return v end +local start={ "pdf","mode","font" } +local push={ "pdf","page","q" } +local pop={ "pdf","page","Q" } +if not LUATEXFUNCTIONALITY or LUATEXFUNCTIONALITY<6472 then + start={ "nop" } +end local function initializecolr(tfmdata,kind,value) if value then local resources=tfmdata.resources @@ -27612,12 +28741,10 @@ local function initializecolr(tfmdata,kind,value) local getactualtext=otf.getactualtext local default=colorvalues[#colorvalues] local b,e=getactualtext(tounicode(0xFFFD)) - local start={ "special","pdf:page:q" } - local stop={ "special","pdf:raw:Q" } - local actualb={ "special","pdf:page:"..b } - local actuale={ "special","pdf:page:"..e } + local actualb={ "pdf","page",b } + local actuale={ "pdf","page",e } local cache=setmetatableindex(function(t,k) - local v={ "char",k } + local v={ "char",k } t[k]=v return v end) @@ -27632,14 +28759,20 @@ local function initializecolr(tfmdata,kind,value) local goback=w~=0 and widths[w] or nil local t={ start, - not u and actualb or { "special","pdf:raw:"..getactualtext(tounicode(u)) } + not u and actualb or { "pdf","page",(getactualtext(tounicode(u))) } } local n=2 local l=nil + local f=false for i=1,s do local entry=colorlist[i] local v=colorvalues[entry.class] or default if v and l~=v then + if f then + n=n+1 t[n]=pop + end + n=n+1 t[n]=push + f=true n=n+1 t[n]=v l=v end @@ -27648,8 +28781,10 @@ local function initializecolr(tfmdata,kind,value) n=n+1 t[n]=goback end end + if f then + n=n+1 t[n]=pop + end n=n+1 t[n]=actuale - n=n+1 t[n]=stop character.commands=t end end @@ -27697,6 +28832,9 @@ local function pdftovirtual(tfmdata,pdfshapes,kind) } local getactualtext=otf.getactualtext local storepdfdata=otf.storepdfdata + local b,e=getactualtext(tounicode(0xFFFD)) + local actualb={ "pdf","page",b } + local actuale={ "pdf","page",e } for unicode,character in sortedhash(characters) do local index=character.index if index then @@ -27717,16 +28855,16 @@ local function pdftovirtual(tfmdata,pdfshapes,kind) if data then local setcode,name,nilcode=storepdfdata(data) if name then - local bt,et=getactualtext(unicode) + local bt=unicode and getactualtext(unicode) local wd=character.width or 0 local ht=character.height or 0 local dp=character.depth or 0 character.commands={ - { "special","pdf:direct:"..bt }, + not unicode and actualb or { "pdf","page",(getactualtext(unicode)) }, { "down",dp+dy*hfactor }, { "right",dx*hfactor }, { "image",{ filename=name,width=wd,height=ht,depth=dp } }, - { "special","pdf:direct:"..et }, + actuale, } character[kind]=true end @@ -27937,7 +29075,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-otc']={ version=1.001, - comment="companion to font-otf.lua (context)", + comment="companion to font-ini.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" @@ -27946,12 +29084,17 @@ local format,insert,sortedkeys,tohash=string.format,table.insert,table.sortedkey local type,next=type,next local lpegmatch=lpeg.match local utfbyte,utflen,utfsplit=utf.byte,utf.len,utf.split +local match=string.match +local sortedhash=table.sortedhash local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf local registerotffeature=otf.features.register local setmetatableindex=table.setmetatableindex +local checkmerge=fonts.helpers.checkmerge +local checkflags=fonts.helpers.checkflags +local checksteps=fonts.helpers.checksteps local normalized={ substitution="substitution", single="substitution", @@ -27960,6 +29103,7 @@ local normalized={ multiple="multiple", kern="kern", pair="pair", + single="single", chainsubstitution="chainsubstitution", chainposition="chainposition", } @@ -27970,6 +29114,7 @@ local types={ multiple="gsub_multiple", kern="gpos_pair", pair="gpos_pair", + single="gpos_single", chainsubstitution="gsub_contextchain", chainposition="gpos_contextchain", } @@ -28299,14 +29444,13 @@ local function addfeature(data,feature,specifications) end return coverage end + local prepare_single=prepare_pair local function prepare_chain(list,featuretype,sublookups) local rules=list.rules local coverage={} if rules then local rulehash={} local rulesize=0 - local sequence={} - local nofsequences=0 local lookuptype=types[featuretype] for nofrules=1,#rules do local rule=rules[nofrules] @@ -28337,7 +29481,7 @@ local function addfeature(data,feature,specifications) local lookups=rule.lookups or false local subtype=nil if lookups and sublookups then - for k,v in next,lookups do + for k,v in sortedhash(lookups) do local t=type(v) if t=="table" then for i=1,#v do @@ -28386,14 +29530,16 @@ local function addfeature(data,feature,specifications) replacements, subtype, } - for unic in next,sequence[start] do + for unic in sortedhash(sequence[start]) do local cu=coverage[unic] if not cu then coverage[unic]=rulehash end end + sequence.n=nofsequences end end + rulehash.n=rulesize end return coverage end @@ -28431,9 +29577,9 @@ local function addfeature(data,feature,specifications) local s=sequences[i] local f=s.features if f then - for k in next,f do + for k in sortedhash(f) do if k==position then - index=i + index=i break end end @@ -28487,6 +29633,7 @@ local function addfeature(data,feature,specifications) local steps={} local sublookups=specification.lookups local category=nil + checkflags(specification,resources) if sublookups then local s={} for i=1,#sublookups do @@ -28508,18 +29655,23 @@ local function addfeature(data,feature,specifications) coverage=prepare_alternate(list,featuretype,nocheck) elseif featuretype=="multiple" then coverage=prepare_multiple(list,featuretype,nocheck) - elseif featuretype=="kern" then - format="kern" + elseif featuretype=="kern" or featuretype=="move" then + format=featuretype coverage=prepare_kern(list,featuretype) elseif featuretype=="pair" then format="pair" coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + format="single" + coverage=prepare_single(list,featuretype) end if coverage and next(coverage) then nofsteps=nofsteps+1 steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) end end + checkmerge(specification) + checksteps(specification) s[i]={ [stepkey]=steps, nofsteps=nofsteps, @@ -28545,14 +29697,18 @@ local function addfeature(data,feature,specifications) elseif featuretype=="multiple" then category="gsub" coverage=prepare_multiple(list,featuretype,nocheck) - elseif featuretype=="kern" then + elseif featuretype=="kern" or featuretype=="move" then category="gpos" - format="kern" + format=featuretype coverage=prepare_kern(list,featuretype) elseif featuretype=="pair" then category="gpos" format="pair" coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + category="gpos" + format="single" + coverage=prepare_single(list,featuretype) elseif featuretype=="chainsubstitution" then category="gsub" coverage=prepare_chain(list,featuretype,sublookups) @@ -28588,6 +29744,9 @@ local function addfeature(data,feature,specifications) nofsteps=nofsteps, type=steptype, } + checkflags(sequence,resources) + checkmerge(sequence) + checksteps(sequence) local first,last=getrange(sequences,category) inject(specification,sequences,sequence,first,last,category,feature) local features=fontfeatures[category] @@ -28746,7 +29905,8 @@ registerotffeature { local lookups={} local protect={} local revert={} -local zwj={ 0x200C } +local zwjchar=0x200C +local zwj={ zwjchar } otf.addfeature { name="blockligatures", type="chainsubstitution", @@ -28785,19 +29945,44 @@ registerotffeature { } local settings_to_array=utilities.parsers and utilities.parsers.settings_to_array or function(s) return string.split(s,",") end +local splitter=lpeg.splitat(":") local function blockligatures(str) local t=settings_to_array(str) for i=1,#t do - local ti=utfsplit(t[i]) - if #ti>1 then - local one=ti[1] - local two=ti[2] - lookups[one]={ one,0x200C } + local ti=t[i] + local before,current,after=lpegmatch(splitter,ti) + if current and after then + if before then + before=utfsplit(before) + for i=1,#before do + before[i]={ before[i] } + end + end + if current then + current=utfsplit(current) + end + if after then + after=utfsplit(after) + for i=1,#after do + after[i]={ after[i] } + end + end + else + before=nil + current=utfsplit(ti) + after=nil + end + if #current>1 then + local one=current[1] + local two=current[2] + lookups[one]={ one,zwjchar } local one={ one } local two={ two } local new=#protect+1 protect[new]={ + before=before, current={ one,two }, + after=after, lookups={ 1 }, } revert[new]={ @@ -28845,7 +30030,7 @@ local afm=handlers.afm or {} handlers.afm=afm local readers=afm.readers or {} afm.readers=readers -afm.version=1.512 +afm.version=1.513 local get_indexes,get_shapes do local decrypt @@ -28869,7 +30054,7 @@ do local dup=P("dup") local put=P("put") local array=P("array") - local name=P("/")*C((R("az")+R("AZ")+R("09")+S("-_."))^1) + local name=P("/")*C((R("az","AZ","09")+S("-_."))^1) local digits=R("09")^1 local cardinal=digits/tonumber local spaces=P(" ")^1 @@ -28880,30 +30065,38 @@ do m=size return position+1 end - local setroutine=function(str,position,index,size) + local setroutine=function(str,position,index,size,filename) local forward=position+tonumber(size) local stream=decrypt(sub(str,position+1,forward),4330,4) routines[index]={ byte(stream,1,#stream) } return forward end - local setvector=function(str,position,name,size) + local setvector=function(str,position,name,size,filename) local forward=position+tonumber(size) if n>=m then return #str elseif forward<#str then + if n==0 and name~=".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) + n=n+1 + end vector[n]=name - n=n+1 + n=n+1 return forward else return #str end end - local setshapes=function(str,position,name,size) + local setshapes=function(str,position,name,size,filename) local forward=position+tonumber(size) local stream=sub(str,position+1,forward) if n>m then return #str elseif forward<#str then + if n==0 and name~=".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) + n=n+1 + end vector[n]=name n=n+1 chars [n]=decrypt(stream,4330,4) @@ -28916,11 +30109,11 @@ do local p_np=spacing*(P("NP")+P("|")) local p_nd=spacing*(P("ND")+P("|")) local p_filterroutines= - (1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd,setroutine)*p_np+P(1))^1 + (1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd*Carg(1),setroutine)*p_np+P(1))^1 local p_filtershapes= - (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd,setshapes)*p_nd+P(1))^1 + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd*Carg(1),setshapes)*p_nd+P(1))^1 local p_filternames=Ct ( - (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal,setvector)+P(1))^1 + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*Carg(1),setvector)+P(1))^1 ) local p_filterencoding=(1-encoding)^0*encoding*spaces*digits*spaces*array*(1-dup)^0*Cf( Ct("")*Cg(spacing*dup*spaces*cardinal*spaces*name*spaces*put)^1 @@ -28931,7 +30124,7 @@ do report_pfb("no data in %a",filename) return end - if not (find(data,"!PS%-AdobeFont%-") or find(data,"%%!FontType1")) then + if not (find(data,"!PS-AdobeFont-",1,true) or find(data,"%!FontType1",1,true)) then report_pfb("no font in %a",filename) return end @@ -28946,8 +30139,8 @@ do local glyphs={} routines,vector,chars={},{},{} if shapestoo then - lpegmatch(p_filterroutines,binary) - lpegmatch(p_filtershapes,binary) + lpegmatch(p_filterroutines,binary,1,filename) + lpegmatch(p_filtershapes,binary,1,filename) local data={ dictionaries={ { @@ -28959,7 +30152,7 @@ do } fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,true) else - lpegmatch(p_filternames,binary) + lpegmatch(p_filternames,binary,1,filename) end names=vector routines,vector,chars=nil,nil,nil @@ -28975,7 +30168,7 @@ do if trace_loading then report_afm("getting index data from %a",pfbname) end - for index=1,#vector do + for index=0,#vector do local name=vector[index] local char=characters[name] if char then @@ -28983,6 +30176,10 @@ do report_afm("glyph %a has index %a",name,index) end char.index=index + else + if trace_indexing then + report_afm("glyph %a has index %a but no data",name,index) + end end end end @@ -29088,7 +30285,6 @@ local p_parameters=P(false)+fontdata*((P("FontName")+P("FullName")+P("FamilyName +(fontdata*C("AXISHEIGHT")*number*rest)/set_1 ) local fullparser=(P("StartFontMetrics")*fontdata*name/start )*(p_charmetrics+p_kernpairs+p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop ) -local fullparser=(P("StartFontMetrics")*fontdata*name/start )*(p_charmetrics+p_kernpairs+p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop ) local infoparser=(P("StartFontMetrics")*fontdata*name/start )*(p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop ) local function read(filename,parser) local afmblob=io.loaddata(filename) @@ -29174,12 +30370,11 @@ if not modules then modules={} end modules ['font-one']={ } local fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers local next,type,tonumber,rawget=next,type,tonumber,rawget -local match,gmatch,lower,gsub,strip,find=string.match,string.gmatch,string.lower,string.gsub,string.strip,string.find -local char,byte,sub=string.char,string.byte,string.sub +local match,gsub=string.match,string.gsub local abs=math.abs -local bxor,rshift=bit32.bxor,bit32.rshift local P,S,R,Cmt,C,Ct,Cs,Carg=lpeg.P,lpeg.S,lpeg.R,lpeg.Cmt,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg local lpegmatch,patterns=lpeg.match,lpeg.patterns +local sortedhash=table.sortedhash local trace_features=false trackers.register("afm.features",function(v) trace_features=v end) local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end) local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end) @@ -29188,6 +30383,7 @@ local report_afm=logs.reporter("fonts","afm loading") local setmetatableindex=table.setmetatableindex local derivetable=table.derive local findbinfile=resolvers.findbinfile +local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 local definers=fonts.definers local readers=fonts.readers local constructors=fonts.constructors @@ -29200,7 +30396,7 @@ local afmfeatures=constructors.features.afm local registerafmfeature=afmfeatures.register local afmenhancers=constructors.enhancers.afm local registerafmenhancer=afmenhancers.register -afm.version=1.512 +afm.version=1.513 afm.cache=containers.define("fonts","one",afm.version,true) afm.autoprefixed=true afm.helpdata={} @@ -29258,9 +30454,9 @@ local function enhance_unify_names(data,filename) local unicodevector=fonts.encodings.agl.unicodes local unicodes={} local names={} - local private=constructors.privateoffset + local private=data.private or privateoffset local descriptions=data.descriptions - for name,blob in next,data.characters do + for name,blob in sortedhash(data.characters) do local code=unicodevector[name] if not code then code=lpegmatch(uparser,name) @@ -29297,12 +30493,12 @@ local function enhance_unify_names(data,filename) end end data.characters=nil + data.private=private local resources=data.resources local filename=resources.filename or file.removesuffix(file.basename(filename)) resources.filename=resolvers.unresolve(filename) resources.unicodes=unicodes resources.marks={} - resources.private=private end local everywhere={ ["*"]={ ["*"]=true } } local noflags={ false,false,false,false } @@ -29656,6 +30852,7 @@ local function copytotfm(data) properties.fullname=fullname properties.psname=fullname properties.name=filename or fullname or fontname + properties.private=properties.private or data.private or privateoffset if next(characters) then return { characters=characters, @@ -29867,7 +31064,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-afk']={ version=1.001, - comment="companion to font-afm.lua", + comment="companion to font-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", @@ -30158,6 +31355,7 @@ local function read_from_tfm(specification) parameters.quad=parameters.quad or parameters[6] or 0 parameters.extra_space=parameters.extra_space or parameters[7] or 0 constructors.enhanceparameters(parameters) + properties.private=properties.private or tfmdata.private or privateoffset if newtfmdata then elseif constructors.resolvevirtualtoo then fonts.loggers.register(tfmdata,file.suffix(filename),specification) @@ -30304,7 +31502,7 @@ do local originals=tfmdata.characters local indices={} local parentfont={ "font",1 } - local private=fonts.constructors.privateoffset + local private=tfmdata or fonts.constructors.privateoffset local reported=encdone[tfmfile][encfile] local backmap=vector and table.swapped(vector) local done={} @@ -30371,6 +31569,7 @@ do tfmdata.tounicode=1 tfmdata.embedding="subset" tfmdata.usedbitmap=bitmap and virtualid + tfmdata.private=private return tfmdata end end @@ -30395,7 +31594,8 @@ end ]] local flushstreamobject=lpdf and lpdf.flushstreamobject local setfontattributes=pdf.setfontattributes - if not flushstreamobject then + if flushstreamobject then + else flushstreamobject=function(data) return pdf.obj { immediate=true, @@ -30601,7 +31801,6 @@ local allocate=utilities.storage.allocate local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") -trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*") local report_defining=logs.reporter("fonts","defining") local fonts=fonts local fontdata=fonts.hashes.identifiers @@ -30892,8 +32091,6 @@ function definers.loadfont(specification) end return tfmdata end -function constructors.checkvirtualids() -end function constructors.readanddefine(name,size) local specification=definers.analyze(name,size) local method=specification.method @@ -30907,7 +32104,6 @@ function constructors.readanddefine(name,size) local tfmdata=definers.loadfont(specification) if tfmdata then tfmdata.properties.hash=hash - constructors.checkvirtualids(tfmdata) id=font.define(tfmdata) definers.register(tfmdata,id) else @@ -30976,7 +32172,7 @@ function definers.read(specification,size,id) local properties=tfmdata.properties or {} local parameters=tfmdata.parameters or {} report_defining("using %a font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a", - properties.format or "unknown",id,properties.name,parameters.size,properties.encodingbytes, + properties.format or "unknown",id or "-",properties.name,parameters.size,properties.encodingbytes, properties.encodingname,properties.fullname,basename(properties.filename)) end statistics.stoptiming(fonts) @@ -31086,6 +32282,7 @@ if context then end local fonts=fonts local otffeatures=fonts.constructors.features.otf +local getprivate=fonts.constructors.getprivate local function initializeitlc(tfmdata,value) if value then local parameters=tfmdata.parameters @@ -31303,14 +32500,12 @@ otffeatures.register { local setmetatableindex=table.setmetatableindex local function additalictowidth(tfmdata,key,value) local characters=tfmdata.characters - local resources=tfmdata.resources local additions={} - local private=resources.private for unicode,old_c in next,characters do local oldwidth=old_c.width local olditalic=old_c.italic if olditalic and olditalic~=0 then - private=private+1 + local private=getprivate(tfmdata) local new_c={ width=oldwidth+olditalic, height=old_c.height, @@ -31328,7 +32523,6 @@ local function additalictowidth(tfmdata,key,value) for k,v in next,additions do characters[k]=v end - resources.private=private end otffeatures.register { name="italicwidths", @@ -33423,6 +34617,7 @@ if context then texio.write_nl("fatal error: this module is not for context") os.exit() end +local next=next local fonts=fonts local nodes=nodes local nuts=nodes.nuts @@ -33464,7 +34659,7 @@ end function nodes.handlers.setbasemodepass(v) basemodepass=v end -function nodes.handlers.nodepass(head) +function nodes.handlers.nodepass(head,groupcode,size,packtype,direction) local fontdata=fonts.hashes.identifiers if fontdata then local nuthead=tonut(head) @@ -33474,6 +34669,7 @@ function nodes.handlers.nodepass(head) local basefont=nil local variants=nil local redundant=nil + local nofused=0 for n in traverse_id(glyph_code,nuthead) do local font=getfont(n) if font~=prevfont then @@ -33490,6 +34686,7 @@ function nodes.handlers.nodepass(head) local processors=shared.processes if processors and #processors>0 then usedfonts[font]=processors + nofused=nofused+1 elseif basemodepass then basefont={ n,nil } basefonts[#basefonts+1]=basefont @@ -33570,6 +34767,7 @@ function nodes.handlers.nodepass(head) local processors=shared.processes if processors and #processors>0 then usedfonts[font]=processors + nofused=nofused+1 end end end @@ -33581,7 +34779,7 @@ function nodes.handlers.nodepass(head) if next(usedfonts) then for font,processors in next,usedfonts do for i=1,#processors do - head=processors[i](head,font,0) or head + head=processors[i](head,font,0,direction,nofused) or head end end end @@ -33620,7 +34818,7 @@ function nodes.handlers.nodepass(head) end end function nodes.handlers.basepass(head) - if not basemodepass then + if basemodepass then head=n_ligaturing(head) head=n_kerning(head) end @@ -33630,9 +34828,9 @@ local nodepass=nodes.handlers.nodepass local basepass=nodes.handlers.basepass local injectpass=nodes.injections.handler local protectpass=nodes.handlers.protectglyphs -function nodes.simple_font_handler(head) +function nodes.simple_font_handler(head,groupcode,size,packtype,direction) if head then - head=nodepass(head) + head=nodepass(head,groupcode,size,packtype,direction) head=injectpass(head) if not basemodepass then head=basepass(head) diff --git a/tex/generic/context/luatex/luatex-fonts-mis.lua b/tex/generic/context/luatex/luatex-fonts-mis.lua new file mode 100644 index 000000000..02a5b60db --- /dev/null +++ b/tex/generic/context/luatex/luatex-fonts-mis.lua @@ -0,0 +1,32 @@ +if not modules then modules = { } end modules ['luatex-font-mis'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local currentfont = font.current + +local hashes = fonts.hashes +local identifiers = hashes.identifiers or { } +local marks = hashes.marks or { } + +hashes.identifiers = identifiers +hashes.marks = marks + +table.setmetatableindex(marks,function(t,k) + if k == true then + return marks[currentfont()] + else + local resources = identifiers[k].resources or { } + local marks = resources.marks or { } + t[k] = marks + return marks + end +end) diff --git a/tex/generic/context/luatex/luatex-fonts.lua b/tex/generic/context/luatex/luatex-fonts.lua index 20690992c..ef3bb74dc 100644 --- a/tex/generic/context/luatex/luatex-fonts.lua +++ b/tex/generic/context/luatex/luatex-fonts.lua @@ -219,6 +219,7 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then -- --reload --force --simple option). loadmodule('font-ini.lua') + loadmodule('luatex-fonts-mis.lua') loadmodule('font-con.lua') loadmodule('luatex-fonts-enc.lua') -- will load font-age on demand loadmodule('font-cid.lua') @@ -231,21 +232,6 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then loadmodule('luatex-fonts-syn.lua') loadmodule('font-oti.lua') - - -- These are the old loader and processing modules. These use the built-in font loader and - -- will stay around (but not be extended), only fixed. - - -- font-otf.lua - -- font-otb.lua - -- font-inj.lua - -- font-ota.lua - -- font-otn.lua - -- font-otp.lua - - -- Here come the new loader and processing modules. The loader is written in Lua and although - -- initial loading is somewhat slower, identifying is faster, cached files can be slightly - -- more efficient, and processing is somewhat faster (only measureable on complex fonts). - loadmodule('font-otr.lua') loadmodule('font-cff.lua') loadmodule('font-ttf.lua') @@ -258,7 +244,6 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then loadmodule('font-ots.lua') loadmodule('font-osd.lua') loadmodule('font-ocl.lua') -- svg needs 0.97 (for fix in memstreams) - loadmodule('font-otc.lua') -- type one code @@ -277,7 +262,7 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then loadmodule('font-def.lua') loadmodule('font-xtx.lua') -- xetex compatible specifiers (plain/latex only) loadmodule('luatex-fonts-ext.lua') -- some extensions - -- loadmodule('luatex-fonts-lig.lua') -- and another one + loadmodule('luatex-fonts-lig.lua') -- and another one -- We need to plug into a callback and the following module implements the handlers. Actual -- plugging in happens later. diff --git a/tex/generic/context/luatex/luatex-gadgets.lua b/tex/generic/context/luatex/luatex-gadgets.lua index 8c835babb..60c21427d 100644 --- a/tex/generic/context/luatex/luatex-gadgets.lua +++ b/tex/generic/context/luatex/luatex-gadgets.lua @@ -16,14 +16,13 @@ gadgets = gadgets or { } -- global namespace -- marking content for optional removal -local marking = { } -gadgets.marking = marking +local marking = { } +gadgets.marking = marking -local marksignal = 5001 -- will be set in the tex module -local lastmarked = 0 -local marked = { } -local local_par = 6 -local whatsit_node = 8 +local marksignal = 5001 -- will be set in the tex module +local lastmarked = 0 +local marked = { } +local local_par_code = 9 function marking.setsignal(n) marksignal = tonumber(n) or marksignal @@ -53,9 +52,7 @@ function marking.remove(str) local first = last while true do local prev = first.prev - if not prev - or prev[marksignal] ~= attr - or (prev.id == whatsit_node and prev.subtype == local_par) then + if not prev or prev[marksignal] ~= attr or prev.id == local_par_code then break else first = prev diff --git a/tex/generic/context/luatex/luatex-math.tex b/tex/generic/context/luatex/luatex-math.tex index 6edc6f74b..4ed900ddc 100644 --- a/tex/generic/context/luatex/luatex-math.tex +++ b/tex/generic/context/luatex/luatex-math.tex @@ -2,8 +2,8 @@ %D [ file=luatex-math, %D version=2013.04.29, %D title=\LUATEX\ Support Macros, -%D subtitle=An exmaple of math, -%D author=Hans Hagen, +%D subtitle=An example of math, +%D author={Hans Hagen & Bruno Voisin}, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %D This module is in no way a complete plain math implementation. I made this file @@ -18,6 +18,8 @@ % we provide a remap feature +% \protected makes our mp test work (no expansion) + \ifdefined\directlua \directlua{dofile(kpse.find_file('luatex-math.lua'))} \fi @@ -34,7 +36,7 @@ \let \sevenbf = \relax \let \fivebf = \relax -\def\latinmodern +\protected\def\latinmodern {\font\tenrm = file:lmroman10-regular.otf:+liga;+kern;+tlig;+trep at 10pt \font\sevenrm = file:lmroman7-regular.otf:+liga;+kern;+tlig;+trep at 7pt \font\fiverm = file:lmroman5-regular.otf:+liga;+kern;+tlig;+trep at 5pt @@ -63,7 +65,7 @@ % \tenrm} -\def\lucidabright +\protected\def\lucidabright {\font\tenrm = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 10pt \font\sevenrm = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 7pt \font\fiverm = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 5pt @@ -112,12 +114,12 @@ % the following commands switch text as well as math -\def\rm{\fam0\relax\the\everymathrm\relax\tenrm\relax} -\def\it{\fam0\relax\the\everymathit\relax\tenit\relax} -\def\sl{\fam0\relax\the\everymathsl\relax\tensl\relax} -\def\bf{\fam0\relax\the\everymathbf\relax\tenbf\relax} -\def\bi{\fam0\relax\the\everymathbi\relax\tenbi\relax} -\def\tt{\fam0\relax\the\everymathtt\relax\tentt\relax} +\protected\def\rm{\fam0\relax\the\everymathrm\relax\tenrm\relax} +\protected\def\it{\fam0\relax\the\everymathit\relax\tenit\relax} +\protected\def\sl{\fam0\relax\the\everymathsl\relax\tensl\relax} +\protected\def\bf{\fam0\relax\the\everymathbf\relax\tenbf\relax} +\protected\def\bi{\fam0\relax\the\everymathbi\relax\tenbi\relax} +\protected\def\tt{\fam0\relax\the\everymathtt\relax\tentt\relax} % tex is fast enough for this kind of assignments: @@ -838,7 +840,69 @@ } \everymathtt {% - % not done + \Umathcode"0030="0"0"1D7F6% + \Umathcode"0031="0"0"1D7F7% + \Umathcode"0032="0"0"1D7F8% + \Umathcode"0033="0"0"1D7F9% + \Umathcode"0034="0"0"1D7FA% + \Umathcode"0035="0"0"1D7FB% + \Umathcode"0036="0"0"1D7FC% + \Umathcode"0037="0"0"1D7FD% + \Umathcode"0038="0"0"1D7FE% + \Umathcode"0039="0"0"1D7FF% + \Umathcode"0041="0"0"1D670% + \Umathcode"0042="0"0"1D671% + \Umathcode"0043="0"0"1D672% + \Umathcode"0044="0"0"1D673% + \Umathcode"0045="0"0"1D674% + \Umathcode"0046="0"0"1D675% + \Umathcode"0047="0"0"1D676% + \Umathcode"0048="0"0"1D677% + \Umathcode"0049="0"0"1D678% + \Umathcode"004A="0"0"1D679% + \Umathcode"004B="0"0"1D67A% + \Umathcode"004C="0"0"1D67B% + \Umathcode"004D="0"0"1D67C% + \Umathcode"004E="0"0"1D67D% + \Umathcode"004F="0"0"1D67E% + \Umathcode"0050="0"0"1D67F% + \Umathcode"0051="0"0"1D680% + \Umathcode"0052="0"0"1D681% + \Umathcode"0053="0"0"1D682% + \Umathcode"0054="0"0"1D683% + \Umathcode"0055="0"0"1D684% + \Umathcode"0056="0"0"1D685% + \Umathcode"0057="0"0"1D686% + \Umathcode"0058="0"0"1D687% + \Umathcode"0059="0"0"1D688% + \Umathcode"005A="0"0"1D689% + \Umathcode"0061="0"0"1D68A% + \Umathcode"0062="0"0"1D68B% + \Umathcode"0063="0"0"1D68C% + \Umathcode"0064="0"0"1D68D% + \Umathcode"0065="0"0"1D68E% + \Umathcode"0066="0"0"1D68F% + \Umathcode"0067="0"0"1D690% + \Umathcode"0068="0"0"1D691% + \Umathcode"0069="0"0"1D692% + \Umathcode"006A="0"0"1D693% + \Umathcode"006B="0"0"1D694% + \Umathcode"006C="0"0"1D695% + \Umathcode"006D="0"0"1D696% + \Umathcode"006E="0"0"1D697% + \Umathcode"006F="0"0"1D698% + \Umathcode"0070="0"0"1D699% + \Umathcode"0071="0"0"1D69A% + \Umathcode"0072="0"0"1D69B% + \Umathcode"0073="0"0"1D69C% + \Umathcode"0074="0"0"1D69D% + \Umathcode"0075="0"0"1D69E% + \Umathcode"0076="0"0"1D69F% + \Umathcode"0077="0"0"1D6A0% + \Umathcode"0078="0"0"1D6A1% + \Umathcode"0079="0"0"1D6A2% + \Umathcode"007A="0"0"1D6A3% + \relax } % Nothing special here: @@ -849,11 +913,11 @@ % much sense, it's more that in good old tex oldstyle was taken from % math fonts. So, just something compatible: -\def\oldstyle{\fam1\relax\tenos\relax} +\protected\def\oldstyle{\fam1\relax\tenos\relax} % Again a text and math one and it had better be used grouped. -\def\cal{\fam0\relax\the\everymathcal\relax\tenit\relax} +\protected\def\cal{\fam0\relax\the\everymathcal\relax\tenit\relax} \everymathcal {% \Umathcode"0041="0"0"1D49C% A @@ -1514,678 +1578,678 @@ % a couple of definitions (we could also use \mathchardef): -\def\acute {\Umathaccent"0"0"0000B4 } -\def\acwopencirclearrow {\Umathchar "3"0"0021BA } -\def\aleph {\Umathchar "0"0"002135 } -\def\Alpha {\Umathchar "0"0"000391 } -\def\alpha {\Umathchar "0"0"0003B1 } -\def\amalg {\Umathchar "2"0"002A3F } -\def\angle {\Umathchar "0"0"002220 } -\def\Angstrom {\Umathchar "0"0"00212B } -\def\approx {\Umathchar "3"0"002248 } -\def\approxEq {\Umathchar "3"0"002245 } -\def\approxeq {\Umathchar "3"0"00224A } -\def\approxnEq {\Umathchar "3"0"002247 } -\def\arrowvert {\Umathchar "0"0"00007C } -\def\Arrowvert {\Umathchar "0"0"002016 } -\def\ast {\Umathchar "2"0"002217 } -\def\ast {\Umathchar "2"0"002217 } -\def\asymp {\Umathchar "3"0"00224D } -\def\backepsilon {\Umathchar "0"0"0003F6 } -\def\backprime {\Umathchar "0"0"012035 } -\def\backsim {\Umathchar "3"0"00223D } -\def\backslash {\Umathchar "0"0"00005C } -\def\bar {\Umathaccent"0"0"0000AF } -\def\barleftarrow {\Umathchar "3"0"0021E4 } -\def\barleftarrowrightarrowbar {\Umathchar "3"0"0021B9 } -\def\barovernorthwestarrow {\Umathchar "3"0"0021B8 } -\def\barwedge {\Umathchar "2"0"0022BC } -\def\because {\Umathchar "3"0"002235 } -\def\Beta {\Umathchar "0"0"000392 } -\def\beta {\Umathchar "0"0"0003B2 } -\def\beth {\Umathchar "0"0"002136 } -\def\between {\Umathchar "3"0"00226C } -\def\bigcap {\Umathchar "1"0"0022C2 } -\def\bigcirc {\Umathchar "2"0"0025EF } -\def\bigcircle {\Umathchar "2"0"0020DD } -\def\bigcircle {\Umathchar "2"0"0020DD } -\def\bigcup {\Umathchar "1"0"0022C3 } -\def\bigdiamond {\Umathchar "0"0"0020DF } -\def\bigodot {\Umathchar "1"0"002A00 } -\def\bigoplus {\Umathchar "1"0"002A01 } -\def\bigotimes {\Umathchar "1"0"002A02 } -\def\bigsqcap {\Umathchar "1"0"002A05 } -\def\bigsqcup {\Umathchar "1"0"002A06 } -\def\bigsquare {\Umathchar "0"0"0020DE } -\def\bigstar {\Umathchar "0"0"002605 } -\def\bigtimes {\Umathchar "1"0"002A09 } -\def\bigtriangledown {\Umathchar "2"0"0025BD } -\def\bigtriangleup {\Umathchar "2"0"0025B3 } -\def\bigudot {\Umathchar "1"0"002A03 } -\def\biguplus {\Umathchar "1"0"002A04 } -\def\bigvee {\Umathchar "1"0"0022C1 } -\def\bigwedge {\Umathchar "1"0"0022C0 } -\def\blacklozenge {\Umathchar "0"0"002666 } -\def\blacksquare {\Umathchar "0"0"0025A0 } -\def\blacktriangle {\Umathchar "2"0"0025B2 } -\def\blacktriangledown {\Umathchar "2"0"0025BC } -\def\blacktriangleleft {\Umathchar "2"0"0025C0 } -\def\blacktriangleright {\Umathchar "2"0"0025B6 } -\def\bot {\Umathchar "0"0"0022A5 } -\def\bowtie {\Umathchar "3"0"0022C8 } -\def\Box {\Umathchar "0"0"0025A1 } -\def\boxdot {\Umathchar "2"0"0022A1 } -\def\boxminus {\Umathchar "2"0"00229F } -\def\boxplus {\Umathchar "2"0"00229E } -\def\boxtimes {\Umathchar "2"0"0022A0 } -%def\braceld {\Umathchar "0"0"000000 } -%def\bracerd {\Umathchar "0"0"000000 } -%def\bracelu {\Umathchar "0"0"000000 } -%def\braceru {\Umathchar "0"0"000000 } -\def\breve {\Umathaccent"0"0"0002D8 } -\def\bullet {\Umathchar "2"0"002022 } -\def\bullet {\Umathchar "2"0"002022 } -\def\Bumpeq {\Umathchar "3"0"00224E } -\def\cap {\Umathchar "2"0"002229 } -\def\Cap {\Umathchar "2"0"0022D2 } -\def\carriagereturn {\Umathchar "0"0"0021B5 } -\def\cdot {\Umathchar "2"0"0022C5 } -\def\cdotp {\Umathchar "6"0"0022C5 } -\def\cdots {\Umathchar "0"0"0022EF } -\def\centerdot {\Umathchar "2"0"0000B7 } -\def\check {\Umathaccent"0"0"0002C7 } -\def\checkmark {\Umathchar "0"0"002713 } -\def\Chi {\Umathchar "0"0"0003A7 } -\def\chi {\Umathchar "0"0"0003C7 } -\def\circ {\Umathchar "2"0"002218 } -\def\circeq {\Umathchar "3"0"002257 } -\def\circlearrowleft {\Umathchar "3"0"0021BB } -\def\circlearrowright {\Umathchar "3"0"0021BA } -\def\circledast {\Umathchar "2"0"00229B } -\def\circledcirc {\Umathchar "2"0"00229A } -\def\circleddash {\Umathchar "2"0"00229D } -\def\circledequals {\Umathchar "2"0"00229C } -\def\circledR {\Umathchar "0"0"0024C7 } -\def\circledS {\Umathchar "0"0"0024C8 } -\def\circleonrightarrow {\Umathchar "3"0"0021F4 } -\def\clubsuit {\Umathchar "0"0"002663 } -\def\colon {\Umathchar "6"0"002236 } -\def\colonequals {\Umathchar "3"0"002254 } -\def\complement {\Umathchar "0"0"002201 } -\def\complexes {\Umathchar "0"0"002102 } -\def\cong {\Umathchar "3"0"002245 } -\def\coprod {\Umathchar "1"0"002210 } -\def\cup {\Umathchar "2"0"00222A } -\def\Cup {\Umathchar "2"0"0022D3 } -\def\curlyeqprec {\Umathchar "3"0"0022DE } -\def\curlyeqsucc {\Umathchar "3"0"0022DF } -\def\curlyvee {\Umathchar "2"0"0022CE } -\def\curlywedge {\Umathchar "2"0"0022CF } -\def\curvearrowleft {\Umathchar "3"0"0021B6 } -\def\curvearrowright {\Umathchar "3"0"0021B7 } -\def\cwopencirclearrow {\Umathchar "3"0"0021BB } -\def\dag {\Umathchar "0"0"002020 } -\def\dagger {\Umathchar "2"0"002020 } -\def\daleth {\Umathchar "0"0"002138 } -\def\dasharrow {\Umathchar "3"0"0021E2 } -\def\dashedleftarrow {\Umathchar "3"0"00290C } -\def\dashedrightarrow {\Umathchar "3"0"00290D } -\def\dashv {\Umathchar "3"0"0022A3 } -\def\ddag {\Umathchar "0"0"002021 } -\def\ddagger {\Umathchar "2"0"002021 } -\def\dddot {\Umathaccent"0"0"0020DB } -\def\ddot {\Umathaccent"0"0"0000A8 } -\def\ddots {\Umathchar "0"0"0022F1 } -\def\Ddownarrow {\Umathchar "3"0"00290B } -\def\definedeq {\Umathchar "3"0"00225D } -\def\Delta {\Umathchar "0"0"000394 } -\def\delta {\Umathchar "0"0"0003B4 } -\def\diamond {\Umathchar "2"0"0022C4 } -\def\diamondsuit {\Umathchar "0"0"002662 } -\def\differentialD {\Umathchar "0"0"002145 } -\def\differentiald {\Umathchar "0"0"002146 } -\def\digamma {\Umathchar "0"0"0003DC } -\def\div {\Umathchar "2"0"0000F7 } -\def\divideontimes {\Umathchar "2"0"0022C7 } -\def\divides {\Umathchar "2"0"002223 } -\def\dot {\Umathaccent"0"0"0002D9 } -\def\doteq {\Umathchar "3"0"002250 } -\def\Doteq {\Umathchar "3"0"002251 } -\def\doteqdot {\Umathchar "3"0"002251 } -\def\dotminus {\Umathchar "2"0"002238 } -\def\dotplus {\Umathchar "2"0"002214 } -\def\dots {\Umathchar "0"0"002026 } -\def\dottedrightarrow {\Umathchar "3"0"002911 } -\def\doublecap {\Umathchar "2"0"0022D2 } -\def\doublecup {\Umathchar "2"0"0022D3 } -\def\doubleprime {\Umathchar "0"0"002033 } -\def\downarrow {\Umathchar "3"0"002193 } -\def\Downarrow {\Umathchar "3"0"0021D3 } -\def\downdasharrow {\Umathchar "3"0"0021E3 } -\def\downdownarrows {\Umathchar "3"0"0021CA } -\def\downharpoonleft {\Umathchar "3"0"0021C3 } -\def\downharpoonright {\Umathchar "3"0"0021C2 } -\def\downuparrows {\Umathchar "3"0"0021F5 } -\def\downwhitearrow {\Umathchar "0"0"0021E9 } -\def\downzigzagarrow {\Umathchar "3"0"0021AF } -\def\ell {\Umathchar "0"0"002113 } -\def\emptyset {\Umathchar "0"0"002205 } -\def\Epsilon {\Umathchar "0"0"000395 } -\def\epsilon {\Umathchar "0"0"0003F5 } -\def\eq {\Umathchar "3"0"00003D } -\def\eqcirc {\Umathchar "3"0"002256 } -\def\eqgtr {\Umathchar "3"0"0022DD } -\def\eqless {\Umathchar "3"0"0022DC } -\def\eqsim {\Umathchar "3"0"002242 } -\def\eqslantgtr {\Umathchar "3"0"002A96 } -\def\eqslantless {\Umathchar "3"0"002A95 } -\def\equalscolon {\Umathchar "3"0"002255 } -\def\equiv {\Umathchar "3"0"002261 } -\def\Eta {\Umathchar "0"0"000397 } -\def\eta {\Umathchar "0"0"0003B7 } -\def\eth {\Umathchar "0"0"0000F0 } -\def\Eulerconst {\Umathchar "0"0"002107 } -\def\exists {\Umathchar "0"0"002203 } -\def\exponentiale {\Umathchar "0"0"002147 } -\def\fallingdotseq {\Umathchar "3"0"002252 } -\def\Finv {\Umathchar "0"0"002132 } -\def\flat {\Umathchar "0"0"00266D } -\def\forall {\Umathchar "0"0"002200 } -\def\frown {\Umathchar "3"0"002322 } -\def\Game {\Umathchar "0"0"002141 } -\def\Gamma {\Umathchar "0"0"000393 } -\def\gamma {\Umathchar "0"0"0003B3 } -\def\ge {\Umathchar "3"0"002265 } -\def\geq {\Umathchar "3"0"002265 } -\def\geqq {\Umathchar "3"0"002267 } -\def\geqslant {\Umathchar "3"0"002A7E } -\def\gets {\Umathchar "3"0"002190 } -\def\gg {\Umathchar "3"0"00226B } -\def\ggg {\Umathchar "3"0"0022D9 } -\def\gggtr {\Umathchar "3"0"0022D9 } -\def\gimel {\Umathchar "0"0"002137 } -\def\gnapprox {\Umathchar "3"0"002A8A } -\def\gneqq {\Umathchar "3"0"002269 } -\def\gnsim {\Umathchar "3"0"0022E7 } -\def\grave {\Umathaccent"0"0"000060 } -\def\gt {\Umathchar "3"0"00003E } -\def\gtrapprox {\Umathchar "3"0"002A86 } -\def\gtrdot {\Umathchar "2"0"0022D7 } -\def\gtreqless {\Umathchar "3"0"0022DB } -\def\gtreqqless {\Umathchar "3"0"002A8C } -\def\gtrless {\Umathchar "3"0"002277 } -\def\gtrsim {\Umathchar "3"0"002273 } -\def\hat {\Umathaccent"0"0"0002C6 } -\def\hbar {\Umathchar "0"0"00210F } -\def\heartsuit {\Umathchar "0"0"002661 } -\def\hookleftarrow {\Umathchar "3"0"0021A9 } -\def\hookrightarrow {\Umathchar "3"0"0021AA } -\def\hslash {\Umathchar "0"0"00210F } -\def\iiint {\Umathchar "1"0"00222D } -\def\iiintop {\Umathchar "0"0"00222D } -\def\iint {\Umathchar "1"0"00222C } -\def\iintop {\Umathchar "0"0"00222C } -\def\Im {\Umathchar "0"0"002111 } -\def\imaginaryi {\Umathchar "0"0"002148 } -\def\imaginaryj {\Umathchar "0"0"002149 } -\def\imath {\Umathchar "0"0"01D6A4 } -\def\imply {\Umathchar "3"0"0021D2 } -\def\in {\Umathchar "0"0"002208 } -\def\infty {\Umathchar "0"0"00221E } -\def\int {\Umathchar "1"0"00222B } -\def\intclockwise {\Umathchar "1"0"002231 } -\def\integers {\Umathchar "0"0"002124 } -\def\intercal {\Umathchar "2"0"0022BA } -\def\intop {\Umathchar "0"0"00222B } -\def\Iota {\Umathchar "0"0"000399 } -\def\iota {\Umathchar "0"0"0003B9 } -\def\jmath {\Umathchar "0"0"01D6A5 } -\def\Join {\Umathchar "3"0"0022C8 } -\def\Kappa {\Umathchar "0"0"00039A } -\def\kappa {\Umathchar "0"0"0003BA } -\def\Lambda {\Umathchar "0"0"00039B } -\def\lambda {\Umathchar "0"0"0003BB } -\def\land {\Umathchar "2"0"002227 } -\def\langle {\Udelimiter "4"0"0027E8 } -\def\lbrace {\Udelimiter "4"0"00007B } -\def\lbrack {\Udelimiter "4"0"00005B } -\def\lceil {\Udelimiter "4"0"002308 } -\def\lceiling {\Udelimiter "4"0"002308 } -\def\ldotp {\Umathchar "6"0"00002E } -\def\ldots {\Umathchar "0"0"002026 } -\def\Ldsh {\Umathchar "3"0"0021B2 } -\def\le {\Umathchar "3"0"002264 } -\def\leadsto {\Umathchar "3"0"0021DD } -\def\leftarrow {\Umathchar "3"0"002190 } -\def\Leftarrow {\Umathchar "3"0"0021D0 } -\def\leftarrowtail {\Umathchar "3"0"0021A2 } -\def\leftarrowtriangle {\Umathchar "3"0"0021FD } -\def\leftdasharrow {\Umathchar "3"0"0021E0 } -\def\leftharpoondown {\Umathchar "3"0"0021BD } -\def\leftharpoonup {\Umathchar "3"0"0021BC } -\def\leftleftarrows {\Umathchar "3"0"0021C7 } -\def\leftrightarrow {\Umathchar "3"0"002194 } -\def\Leftrightarrow {\Umathchar "3"0"0021D4 } -\def\leftrightarrows {\Umathchar "3"0"0021C6 } -\def\leftrightarrowtriangle {\Umathchar "3"0"0021FF } -\def\leftrightharpoons {\Umathchar "3"0"0021CB } -\def\leftrightsquigarrow {\Umathchar "3"0"0021AD } -\def\leftsquigarrow {\Umathchar "3"0"0021DC } -\def\leftthreetimes {\Umathchar "2"0"0022CB } -\def\leftwavearrow {\Umathchar "3"0"00219C } -\def\leftwhitearrow {\Umathchar "0"0"0021E6 } -\def\leq {\Umathchar "3"0"002264 } -\def\leqq {\Umathchar "3"0"002266 } -\def\leqslant {\Umathchar "3"0"002A7D } -\def\lessapprox {\Umathchar "3"0"002A85 } -\def\lessdot {\Umathchar "2"0"0022D6 } -\def\lesseqgtr {\Umathchar "3"0"0022DA } -\def\lesseqqgtr {\Umathchar "3"0"002A8B } -\def\lessgtr {\Umathchar "3"0"002276 } -\def\lesssim {\Umathchar "3"0"002272 } -\def\lfloor {\Udelimiter "4"0"00230A } -\def\lgroup {\Udelimiter "4"0"0027EE } -\def\lhook {\Umathchar "3"0"0FE322 } -\def\lhooknwarrow {\Umathchar "3"0"002923 } -\def\lhooksearrow {\Umathchar "3"0"002925 } -\def\linefeed {\Umathchar "0"0"0021B4 } -\def\ll {\Umathchar "3"0"00226A } -\def\llangle {\Udelimiter "4"0"0027EA } -\def\llbracket {\Udelimiter "4"0"0027E6 } -\def\llcorner {\Udelimiter "4"0"00231E } -\def\Lleftarrow {\Umathchar "3"0"0021DA } -\def\lll {\Umathchar "3"0"0022D8 } -\def\llless {\Umathchar "3"0"0022D8 } -\def\lmoustache {\Udelimiter "4"0"0023B0 } -\def\lnapprox {\Umathchar "3"0"002A89 } -\def\lneq {\Umathchar "3"0"002A87 } -\def\lneqq {\Umathchar "3"0"002268 } -\def\lnot {\Umathchar "0"0"0000AC } -\def\lnsim {\Umathchar "3"0"0022E6 } -\def\longleftarrow {\Umathchar "3"0"0027F5 } -\def\Longleftarrow {\Umathchar "3"0"0027F8 } -\def\longleftrightarrow {\Umathchar "3"0"0027F7 } -\def\Longleftrightarrow {\Umathchar "3"0"0027FA } -\def\longmapsfrom {\Umathchar "3"0"0027FB } -\def\Longmapsfrom {\Umathchar "3"0"0027FD } -\def\longmapsto {\Umathchar "3"0"0027FC } -\def\Longmapsto {\Umathchar "3"0"0027FE } -\def\longrightarrow {\Umathchar "3"0"0027F6 } -\def\Longrightarrow {\Umathchar "3"0"0027F9 } -\def\longrightsquigarrow {\Umathchar "3"0"0027FF } -\def\looparrowleft {\Umathchar "3"0"0021AB } -\def\looparrowright {\Umathchar "3"0"0021AC } -\def\lor {\Umathchar "2"0"002228 } -\def\lozenge {\Umathchar "0"0"0025CA } -\def\lparent {\Udelimiter "4"0"000028 } -\def\lrcorner {\Udelimiter "5"0"00231F } -\def\Lsh {\Umathchar "3"0"0021B0 } -\def\lt {\Umathchar "3"0"00003C } -\def\ltimes {\Umathchar "2"0"0022C9 } -\def\lvert {\Udelimiter "4"0"00007C } -\def\lVert {\Udelimiter "4"0"002016 } -\def\maltese {\Umathchar "0"0"002720 } -\def\mapsdown {\Umathchar "3"0"0021A7 } -\def\mapsfrom {\Umathchar "3"0"0021A4 } -\def\Mapsfrom {\Umathchar "3"0"002906 } -\def\mapsfromchar {\Umathchar "3"0"0FE324 } -\def\mapsto {\Umathchar "3"0"0021A6 } -\def\Mapsto {\Umathchar "3"0"002907 } -\def\mapstochar {\Umathchar "3"0"0FE321 } -\def\mapsup {\Umathchar "3"0"0021A5 } -\def\mathring {\Umathaccent"0"0"0002DA } -\def\measuredangle {\Umathchar "0"0"002221 } -\def\measuredeq {\Umathchar "3"0"00225E } -\def\mho {\Umathchar "0"0"002127 } -\def\mid {\Umathchar "3"0"00007C } -\def\minus {\Umathchar "2"0"002212 } -\def\minuscolon {\Umathchar "2"0"002239 } -\def\models {\Umathchar "3"0"0022A7 } -\def\mp {\Umathchar "2"0"002213 } -\def\Mu {\Umathchar "0"0"00039C } -\def\mu {\Umathchar "0"0"0003BC } -\def\multimap {\Umathchar "3"0"0022B8 } -\def\napprox {\Umathchar "3"0"002249 } -\def\napproxEq {\Umathchar "3"0"002246 } -\def\nasymp {\Umathchar "3"0"00226D } -\def\natural {\Umathchar "0"0"00266E } -\def\naturalnumbers {\Umathchar "0"0"002115 } -\def\ncong {\Umathchar "3"0"002246 } -\def\ndivides {\Umathchar "2"0"002224 } -\def\ne {\Umathchar "3"0"002260 } -\def\nearrow {\Umathchar "3"0"002197 } -\def\Nearrow {\Umathchar "3"0"0021D7 } -\def\neg {\Umathchar "0"0"0000AC } -\def\negativesign {\Umathchar "2"0"00207B } -\def\neq {\Umathchar "3"0"002260 } -\def\nequiv {\Umathchar "3"0"002262 } -\def\neswarrow {\Umathchar "3"0"002922 } -\def\nexists {\Umathchar "0"0"002204 } -\def\ngeq {\Umathchar "3"0"002271 } -\def\ngtr {\Umathchar "3"0"00226F } -\def\ngtrless {\Umathchar "3"0"002279 } -\def\ngtrsim {\Umathchar "3"0"002275 } -\def\nHdownarrow {\Umathchar "3"0"0021DF } -\def\nHuparrow {\Umathchar "3"0"0021DE } -\def\ni {\Umathchar "3"0"00220B } -\def\nin {\Umathchar "3"0"002209 } -\def\nleftarrow {\Umathchar "3"0"00219A } -\def\nLeftarrow {\Umathchar "3"0"0021CD } -\def\nleftrightarrow {\Umathchar "3"0"0021AE } -\def\nLeftrightarrow {\Umathchar "3"0"0021CE } -\def\nleq {\Umathchar "3"0"002270 } -\def\nless {\Umathchar "3"0"00226E } -\def\nlessgtr {\Umathchar "3"0"002278 } -\def\nlesssim {\Umathchar "3"0"002274 } -\def\nmid {\Umathchar "3"0"002224 } -\def\nni {\Umathchar "3"0"00220C } -\def\not {\Umathchar "3"0"000338 } -\def\notin {\Umathchar "3"0"002209 } -\def\nowns {\Umathchar "3"0"00220C } -\def\nparallel {\Umathchar "3"0"002226 } -\def\nprec {\Umathchar "3"0"002280 } -\def\npreccurlyeq {\Umathchar "3"0"0022E0 } -\def\nrightarrow {\Umathchar "3"0"00219B } -\def\nRightarrow {\Umathchar "3"0"0021CF } -\def\nsim {\Umathchar "3"0"002241 } -\def\nsimeq {\Umathchar "3"0"002244 } -\def\nsqsubseteq {\Umathchar "3"0"0022E2 } -\def\nsqsupseteq {\Umathchar "3"0"0022E3 } -\def\nsubset {\Umathchar "3"0"002284 } -\def\nsubseteq {\Umathchar "3"0"002288 } -\def\nsucc {\Umathchar "3"0"002281 } -\def\nsucccurlyeq {\Umathchar "3"0"0022E1 } -\def\nsupset {\Umathchar "3"0"002285 } -\def\nsupseteq {\Umathchar "3"0"002289 } -\def\ntriangleleft {\Umathchar "3"0"0022EB } -\def\ntrianglelefteq {\Umathchar "3"0"0022EC } -\def\ntriangleright {\Umathchar "3"0"0022EA } -\def\ntrianglerighteq {\Umathchar "3"0"0022ED } -\def\Nu {\Umathchar "0"0"00039D } -\def\nu {\Umathchar "0"0"0003BD } -\def\nvdash {\Umathchar "3"0"0022AC } -\def\nvDash {\Umathchar "3"0"0022AD } -\def\nVdash {\Umathchar "3"0"0022AE } -\def\nVDash {\Umathchar "3"0"0022AF } -\def\nvleftarrow {\Umathchar "3"0"0021F7 } -\def\nVleftarrow {\Umathchar "3"0"0021FA } -\def\nvleftrightarrow {\Umathchar "3"0"0021F9 } -\def\nVleftrightarrow {\Umathchar "3"0"0021FC } -\def\nvrightarrow {\Umathchar "3"0"0021F8 } -\def\nVrightarrow {\Umathchar "3"0"0021FB } -\def\nwarrow {\Umathchar "3"0"002196 } -\def\Nwarrow {\Umathchar "3"0"0021D6 } -\def\nwsearrow {\Umathchar "3"0"002921 } -\def\odot {\Umathchar "2"0"002299 } -\def\ohm {\Umathchar "0"0"002126 } -\def\oiiint {\Umathchar "1"0"002230 } -\def\oiint {\Umathchar "1"0"00222F } -\def\oint {\Umathchar "1"0"00222E } -\def\ointclockwise {\Umathchar "1"0"002232 } -\def\ointctrclockwise {\Umathchar "1"0"002233 } -\def\Omega {\Umathchar "0"0"0003A9 } -\def\omega {\Umathchar "0"0"0003C9 } -\def\Omicron {\Umathchar "0"0"00039F } -\def\omicron {\Umathchar "0"0"0003BF } -\def\ominus {\Umathchar "2"0"002296 } -\def\oplus {\Umathchar "2"0"002295 } -\def\oslash {\Umathchar "2"0"002298 } -\def\otimes {\Umathchar "2"0"002297 } -\def\overbar {\Umathaccent"0"0"00203E } -\def\overbrace {\Umathaccent"0"0"0023DE } -\def\overbracket {\Umathaccent"0"0"0023B4 } -\def\overparent {\Umathaccent"0"0"0023DC } -\def\owns {\Umathchar "3"0"00220B } -\def\P {\Umathchar "0"0"0000B6 } -\def\parallel {\Umathchar "3"0"002225 } -\def\partial {\Umathchar "0"0"002202 } -\def\perp {\Umathchar "3"0"0022A5 } -\def\Phi {\Umathchar "0"0"0003A6 } -\def\phi {\Umathchar "0"0"0003D5 } -\def\Pi {\Umathchar "0"0"0003A0 } -\def\pi {\Umathchar "0"0"0003C0 } -\def\pitchfork {\Umathchar "3"0"0022D4 } -\def\Plankconst {\Umathchar "0"0"00210E } -\def\pm {\Umathchar "2"0"0000B1 } -\def\positivesign {\Umathchar "2"0"00207A } -\def\prec {\Umathchar "3"0"00227A } -\def\precapprox {\Umathchar "3"0"002AB7 } -\def\preccurlyeq {\Umathchar "3"0"00227C } -\def\preceq {\Umathchar "3"0"002AAF } -\def\preceqq {\Umathchar "3"0"002AB3 } -\def\precnapprox {\Umathchar "3"0"002AB9 } -\def\precneq {\Umathchar "3"0"002AB1 } -\def\precneqq {\Umathchar "3"0"002AB5 } -\def\precnsim {\Umathchar "3"0"0022E8 } -\def\precsim {\Umathchar "3"0"00227E } -\def\prime {\Umathchar "0"0"002032 } -\def\primes {\Umathchar "0"0"002119 } -\def\prod {\Umathchar "1"0"00220F } -\def\PropertyLine {\Umathchar "0"0"00214A } -\def\propto {\Umathchar "3"0"00221D } -\def\Psi {\Umathchar "0"0"0003A8 } -\def\psi {\Umathchar "0"0"0003C8 } -\def\questionedeq {\Umathchar "3"0"00225F } -\def\rangle {\Udelimiter "5"0"0027E9 } -\def\rationals {\Umathchar "0"0"00211A } -\def\rbrace {\Udelimiter "5"0"00007D } -\def\rbrack {\Udelimiter "5"0"00005D } -\def\rceil {\Udelimiter "5"0"002309 } -\def\rceiling {\Udelimiter "5"0"002309 } -\def\Rdsh {\Umathchar "3"0"0021B3 } -\def\Re {\Umathchar "0"0"00211C } -\def\reals {\Umathchar "0"0"00211D } -\def\Relbar {\Umathchar "3"0"00003D } -\def\relbar {\Umathchar "3"0"002212 } -\def\restriction {\Umathchar "3"0"0021BE } -\def\rfloor {\Udelimiter "5"0"00230B } -\def\rgroup {\Udelimiter "5"0"0027EF } -\def\Rho {\Umathchar "0"0"0003A1 } -\def\rho {\Umathchar "0"0"0003C1 } -\def\rhook {\Umathchar "3"0"0FE323 } -\def\rhooknearrow {\Umathchar "3"0"002924 } -\def\rhookswarrow {\Umathchar "3"0"002926 } -\def\rightangle {\Umathchar "0"0"00221F } -\def\rightarrow {\Umathchar "3"0"002192 } -\def\Rightarrow {\Umathchar "3"0"0021D2 } -\def\rightarrowbar {\Umathchar "3"0"0021E5 } -\def\rightarrowtail {\Umathchar "3"0"0021A3 } -\def\rightarrowtriangle {\Umathchar "3"0"0021FE } -\def\rightdasharrow {\Umathchar "3"0"0021E2 } -\def\rightharpoondown {\Umathchar "3"0"0021C1 } -\def\rightharpoonup {\Umathchar "3"0"0021C0 } -\def\rightleftarrows {\Umathchar "3"0"0021C4 } -\def\rightleftharpoons {\Umathchar "3"0"0021CC } -\def\rightrightarrows {\Umathchar "3"0"0021C9 } -\def\rightsquigarrow {\Umathchar "3"0"0021DD } -\def\rightthreearrows {\Umathchar "3"0"0021F6 } -\def\rightthreetimes {\Umathchar "2"0"0022CC } -\def\rightwavearrow {\Umathchar "3"0"00219D } -\def\rightwhitearrow {\Umathchar "0"0"0021E8 } -\def\risingdotseq {\Umathchar "3"0"002253 } -\def\rmoustache {\Udelimiter "5"0"0023B1 } -\def\rneq {\Umathchar "3"0"002A88 } -\def\rparent {\Udelimiter "5"0"000029 } -\def\rrangle {\Udelimiter "5"0"0027EB } -\def\rrbracket {\Udelimiter "5"0"0027E7 } -\def\Rrightarrow {\Umathchar "3"0"0021DB } -\def\Rsh {\Umathchar "3"0"0021B1 } -\def\rtimes {\Umathchar "2"0"0022CA } -\def\rvert {\Udelimiter "5"0"00007C } -\def\rVert {\Udelimiter "5"0"002016 } -\def\S {\Umathchar "0"0"0000A7 } -\def\searrow {\Umathchar "3"0"002198 } -\def\Searrow {\Umathchar "3"0"0021D8 } -\def\setminus {\Umathchar "2"0"002216 } -\def\sharp {\Umathchar "0"0"00266F } -\def\Sigma {\Umathchar "0"0"0003A3 } -\def\sigma {\Umathchar "0"0"0003C3 } -\def\sim {\Umathchar "3"0"00223C } -\def\simeq {\Umathchar "3"0"002243 } -\def\slash {\Umathchar "0"0"002044 } -\def\smile {\Umathchar "3"0"002323 } -\def\solidus {\Udelimiter "5"0"002044 } -\def\spadesuit {\Umathchar "0"0"002660 } -\def\sphericalangle {\Umathchar "0"0"002222 } -\def\sqcap {\Umathchar "2"0"002293 } -\def\sqcup {\Umathchar "2"0"002294 } -\def\sqsubset {\Umathchar "3"0"00228F } -\def\sqsubseteq {\Umathchar "2"0"002291 } -\def\sqsubsetneq {\Umathchar "3"0"0022E4 } -\def\sqsupset {\Umathchar "3"0"002290 } -\def\sqsupseteq {\Umathchar "2"0"002292 } -\def\sqsupsetneq {\Umathchar "3"0"0022E5 } -\def\square {\Umathchar "0"0"0025A1 } -\def\squaredots {\Umathchar "3"0"002237 } -\def\star {\Umathchar "2"0"0022C6 } -\def\stareq {\Umathchar "3"0"00225B } -\def\subset {\Umathchar "3"0"002282 } -\def\Subset {\Umathchar "3"0"0022D0 } -\def\subseteq {\Umathchar "3"0"002286 } -\def\subseteqq {\Umathchar "3"0"002AC5 } -\def\subsetneq {\Umathchar "3"0"00228A } -\def\subsetneqq {\Umathchar "3"0"002ACB } -\def\succ {\Umathchar "3"0"00227B } -\def\succapprox {\Umathchar "3"0"002AB8 } -\def\succcurlyeq {\Umathchar "3"0"00227D } -\def\succeq {\Umathchar "3"0"002AB0 } -\def\succeqq {\Umathchar "3"0"002AB4 } -\def\succnapprox {\Umathchar "3"0"002ABA } -\def\succneq {\Umathchar "3"0"002AB2 } -\def\succneqq {\Umathchar "3"0"002AB6 } -\def\succnsim {\Umathchar "3"0"0022E9 } -\def\succsim {\Umathchar "3"0"00227F } -\def\sum {\Umathchar "1"0"002211 } -\def\supset {\Umathchar "3"0"002283 } -\def\Supset {\Umathchar "3"0"0022D1 } -\def\supseteq {\Umathchar "3"0"002287 } -\def\supseteqq {\Umathchar "3"0"002AC6 } -\def\supsetneq {\Umathchar "3"0"00228B } -\def\supsetneqq {\Umathchar "3"0"002ACC } -\def\surd {\Umathchar "2"0"00221A } -\def\swarrow {\Umathchar "3"0"002199 } -\def\Swarrow {\Umathchar "3"0"0021D9 } -\def\Tau {\Umathchar "0"0"0003A4 } -\def\tau {\Umathchar "0"0"0003C4 } -\def\therefore {\Umathchar "3"0"002234 } -\def\Theta {\Umathchar "0"0"000398 } -\def\theta {\Umathchar "0"0"0003B8 } -\def\tilde {\Umathaccent"0"0"0002DC } -\def\times {\Umathchar "2"0"0000D7 } -\def\to {\Umathchar "3"0"002192 } -\def\top {\Umathchar "0"0"0022A4 } -\def\triangle {\Umathchar "0"0"0025B3 } -\def\triangledown {\Umathchar "2"0"0025BD } -\def\triangleleft {\Umathchar "2"0"0025C1 } -\def\triangleq {\Umathchar "3"0"00225C } -\def\triangleright {\Umathchar "2"0"0025B7 } -\def\tripleprime {\Umathchar "0"0"002034 } -\def\turnediota {\Umathchar "0"0"002129 } -\def\twoheaddownarrow {\Umathchar "3"0"0021A1 } -\def\twoheadleftarrow {\Umathchar "3"0"00219E } -\def\twoheadrightarrow {\Umathchar "3"0"0021A0 } -\def\twoheadrightarrowtail {\Umathchar "3"0"002916 } -\def\twoheaduparrow {\Umathchar "3"0"00219F } -\def\udots {\Umathchar "0"0"0022F0 } -\def\ulcorner {\Udelimiter "4"0"00231C } -\def\underbar {\Umathaccent bottom "0"0"00203E } -\def\underbrace {\Umathaccent bottom "0"0"0023DF } -\def\underbracket {\Umathaccent bottom "0"0"0023B5 } -\def\underparent {\Umathaccent bottom "0"0"0023DD } -\def\upand {\Umathchar "2"0"00214B } -\def\uparrow {\Umathchar "3"0"002191 } -\def\Uparrow {\Umathchar "3"0"0021D1 } -\def\updasharrow {\Umathchar "3"0"0021E1 } -\def\updownarrow {\Umathchar "3"0"002195 } -\def\Updownarrow {\Umathchar "3"0"0021D5 } -\def\updownarrowbar {\Umathchar "0"0"0021A8 } -\def\updownarrows {\Umathchar "3"0"0021C5 } -\def\upharpoonleft {\Umathchar "3"0"0021BF } -\def\upharpoonright {\Umathchar "3"0"0021BE } -\def\uplus {\Umathchar "2"0"00228E } -\def\Upsilon {\Umathchar "0"0"0003A5 } -\def\upsilon {\Umathchar "0"0"0003C5 } -\def\upuparrows {\Umathchar "3"0"0021C8 } -\def\upwhitearrow {\Umathchar "0"0"0021E7 } -\def\urcorner {\Udelimiter "5"0"00231D } -\def\Uuparrow {\Umathchar "3"0"00290A } -\def\varepsilon {\Umathchar "0"0"0003B5 } -\def\varkappa {\Umathchar "0"0"0003F0 } -\def\varkappa {\Umathchar "0"0"0003F0 } -\def\varnothing {\Umathchar "0"0"002300 } -\def\varphi {\Umathchar "0"0"0003C6 } -\def\varpi {\Umathchar "0"0"0003D6 } -\def\varrho {\Umathchar "0"0"01D71A } -\def\varsigma {\Umathchar "0"0"0003C2 } -\def\vartheta {\Umathchar "0"0"01D717 } -\def\varTheta {\Umathchar "0"0"0003D1 } -\def\vdash {\Umathchar "3"0"0022A2 } -\def\vDash {\Umathchar "3"0"0022A8 } -\def\Vdash {\Umathchar "3"0"0022A9 } -\def\VDash {\Umathchar "3"0"0022AB } -\def\vdots {\Umathchar "0"0"0022EE } -\def\vec {\Umathaccent"0"0"0020D7 } -\def\vee {\Umathchar "2"0"002228 } -\def\veebar {\Umathchar "2"0"0022BB } -\def\veeeq {\Umathchar "3"0"00225A } -\def\vert {\Udelimiter "0"0"00007C } -\def\Vert {\Udelimiter "0"0"002016 } -\def\Vvdash {\Umathchar "3"0"0022AA } -\def\wedge {\Umathchar "2"0"002227 } -\def\wedgeeq {\Umathchar "3"0"002259 } -\def\whitearrowupfrombar {\Umathchar "0"0"0021EB } -\def\widehat {\Umathaccent"0"0"000302 } -\def\widetilde {\Umathaccent"0"0"000303 } -\def\wp {\Umathchar "0"0"002118 } -\def\wr {\Umathchar "2"0"002240 } -\def\Xi {\Umathchar "0"0"00039E } -\def\xi {\Umathchar "0"0"0003BE } -\def\yen {\Umathchar "0"0"0000A5 } -\def\Zeta {\Umathchar "0"0"000396 } -\def\zeta {\Umathchar "0"0"0003B6 } +\protected\def\acute {\Umathaccent"0"0"0000B4 } +\protected\def\acwopencirclearrow {\Umathchar "3"0"0021BA } +\protected\def\aleph {\Umathchar "0"0"002135 } +\protected\def\Alpha {\Umathchar "0"0"000391 } +\protected\def\alpha {\Umathchar "0"0"0003B1 } +\protected\def\amalg {\Umathchar "2"0"002A3F } +\protected\def\angle {\Umathchar "0"0"002220 } +\protected\def\Angstrom {\Umathchar "0"0"00212B } +\protected\def\approx {\Umathchar "3"0"002248 } +\protected\def\approxEq {\Umathchar "3"0"002245 } +\protected\def\approxeq {\Umathchar "3"0"00224A } +\protected\def\approxnEq {\Umathchar "3"0"002247 } +\protected\def\arrowvert {\Umathchar "0"0"00007C } +\protected\def\Arrowvert {\Umathchar "0"0"002016 } +\protected\def\ast {\Umathchar "2"0"002217 } +\protected\def\ast {\Umathchar "2"0"002217 } +\protected\def\asymp {\Umathchar "3"0"00224D } +\protected\def\backepsilon {\Umathchar "0"0"0003F6 } +\protected\def\backprime {\Umathchar "0"0"012035 } +\protected\def\backsim {\Umathchar "3"0"00223D } +\protected\def\backslash {\Umathchar "0"0"00005C } +\protected\def\bar {\Umathaccent"0"0"0000AF } +\protected\def\barleftarrow {\Umathchar "3"0"0021E4 } +\protected\def\barleftarrowrightarrowbar {\Umathchar "3"0"0021B9 } +\protected\def\barovernorthwestarrow {\Umathchar "3"0"0021B8 } +\protected\def\barwedge {\Umathchar "2"0"0022BC } +\protected\def\because {\Umathchar "3"0"002235 } +\protected\def\Beta {\Umathchar "0"0"000392 } +\protected\def\beta {\Umathchar "0"0"0003B2 } +\protected\def\beth {\Umathchar "0"0"002136 } +\protected\def\between {\Umathchar "3"0"00226C } +\protected\def\bigcap {\Umathchar "1"0"0022C2 } +\protected\def\bigcirc {\Umathchar "2"0"0025EF } +\protected\def\bigcircle {\Umathchar "2"0"0020DD } +\protected\def\bigcircle {\Umathchar "2"0"0020DD } +\protected\def\bigcup {\Umathchar "1"0"0022C3 } +\protected\def\bigdiamond {\Umathchar "0"0"0020DF } +\protected\def\bigodot {\Umathchar "1"0"002A00 } +\protected\def\bigoplus {\Umathchar "1"0"002A01 } +\protected\def\bigotimes {\Umathchar "1"0"002A02 } +\protected\def\bigsqcap {\Umathchar "1"0"002A05 } +\protected\def\bigsqcup {\Umathchar "1"0"002A06 } +\protected\def\bigsquare {\Umathchar "0"0"0020DE } +\protected\def\bigstar {\Umathchar "0"0"002605 } +\protected\def\bigtimes {\Umathchar "1"0"002A09 } +\protected\def\bigtriangledown {\Umathchar "2"0"0025BD } +\protected\def\bigtriangleup {\Umathchar "2"0"0025B3 } +\protected\def\bigudot {\Umathchar "1"0"002A03 } +\protected\def\biguplus {\Umathchar "1"0"002A04 } +\protected\def\bigvee {\Umathchar "1"0"0022C1 } +\protected\def\bigwedge {\Umathchar "1"0"0022C0 } +\protected\def\blacklozenge {\Umathchar "0"0"002666 } +\protected\def\blacksquare {\Umathchar "0"0"0025A0 } +\protected\def\blacktriangle {\Umathchar "2"0"0025B2 } +\protected\def\blacktriangledown {\Umathchar "2"0"0025BC } +\protected\def\blacktriangleleft {\Umathchar "2"0"0025C0 } +\protected\def\blacktriangleright {\Umathchar "2"0"0025B6 } +\protected\def\bot {\Umathchar "0"0"0022A5 } +\protected\def\bowtie {\Umathchar "3"0"0022C8 } +\protected\def\Box {\Umathchar "0"0"0025A1 } +\protected\def\boxdot {\Umathchar "2"0"0022A1 } +\protected\def\boxminus {\Umathchar "2"0"00229F } +\protected\def\boxplus {\Umathchar "2"0"00229E } +\protected\def\boxtimes {\Umathchar "2"0"0022A0 } +%protected\def\braceld {\Umathchar "0"0"000000 } +%protected\def\bracerd {\Umathchar "0"0"000000 } +%protected\def\bracelu {\Umathchar "0"0"000000 } +%protected\def\braceru {\Umathchar "0"0"000000 } +\protected\def\breve {\Umathaccent"0"0"0002D8 } +\protected\def\bullet {\Umathchar "2"0"002022 } +\protected\def\bullet {\Umathchar "2"0"002022 } +\protected\def\Bumpeq {\Umathchar "3"0"00224E } +\protected\def\cap {\Umathchar "2"0"002229 } +\protected\def\Cap {\Umathchar "2"0"0022D2 } +\protected\def\carriagereturn {\Umathchar "0"0"0021B5 } +\protected\def\cdot {\Umathchar "2"0"0022C5 } +\protected\def\cdotp {\Umathchar "6"0"0022C5 } +\protected\def\cdots {\Umathchar "0"0"0022EF } +\protected\def\centerdot {\Umathchar "2"0"0000B7 } +\protected\def\check {\Umathaccent"0"0"0002C7 } +\protected\def\checkmark {\Umathchar "0"0"002713 } +\protected\def\Chi {\Umathchar "0"0"0003A7 } +\protected\def\chi {\Umathchar "0"0"0003C7 } +\protected\def\circ {\Umathchar "2"0"002218 } +\protected\def\circeq {\Umathchar "3"0"002257 } +\protected\def\circlearrowleft {\Umathchar "3"0"0021BB } +\protected\def\circlearrowright {\Umathchar "3"0"0021BA } +\protected\def\circledast {\Umathchar "2"0"00229B } +\protected\def\circledcirc {\Umathchar "2"0"00229A } +\protected\def\circleddash {\Umathchar "2"0"00229D } +\protected\def\circledequals {\Umathchar "2"0"00229C } +\protected\def\circledR {\Umathchar "0"0"0024C7 } +\protected\def\circledS {\Umathchar "0"0"0024C8 } +\protected\def\circleonrightarrow {\Umathchar "3"0"0021F4 } +\protected\def\clubsuit {\Umathchar "0"0"002663 } +\protected\def\colon {\Umathchar "6"0"002236 } +\protected\def\colonequals {\Umathchar "3"0"002254 } +\protected\def\complement {\Umathchar "0"0"002201 } +\protected\def\complexes {\Umathchar "0"0"002102 } +\protected\def\cong {\Umathchar "3"0"002245 } +\protected\def\coprod {\Umathchar "1"0"002210 } +\protected\def\cup {\Umathchar "2"0"00222A } +\protected\def\Cup {\Umathchar "2"0"0022D3 } +\protected\def\curlyeqprec {\Umathchar "3"0"0022DE } +\protected\def\curlyeqsucc {\Umathchar "3"0"0022DF } +\protected\def\curlyvee {\Umathchar "2"0"0022CE } +\protected\def\curlywedge {\Umathchar "2"0"0022CF } +\protected\def\curvearrowleft {\Umathchar "3"0"0021B6 } +\protected\def\curvearrowright {\Umathchar "3"0"0021B7 } +\protected\def\cwopencirclearrow {\Umathchar "3"0"0021BB } +\protected\def\dag {\Umathchar "0"0"002020 } +\protected\def\dagger {\Umathchar "2"0"002020 } +\protected\def\daleth {\Umathchar "0"0"002138 } +\protected\def\dasharrow {\Umathchar "3"0"0021E2 } +\protected\def\dashedleftarrow {\Umathchar "3"0"00290C } +\protected\def\dashedrightarrow {\Umathchar "3"0"00290D } +\protected\def\dashv {\Umathchar "3"0"0022A3 } +\protected\def\ddag {\Umathchar "0"0"002021 } +\protected\def\ddagger {\Umathchar "2"0"002021 } +\protected\def\dddot {\Umathaccent"0"0"0020DB } +\protected\def\ddot {\Umathaccent"0"0"0000A8 } +\protected\def\ddots {\Umathchar "0"0"0022F1 } +\protected\def\Ddownarrow {\Umathchar "3"0"00290B } +\protected\def\definedeq {\Umathchar "3"0"00225D } +\protected\def\Delta {\Umathchar "0"0"000394 } +\protected\def\delta {\Umathchar "0"0"0003B4 } +\protected\def\diamond {\Umathchar "2"0"0022C4 } +\protected\def\diamondsuit {\Umathchar "0"0"002662 } +\protected\def\differentialD {\Umathchar "0"0"002145 } +\protected\def\differentiald {\Umathchar "0"0"002146 } +\protected\def\digamma {\Umathchar "0"0"0003DC } +\protected\def\div {\Umathchar "2"0"0000F7 } +\protected\def\divideontimes {\Umathchar "2"0"0022C7 } +\protected\def\divides {\Umathchar "2"0"002223 } +\protected\def\dot {\Umathaccent"0"0"0002D9 } +\protected\def\doteq {\Umathchar "3"0"002250 } +\protected\def\Doteq {\Umathchar "3"0"002251 } +\protected\def\doteqdot {\Umathchar "3"0"002251 } +\protected\def\dotminus {\Umathchar "2"0"002238 } +\protected\def\dotplus {\Umathchar "2"0"002214 } +\protected\def\dots {\Umathchar "0"0"002026 } +\protected\def\dottedrightarrow {\Umathchar "3"0"002911 } +\protected\def\doublecap {\Umathchar "2"0"0022D2 } +\protected\def\doublecup {\Umathchar "2"0"0022D3 } +\protected\def\doubleprime {\Umathchar "0"0"002033 } +\protected\def\downarrow {\Umathchar "3"0"002193 } +\protected\def\Downarrow {\Umathchar "3"0"0021D3 } +\protected\def\downdasharrow {\Umathchar "3"0"0021E3 } +\protected\def\downdownarrows {\Umathchar "3"0"0021CA } +\protected\def\downharpoonleft {\Umathchar "3"0"0021C3 } +\protected\def\downharpoonright {\Umathchar "3"0"0021C2 } +\protected\def\downuparrows {\Umathchar "3"0"0021F5 } +\protected\def\downwhitearrow {\Umathchar "0"0"0021E9 } +\protected\def\downzigzagarrow {\Umathchar "3"0"0021AF } +\protected\def\ell {\Umathchar "0"0"002113 } +\protected\def\emptyset {\Umathchar "0"0"002205 } +\protected\def\Epsilon {\Umathchar "0"0"000395 } +\protected\def\epsilon {\Umathchar "0"0"0003F5 } +\protected\def\eq {\Umathchar "3"0"00003D } +\protected\def\eqcirc {\Umathchar "3"0"002256 } +\protected\def\eqgtr {\Umathchar "3"0"0022DD } +\protected\def\eqless {\Umathchar "3"0"0022DC } +\protected\def\eqsim {\Umathchar "3"0"002242 } +\protected\def\eqslantgtr {\Umathchar "3"0"002A96 } +\protected\def\eqslantless {\Umathchar "3"0"002A95 } +\protected\def\equalscolon {\Umathchar "3"0"002255 } +\protected\def\equiv {\Umathchar "3"0"002261 } +\protected\def\Eta {\Umathchar "0"0"000397 } +\protected\def\eta {\Umathchar "0"0"0003B7 } +\protected\def\eth {\Umathchar "0"0"0000F0 } +\protected\def\Eulerconst {\Umathchar "0"0"002107 } +\protected\def\exists {\Umathchar "0"0"002203 } +\protected\def\exponentiale {\Umathchar "0"0"002147 } +\protected\def\fallingdotseq {\Umathchar "3"0"002252 } +\protected\def\Finv {\Umathchar "0"0"002132 } +\protected\def\flat {\Umathchar "0"0"00266D } +\protected\def\forall {\Umathchar "0"0"002200 } +\protected\def\frown {\Umathchar "3"0"002322 } +\protected\def\Game {\Umathchar "0"0"002141 } +\protected\def\Gamma {\Umathchar "0"0"000393 } +\protected\def\gamma {\Umathchar "0"0"0003B3 } +\protected\def\ge {\Umathchar "3"0"002265 } +\protected\def\geq {\Umathchar "3"0"002265 } +\protected\def\geqq {\Umathchar "3"0"002267 } +\protected\def\geqslant {\Umathchar "3"0"002A7E } +\protected\def\gets {\Umathchar "3"0"002190 } +\protected\def\gg {\Umathchar "3"0"00226B } +\protected\def\ggg {\Umathchar "3"0"0022D9 } +\protected\def\gggtr {\Umathchar "3"0"0022D9 } +\protected\def\gimel {\Umathchar "0"0"002137 } +\protected\def\gnapprox {\Umathchar "3"0"002A8A } +\protected\def\gneqq {\Umathchar "3"0"002269 } +\protected\def\gnsim {\Umathchar "3"0"0022E7 } +\protected\def\grave {\Umathaccent"0"0"000060 } +\protected\def\gt {\Umathchar "3"0"00003E } +\protected\def\gtrapprox {\Umathchar "3"0"002A86 } +\protected\def\gtrdot {\Umathchar "2"0"0022D7 } +\protected\def\gtreqless {\Umathchar "3"0"0022DB } +\protected\def\gtreqqless {\Umathchar "3"0"002A8C } +\protected\def\gtrless {\Umathchar "3"0"002277 } +\protected\def\gtrsim {\Umathchar "3"0"002273 } +\protected\def\hat {\Umathaccent"0"0"0002C6 } +\protected\def\hbar {\Umathchar "0"0"00210F } +\protected\def\heartsuit {\Umathchar "0"0"002661 } +\protected\def\hookleftarrow {\Umathchar "3"0"0021A9 } +\protected\def\hookrightarrow {\Umathchar "3"0"0021AA } +\protected\def\hslash {\Umathchar "0"0"00210F } +\protected\def\iiint {\Umathchar "1"0"00222D } +\protected\def\iiintop {\Umathchar "0"0"00222D } +\protected\def\iint {\Umathchar "1"0"00222C } +\protected\def\iintop {\Umathchar "0"0"00222C } +\protected\def\Im {\Umathchar "0"0"002111 } +\protected\def\imaginaryi {\Umathchar "0"0"002148 } +\protected\def\imaginaryj {\Umathchar "0"0"002149 } +\protected\def\imath {\Umathchar "0"0"01D6A4 } +\protected\def\imply {\Umathchar "3"0"0021D2 } +\protected\def\in {\Umathchar "0"0"002208 } +\protected\def\infty {\Umathchar "0"0"00221E } +\protected\def\int {\Umathchar "1"0"00222B } +\protected\def\intclockwise {\Umathchar "1"0"002231 } +\protected\def\integers {\Umathchar "0"0"002124 } +\protected\def\intercal {\Umathchar "2"0"0022BA } +\protected\def\intop {\Umathchar "0"0"00222B } +\protected\def\Iota {\Umathchar "0"0"000399 } +\protected\def\iota {\Umathchar "0"0"0003B9 } +\protected\def\jmath {\Umathchar "0"0"01D6A5 } +\protected\def\Join {\Umathchar "3"0"0022C8 } +\protected\def\Kappa {\Umathchar "0"0"00039A } +\protected\def\kappa {\Umathchar "0"0"0003BA } +\protected\def\Lambda {\Umathchar "0"0"00039B } +\protected\def\lambda {\Umathchar "0"0"0003BB } +\protected\def\land {\Umathchar "2"0"002227 } +\protected\def\langle {\Udelimiter "4"0"0027E8 } +\protected\def\lbrace {\Udelimiter "4"0"00007B } +\protected\def\lbrack {\Udelimiter "4"0"00005B } +\protected\def\lceil {\Udelimiter "4"0"002308 } +\protected\def\lceiling {\Udelimiter "4"0"002308 } +\protected\def\ldotp {\Umathchar "6"0"00002E } +\protected\def\ldots {\Umathchar "0"0"002026 } +\protected\def\Ldsh {\Umathchar "3"0"0021B2 } +\protected\def\le {\Umathchar "3"0"002264 } +\protected\def\leadsto {\Umathchar "3"0"0021DD } +\protected\def\leftarrow {\Umathchar "3"0"002190 } +\protected\def\Leftarrow {\Umathchar "3"0"0021D0 } +\protected\def\leftarrowtail {\Umathchar "3"0"0021A2 } +\protected\def\leftarrowtriangle {\Umathchar "3"0"0021FD } +\protected\def\leftdasharrow {\Umathchar "3"0"0021E0 } +\protected\def\leftharpoondown {\Umathchar "3"0"0021BD } +\protected\def\leftharpoonup {\Umathchar "3"0"0021BC } +\protected\def\leftleftarrows {\Umathchar "3"0"0021C7 } +\protected\def\leftrightarrow {\Umathchar "3"0"002194 } +\protected\def\Leftrightarrow {\Umathchar "3"0"0021D4 } +\protected\def\leftrightarrows {\Umathchar "3"0"0021C6 } +\protected\def\leftrightarrowtriangle {\Umathchar "3"0"0021FF } +\protected\def\leftrightharpoons {\Umathchar "3"0"0021CB } +\protected\def\leftrightsquigarrow {\Umathchar "3"0"0021AD } +\protected\def\leftsquigarrow {\Umathchar "3"0"0021DC } +\protected\def\leftthreetimes {\Umathchar "2"0"0022CB } +\protected\def\leftwavearrow {\Umathchar "3"0"00219C } +\protected\def\leftwhitearrow {\Umathchar "0"0"0021E6 } +\protected\def\leq {\Umathchar "3"0"002264 } +\protected\def\leqq {\Umathchar "3"0"002266 } +\protected\def\leqslant {\Umathchar "3"0"002A7D } +\protected\def\lessapprox {\Umathchar "3"0"002A85 } +\protected\def\lessdot {\Umathchar "2"0"0022D6 } +\protected\def\lesseqgtr {\Umathchar "3"0"0022DA } +\protected\def\lesseqqgtr {\Umathchar "3"0"002A8B } +\protected\def\lessgtr {\Umathchar "3"0"002276 } +\protected\def\lesssim {\Umathchar "3"0"002272 } +\protected\def\lfloor {\Udelimiter "4"0"00230A } +\protected\def\lgroup {\Udelimiter "4"0"0027EE } +\protected\def\lhook {\Umathchar "3"0"0FE322 } +\protected\def\lhooknwarrow {\Umathchar "3"0"002923 } +\protected\def\lhooksearrow {\Umathchar "3"0"002925 } +\protected\def\linefeed {\Umathchar "0"0"0021B4 } +\protected\def\ll {\Umathchar "3"0"00226A } +\protected\def\llangle {\Udelimiter "4"0"0027EA } +\protected\def\llbracket {\Udelimiter "4"0"0027E6 } +\protected\def\llcorner {\Udelimiter "4"0"00231E } +\protected\def\Lleftarrow {\Umathchar "3"0"0021DA } +\protected\def\lll {\Umathchar "3"0"0022D8 } +\protected\def\llless {\Umathchar "3"0"0022D8 } +\protected\def\lmoustache {\Udelimiter "4"0"0023B0 } +\protected\def\lnapprox {\Umathchar "3"0"002A89 } +\protected\def\lneq {\Umathchar "3"0"002A87 } +\protected\def\lneqq {\Umathchar "3"0"002268 } +\protected\def\lnot {\Umathchar "0"0"0000AC } +\protected\def\lnsim {\Umathchar "3"0"0022E6 } +\protected\def\longleftarrow {\Umathchar "3"0"0027F5 } +\protected\def\Longleftarrow {\Umathchar "3"0"0027F8 } +\protected\def\longleftrightarrow {\Umathchar "3"0"0027F7 } +\protected\def\Longleftrightarrow {\Umathchar "3"0"0027FA } +\protected\def\longmapsfrom {\Umathchar "3"0"0027FB } +\protected\def\Longmapsfrom {\Umathchar "3"0"0027FD } +\protected\def\longmapsto {\Umathchar "3"0"0027FC } +\protected\def\Longmapsto {\Umathchar "3"0"0027FE } +\protected\def\longrightarrow {\Umathchar "3"0"0027F6 } +\protected\def\Longrightarrow {\Umathchar "3"0"0027F9 } +\protected\def\longrightsquigarrow {\Umathchar "3"0"0027FF } +\protected\def\looparrowleft {\Umathchar "3"0"0021AB } +\protected\def\looparrowright {\Umathchar "3"0"0021AC } +\protected\def\lor {\Umathchar "2"0"002228 } +\protected\def\lozenge {\Umathchar "0"0"0025CA } +\protected\def\lparent {\Udelimiter "4"0"000028 } +\protected\def\lrcorner {\Udelimiter "5"0"00231F } +\protected\def\Lsh {\Umathchar "3"0"0021B0 } +\protected\def\lt {\Umathchar "3"0"00003C } +\protected\def\ltimes {\Umathchar "2"0"0022C9 } +\protected\def\lvert {\Udelimiter "4"0"00007C } +\protected\def\lVert {\Udelimiter "4"0"002016 } +\protected\def\maltese {\Umathchar "0"0"002720 } +\protected\def\mapsdown {\Umathchar "3"0"0021A7 } +\protected\def\mapsfrom {\Umathchar "3"0"0021A4 } +\protected\def\Mapsfrom {\Umathchar "3"0"002906 } +\protected\def\mapsfromchar {\Umathchar "3"0"0FE324 } +\protected\def\mapsto {\Umathchar "3"0"0021A6 } +\protected\def\Mapsto {\Umathchar "3"0"002907 } +\protected\def\mapstochar {\Umathchar "3"0"0FE321 } +\protected\def\mapsup {\Umathchar "3"0"0021A5 } +\protected\def\mathring {\Umathaccent"0"0"0002DA } +\protected\def\measuredangle {\Umathchar "0"0"002221 } +\protected\def\measuredeq {\Umathchar "3"0"00225E } +\protected\def\mho {\Umathchar "0"0"002127 } +\protected\def\mid {\Umathchar "3"0"00007C } +\protected\def\minus {\Umathchar "2"0"002212 } +\protected\def\minuscolon {\Umathchar "2"0"002239 } +\protected\def\models {\Umathchar "3"0"0022A7 } +\protected\def\mp {\Umathchar "2"0"002213 } +\protected\def\Mu {\Umathchar "0"0"00039C } +\protected\def\mu {\Umathchar "0"0"0003BC } +\protected\def\multimap {\Umathchar "3"0"0022B8 } +\protected\def\napprox {\Umathchar "3"0"002249 } +\protected\def\napproxEq {\Umathchar "3"0"002246 } +\protected\def\nasymp {\Umathchar "3"0"00226D } +\protected\def\natural {\Umathchar "0"0"00266E } +\protected\def\naturalnumbers {\Umathchar "0"0"002115 } +\protected\def\ncong {\Umathchar "3"0"002246 } +\protected\def\ndivides {\Umathchar "2"0"002224 } +\protected\def\ne {\Umathchar "3"0"002260 } +\protected\def\nearrow {\Umathchar "3"0"002197 } +\protected\def\Nearrow {\Umathchar "3"0"0021D7 } +\protected\def\neg {\Umathchar "0"0"0000AC } +\protected\def\negativesign {\Umathchar "2"0"00207B } +\protected\def\neq {\Umathchar "3"0"002260 } +\protected\def\nequiv {\Umathchar "3"0"002262 } +\protected\def\neswarrow {\Umathchar "3"0"002922 } +\protected\def\nexists {\Umathchar "0"0"002204 } +\protected\def\ngeq {\Umathchar "3"0"002271 } +\protected\def\ngtr {\Umathchar "3"0"00226F } +\protected\def\ngtrless {\Umathchar "3"0"002279 } +\protected\def\ngtrsim {\Umathchar "3"0"002275 } +\protected\def\nHdownarrow {\Umathchar "3"0"0021DF } +\protected\def\nHuparrow {\Umathchar "3"0"0021DE } +\protected\def\ni {\Umathchar "3"0"00220B } +\protected\def\nin {\Umathchar "3"0"002209 } +\protected\def\nleftarrow {\Umathchar "3"0"00219A } +\protected\def\nLeftarrow {\Umathchar "3"0"0021CD } +\protected\def\nleftrightarrow {\Umathchar "3"0"0021AE } +\protected\def\nLeftrightarrow {\Umathchar "3"0"0021CE } +\protected\def\nleq {\Umathchar "3"0"002270 } +\protected\def\nless {\Umathchar "3"0"00226E } +\protected\def\nlessgtr {\Umathchar "3"0"002278 } +\protected\def\nlesssim {\Umathchar "3"0"002274 } +\protected\def\nmid {\Umathchar "3"0"002224 } +\protected\def\nni {\Umathchar "3"0"00220C } +\protected\def\not {\Umathchar "3"0"000338 } +\protected\def\notin {\Umathchar "3"0"002209 } +\protected\def\nowns {\Umathchar "3"0"00220C } +\protected\def\nparallel {\Umathchar "3"0"002226 } +\protected\def\nprec {\Umathchar "3"0"002280 } +\protected\def\npreccurlyeq {\Umathchar "3"0"0022E0 } +\protected\def\nrightarrow {\Umathchar "3"0"00219B } +\protected\def\nRightarrow {\Umathchar "3"0"0021CF } +\protected\def\nsim {\Umathchar "3"0"002241 } +\protected\def\nsimeq {\Umathchar "3"0"002244 } +\protected\def\nsqsubseteq {\Umathchar "3"0"0022E2 } +\protected\def\nsqsupseteq {\Umathchar "3"0"0022E3 } +\protected\def\nsubset {\Umathchar "3"0"002284 } +\protected\def\nsubseteq {\Umathchar "3"0"002288 } +\protected\def\nsucc {\Umathchar "3"0"002281 } +\protected\def\nsucccurlyeq {\Umathchar "3"0"0022E1 } +\protected\def\nsupset {\Umathchar "3"0"002285 } +\protected\def\nsupseteq {\Umathchar "3"0"002289 } +\protected\def\ntriangleleft {\Umathchar "3"0"0022EB } +\protected\def\ntrianglelefteq {\Umathchar "3"0"0022EC } +\protected\def\ntriangleright {\Umathchar "3"0"0022EA } +\protected\def\ntrianglerighteq {\Umathchar "3"0"0022ED } +\protected\def\Nu {\Umathchar "0"0"00039D } +\protected\def\nu {\Umathchar "0"0"0003BD } +\protected\def\nvdash {\Umathchar "3"0"0022AC } +\protected\def\nvDash {\Umathchar "3"0"0022AD } +\protected\def\nVdash {\Umathchar "3"0"0022AE } +\protected\def\nVDash {\Umathchar "3"0"0022AF } +\protected\def\nvleftarrow {\Umathchar "3"0"0021F7 } +\protected\def\nVleftarrow {\Umathchar "3"0"0021FA } +\protected\def\nvleftrightarrow {\Umathchar "3"0"0021F9 } +\protected\def\nVleftrightarrow {\Umathchar "3"0"0021FC } +\protected\def\nvrightarrow {\Umathchar "3"0"0021F8 } +\protected\def\nVrightarrow {\Umathchar "3"0"0021FB } +\protected\def\nwarrow {\Umathchar "3"0"002196 } +\protected\def\Nwarrow {\Umathchar "3"0"0021D6 } +\protected\def\nwsearrow {\Umathchar "3"0"002921 } +\protected\def\odot {\Umathchar "2"0"002299 } +\protected\def\ohm {\Umathchar "0"0"002126 } +\protected\def\oiiint {\Umathchar "1"0"002230 } +\protected\def\oiint {\Umathchar "1"0"00222F } +\protected\def\oint {\Umathchar "1"0"00222E } +\protected\def\ointclockwise {\Umathchar "1"0"002232 } +\protected\def\ointctrclockwise {\Umathchar "1"0"002233 } +\protected\def\Omega {\Umathchar "0"0"0003A9 } +\protected\def\omega {\Umathchar "0"0"0003C9 } +\protected\def\Omicron {\Umathchar "0"0"00039F } +\protected\def\omicron {\Umathchar "0"0"0003BF } +\protected\def\ominus {\Umathchar "2"0"002296 } +\protected\def\oplus {\Umathchar "2"0"002295 } +\protected\def\oslash {\Umathchar "2"0"002298 } +\protected\def\otimes {\Umathchar "2"0"002297 } +\protected\def\overbar {\Umathaccent"0"0"00203E } +\protected\def\overbrace {\Umathaccent"0"0"0023DE } +\protected\def\overbracket {\Umathaccent"0"0"0023B4 } +\protected\def\overparent {\Umathaccent"0"0"0023DC } +\protected\def\owns {\Umathchar "3"0"00220B } +\protected\def\P {\Umathchar "0"0"0000B6 } +\protected\def\parallel {\Umathchar "3"0"002225 } +\protected\def\partial {\Umathchar "0"0"002202 } +\protected\def\perp {\Umathchar "3"0"0022A5 } +\protected\def\Phi {\Umathchar "0"0"0003A6 } +\protected\def\phi {\Umathchar "0"0"0003D5 } +\protected\def\Pi {\Umathchar "0"0"0003A0 } +\protected\def\pi {\Umathchar "0"0"0003C0 } +\protected\def\pitchfork {\Umathchar "3"0"0022D4 } +\protected\def\Plankconst {\Umathchar "0"0"00210E } +\protected\def\pm {\Umathchar "2"0"0000B1 } +\protected\def\positivesign {\Umathchar "2"0"00207A } +\protected\def\prec {\Umathchar "3"0"00227A } +\protected\def\precapprox {\Umathchar "3"0"002AB7 } +\protected\def\preccurlyeq {\Umathchar "3"0"00227C } +\protected\def\preceq {\Umathchar "3"0"002AAF } +\protected\def\preceqq {\Umathchar "3"0"002AB3 } +\protected\def\precnapprox {\Umathchar "3"0"002AB9 } +\protected\def\precneq {\Umathchar "3"0"002AB1 } +\protected\def\precneqq {\Umathchar "3"0"002AB5 } +\protected\def\precnsim {\Umathchar "3"0"0022E8 } +\protected\def\precsim {\Umathchar "3"0"00227E } +\protected\def\prime {\Umathchar "0"0"002032 } +\protected\def\primes {\Umathchar "0"0"002119 } +\protected\def\prod {\Umathchar "1"0"00220F } +\protected\def\PropertyLine {\Umathchar "0"0"00214A } +\protected\def\propto {\Umathchar "3"0"00221D } +\protected\def\Psi {\Umathchar "0"0"0003A8 } +\protected\def\psi {\Umathchar "0"0"0003C8 } +\protected\def\questionedeq {\Umathchar "3"0"00225F } +\protected\def\rangle {\Udelimiter "5"0"0027E9 } +\protected\def\rationals {\Umathchar "0"0"00211A } +\protected\def\rbrace {\Udelimiter "5"0"00007D } +\protected\def\rbrack {\Udelimiter "5"0"00005D } +\protected\def\rceil {\Udelimiter "5"0"002309 } +\protected\def\rceiling {\Udelimiter "5"0"002309 } +\protected\def\Rdsh {\Umathchar "3"0"0021B3 } +\protected\def\Re {\Umathchar "0"0"00211C } +\protected\def\reals {\Umathchar "0"0"00211D } +\protected\def\Relbar {\Umathchar "3"0"00003D } +\protected\def\relbar {\Umathchar "3"0"002212 } +\protected\def\restriction {\Umathchar "3"0"0021BE } +\protected\def\rfloor {\Udelimiter "5"0"00230B } +\protected\def\rgroup {\Udelimiter "5"0"0027EF } +\protected\def\Rho {\Umathchar "0"0"0003A1 } +\protected\def\rho {\Umathchar "0"0"0003C1 } +\protected\def\rhook {\Umathchar "3"0"0FE323 } +\protected\def\rhooknearrow {\Umathchar "3"0"002924 } +\protected\def\rhookswarrow {\Umathchar "3"0"002926 } +\protected\def\rightangle {\Umathchar "0"0"00221F } +\protected\def\rightarrow {\Umathchar "3"0"002192 } +\protected\def\Rightarrow {\Umathchar "3"0"0021D2 } +\protected\def\rightarrowbar {\Umathchar "3"0"0021E5 } +\protected\def\rightarrowtail {\Umathchar "3"0"0021A3 } +\protected\def\rightarrowtriangle {\Umathchar "3"0"0021FE } +\protected\def\rightdasharrow {\Umathchar "3"0"0021E2 } +\protected\def\rightharpoondown {\Umathchar "3"0"0021C1 } +\protected\def\rightharpoonup {\Umathchar "3"0"0021C0 } +\protected\def\rightleftarrows {\Umathchar "3"0"0021C4 } +\protected\def\rightleftharpoons {\Umathchar "3"0"0021CC } +\protected\def\rightrightarrows {\Umathchar "3"0"0021C9 } +\protected\def\rightsquigarrow {\Umathchar "3"0"0021DD } +\protected\def\rightthreearrows {\Umathchar "3"0"0021F6 } +\protected\def\rightthreetimes {\Umathchar "2"0"0022CC } +\protected\def\rightwavearrow {\Umathchar "3"0"00219D } +\protected\def\rightwhitearrow {\Umathchar "0"0"0021E8 } +\protected\def\risingdotseq {\Umathchar "3"0"002253 } +\protected\def\rmoustache {\Udelimiter "5"0"0023B1 } +\protected\def\rneq {\Umathchar "3"0"002A88 } +\protected\def\rparent {\Udelimiter "5"0"000029 } +\protected\def\rrangle {\Udelimiter "5"0"0027EB } +\protected\def\rrbracket {\Udelimiter "5"0"0027E7 } +\protected\def\Rrightarrow {\Umathchar "3"0"0021DB } +\protected\def\Rsh {\Umathchar "3"0"0021B1 } +\protected\def\rtimes {\Umathchar "2"0"0022CA } +\protected\def\rvert {\Udelimiter "5"0"00007C } +\protected\def\rVert {\Udelimiter "5"0"002016 } +\protected\def\S {\Umathchar "0"0"0000A7 } +\protected\def\searrow {\Umathchar "3"0"002198 } +\protected\def\Searrow {\Umathchar "3"0"0021D8 } +\protected\def\setminus {\Umathchar "2"0"002216 } +\protected\def\sharp {\Umathchar "0"0"00266F } +\protected\def\Sigma {\Umathchar "0"0"0003A3 } +\protected\def\sigma {\Umathchar "0"0"0003C3 } +\protected\def\sim {\Umathchar "3"0"00223C } +\protected\def\simeq {\Umathchar "3"0"002243 } +\protected\def\slash {\Umathchar "0"0"002044 } +\protected\def\smile {\Umathchar "3"0"002323 } +\protected\def\solidus {\Udelimiter "5"0"002044 } +\protected\def\spadesuit {\Umathchar "0"0"002660 } +\protected\def\sphericalangle {\Umathchar "0"0"002222 } +\protected\def\sqcap {\Umathchar "2"0"002293 } +\protected\def\sqcup {\Umathchar "2"0"002294 } +\protected\def\sqsubset {\Umathchar "3"0"00228F } +\protected\def\sqsubseteq {\Umathchar "2"0"002291 } +\protected\def\sqsubsetneq {\Umathchar "3"0"0022E4 } +\protected\def\sqsupset {\Umathchar "3"0"002290 } +\protected\def\sqsupseteq {\Umathchar "2"0"002292 } +\protected\def\sqsupsetneq {\Umathchar "3"0"0022E5 } +\protected\def\square {\Umathchar "0"0"0025A1 } +\protected\def\squaredots {\Umathchar "3"0"002237 } +\protected\def\star {\Umathchar "2"0"0022C6 } +\protected\def\stareq {\Umathchar "3"0"00225B } +\protected\def\subset {\Umathchar "3"0"002282 } +\protected\def\Subset {\Umathchar "3"0"0022D0 } +\protected\def\subseteq {\Umathchar "3"0"002286 } +\protected\def\subseteqq {\Umathchar "3"0"002AC5 } +\protected\def\subsetneq {\Umathchar "3"0"00228A } +\protected\def\subsetneqq {\Umathchar "3"0"002ACB } +\protected\def\succ {\Umathchar "3"0"00227B } +\protected\def\succapprox {\Umathchar "3"0"002AB8 } +\protected\def\succcurlyeq {\Umathchar "3"0"00227D } +\protected\def\succeq {\Umathchar "3"0"002AB0 } +\protected\def\succeqq {\Umathchar "3"0"002AB4 } +\protected\def\succnapprox {\Umathchar "3"0"002ABA } +\protected\def\succneq {\Umathchar "3"0"002AB2 } +\protected\def\succneqq {\Umathchar "3"0"002AB6 } +\protected\def\succnsim {\Umathchar "3"0"0022E9 } +\protected\def\succsim {\Umathchar "3"0"00227F } +\protected\def\sum {\Umathchar "1"0"002211 } +\protected\def\supset {\Umathchar "3"0"002283 } +\protected\def\Supset {\Umathchar "3"0"0022D1 } +\protected\def\supseteq {\Umathchar "3"0"002287 } +\protected\def\supseteqq {\Umathchar "3"0"002AC6 } +\protected\def\supsetneq {\Umathchar "3"0"00228B } +\protected\def\supsetneqq {\Umathchar "3"0"002ACC } +\protected\def\surd {\Umathchar "2"0"00221A } +\protected\def\swarrow {\Umathchar "3"0"002199 } +\protected\def\Swarrow {\Umathchar "3"0"0021D9 } +\protected\def\Tau {\Umathchar "0"0"0003A4 } +\protected\def\tau {\Umathchar "0"0"0003C4 } +\protected\def\therefore {\Umathchar "3"0"002234 } +\protected\def\Theta {\Umathchar "0"0"000398 } +\protected\def\theta {\Umathchar "0"0"0003B8 } +\protected\def\tilde {\Umathaccent"0"0"0002DC } +\protected\def\times {\Umathchar "2"0"0000D7 } +\protected\def\to {\Umathchar "3"0"002192 } +\protected\def\top {\Umathchar "0"0"0022A4 } +\protected\def\triangle {\Umathchar "0"0"0025B3 } +\protected\def\triangledown {\Umathchar "2"0"0025BD } +\protected\def\triangleleft {\Umathchar "2"0"0025C1 } +\protected\def\triangleq {\Umathchar "3"0"00225C } +\protected\def\triangleright {\Umathchar "2"0"0025B7 } +\protected\def\tripleprime {\Umathchar "0"0"002034 } +\protected\def\turnediota {\Umathchar "0"0"002129 } +\protected\def\twoheaddownarrow {\Umathchar "3"0"0021A1 } +\protected\def\twoheadleftarrow {\Umathchar "3"0"00219E } +\protected\def\twoheadrightarrow {\Umathchar "3"0"0021A0 } +\protected\def\twoheadrightarrowtail {\Umathchar "3"0"002916 } +\protected\def\twoheaduparrow {\Umathchar "3"0"00219F } +\protected\def\udots {\Umathchar "0"0"0022F0 } +\protected\def\ulcorner {\Udelimiter "4"0"00231C } +\protected\def\underbar {\Umathaccent bottom "0"0"00203E } +\protected\def\underbrace {\Umathaccent bottom "0"0"0023DF } +\protected\def\underbracket {\Umathaccent bottom "0"0"0023B5 } +\protected\def\underparent {\Umathaccent bottom "0"0"0023DD } +\protected\def\upand {\Umathchar "2"0"00214B } +\protected\def\uparrow {\Umathchar "3"0"002191 } +\protected\def\Uparrow {\Umathchar "3"0"0021D1 } +\protected\def\updasharrow {\Umathchar "3"0"0021E1 } +\protected\def\updownarrow {\Umathchar "3"0"002195 } +\protected\def\Updownarrow {\Umathchar "3"0"0021D5 } +\protected\def\updownarrowbar {\Umathchar "0"0"0021A8 } +\protected\def\updownarrows {\Umathchar "3"0"0021C5 } +\protected\def\upharpoonleft {\Umathchar "3"0"0021BF } +\protected\def\upharpoonright {\Umathchar "3"0"0021BE } +\protected\def\uplus {\Umathchar "2"0"00228E } +\protected\def\Upsilon {\Umathchar "0"0"0003A5 } +\protected\def\upsilon {\Umathchar "0"0"0003C5 } +\protected\def\upuparrows {\Umathchar "3"0"0021C8 } +\protected\def\upwhitearrow {\Umathchar "0"0"0021E7 } +\protected\def\urcorner {\Udelimiter "5"0"00231D } +\protected\def\Uuparrow {\Umathchar "3"0"00290A } +\protected\def\varepsilon {\Umathchar "0"0"0003B5 } +\protected\def\varkappa {\Umathchar "0"0"0003F0 } +\protected\def\varkappa {\Umathchar "0"0"0003F0 } +\protected\def\varnothing {\Umathchar "0"0"002300 } +\protected\def\varphi {\Umathchar "0"0"0003C6 } +\protected\def\varpi {\Umathchar "0"0"0003D6 } +\protected\def\varrho {\Umathchar "0"0"01D71A } +\protected\def\varsigma {\Umathchar "0"0"0003C2 } +\protected\def\vartheta {\Umathchar "0"0"01D717 } +\protected\def\varTheta {\Umathchar "0"0"0003D1 } +\protected\def\vdash {\Umathchar "3"0"0022A2 } +\protected\def\vDash {\Umathchar "3"0"0022A8 } +\protected\def\Vdash {\Umathchar "3"0"0022A9 } +\protected\def\VDash {\Umathchar "3"0"0022AB } +\protected\def\vdots {\Umathchar "0"0"0022EE } +\protected\def\vec {\Umathaccent"0"0"0020D7 } +\protected\def\vee {\Umathchar "2"0"002228 } +\protected\def\veebar {\Umathchar "2"0"0022BB } +\protected\def\veeeq {\Umathchar "3"0"00225A } +\protected\def\vert {\Udelimiter "0"0"00007C } +\protected\def\Vert {\Udelimiter "0"0"002016 } +\protected\def\Vvdash {\Umathchar "3"0"0022AA } +\protected\def\wedge {\Umathchar "2"0"002227 } +\protected\def\wedgeeq {\Umathchar "3"0"002259 } +\protected\def\whitearrowupfrombar {\Umathchar "0"0"0021EB } +\protected\def\widehat {\Umathaccent"0"0"000302 } +\protected\def\widetilde {\Umathaccent"0"0"000303 } +\protected\def\wp {\Umathchar "0"0"002118 } +\protected\def\wr {\Umathchar "2"0"002240 } +\protected\def\Xi {\Umathchar "0"0"00039E } +\protected\def\xi {\Umathchar "0"0"0003BE } +\protected\def\yen {\Umathchar "0"0"0000A5 } +\protected\def\Zeta {\Umathchar "0"0"000396 } +\protected\def\zeta {\Umathchar "0"0"0003B6 } %D The following are suggested by Bruno. As I don't use plain and as the above are %D taken from text unicode greek I suppose his list is better: -\def\alpha {\Umathchar "0"0"01D6FC } -\def\beta {\Umathchar "0"0"01D6FD } -\def\chi {\Umathchar "0"0"01D712 } -\def\delta {\Umathchar "0"0"01D6FF } -\def\digamma {\Umathchar "0"0"0003DC } -\def\epsilon {\Umathchar "0"0"01D716 } -\def\eta {\Umathchar "0"0"01D702 } -\def\gamma {\Umathchar "0"0"01D6FE } -\def\iota {\Umathchar "0"0"01D704 } -\def\kappa {\Umathchar "0"0"01D705 } -\def\lambda {\Umathchar "0"0"01D706 } -\def\mu {\Umathchar "0"0"01D707 } -\def\nu {\Umathchar "0"0"01D708 } -\def\omega {\Umathchar "0"0"01D714 } -\def\omicron {\Umathchar "0"0"01D70A } -\def\phi {\Umathchar "0"0"01D719 } -\def\pi {\Umathchar "0"0"01D70B } -\def\psi {\Umathchar "0"0"01D713 } -\def\rho {\Umathchar "0"0"01D70C } -\def\sigma {\Umathchar "0"0"01D70E } -\def\tau {\Umathchar "0"0"01D70F } -\def\theta {\Umathchar "0"0"01D703 } -\def\upsilon {\Umathchar "0"0"01D710 } -\def\varepsilon {\Umathchar "0"0"01D700 } -\def\varkappa {\Umathchar "0"0"01D718 } -\def\varphi {\Umathchar "0"0"01D711 } -\def\varpi {\Umathchar "0"0"01D71B } -\def\varrho {\Umathchar "0"0"01D71A } -\def\varsigma {\Umathchar "0"0"01D70D } -\def\vartheta {\Umathchar "0"0"01D717 } -\def\xi {\Umathchar "0"0"01D709 } -\def\zeta {\Umathchar "0"0"01D701 } +\protected\def\alpha {\Umathchar "0"0"01D6FC } +\protected\def\beta {\Umathchar "0"0"01D6FD } +\protected\def\chi {\Umathchar "0"0"01D712 } +\protected\def\delta {\Umathchar "0"0"01D6FF } +\protected\def\digamma {\Umathchar "0"0"0003DC } +\protected\def\epsilon {\Umathchar "0"0"01D716 } +\protected\def\eta {\Umathchar "0"0"01D702 } +\protected\def\gamma {\Umathchar "0"0"01D6FE } +\protected\def\iota {\Umathchar "0"0"01D704 } +\protected\def\kappa {\Umathchar "0"0"01D705 } +\protected\def\lambda {\Umathchar "0"0"01D706 } +\protected\def\mu {\Umathchar "0"0"01D707 } +\protected\def\nu {\Umathchar "0"0"01D708 } +\protected\def\omega {\Umathchar "0"0"01D714 } +\protected\def\omicron {\Umathchar "0"0"01D70A } +\protected\def\phi {\Umathchar "0"0"01D719 } +\protected\def\pi {\Umathchar "0"0"01D70B } +\protected\def\psi {\Umathchar "0"0"01D713 } +\protected\def\rho {\Umathchar "0"0"01D70C } +\protected\def\sigma {\Umathchar "0"0"01D70E } +\protected\def\tau {\Umathchar "0"0"01D70F } +\protected\def\theta {\Umathchar "0"0"01D703 } +\protected\def\upsilon {\Umathchar "0"0"01D710 } +\protected\def\varepsilon {\Umathchar "0"0"01D700 } +\protected\def\varkappa {\Umathchar "0"0"01D718 } +\protected\def\varphi {\Umathchar "0"0"01D711 } +\protected\def\varpi {\Umathchar "0"0"01D71B } +\protected\def\varrho {\Umathchar "0"0"01D71A } +\protected\def\varsigma {\Umathchar "0"0"01D70D } +\protected\def\vartheta {\Umathchar "0"0"01D717 } +\protected\def\xi {\Umathchar "0"0"01D709 } +\protected\def\zeta {\Umathchar "0"0"01D701 } -\def\varTheta {\Umathchar "0"0"0003F4 } +\protected\def\varTheta {\Umathchar "0"0"0003F4 } % a few definitions: -\def\sqrt {\Uroot "0 "221A{}} -\def\root#1\of{\Uroot "0 "221A{#1}} +\protected\def\sqrt {\Uroot "0 "221A{}} +\protected\def\root#1\of{\Uroot "0 "221A{#1}} % \skewchar\teni='177 \skewchar\seveni='177 \skewchar\fivei='177 % \skewchar\tensy='60 \skewchar\sevensy='60 \skewchar\fivesy='60 @@ -2218,21 +2282,61 @@ % just use utf -\def\`#1{#1^^^^0300} -\def\'#1{#1^^^^0301} -\def\^#1{#1^^^^0302} -\def\~#1{#1^^^^0303} -\def\=#1{#1^^^^0304} -\def\u#1{#1^^^^0306} -\def\.#1{#1^^^^0307} -\def\"#1{#1^^^^0308} -\def\r#1{#1^^^^030a} % not in plain -\def\H#1{#1^^^^030b} -\def\v#1{#1^^^^030c} -\def\d#1{#1^^^^0323} -\def\c#1{#1^^^^0327} -\def\k#1{#1^^^^0328} % not in plain -\def\b#1{#1^^^^0331} +\protected\def\`#1{#1^^^^0300} +\protected\def\'#1{#1^^^^0301} +\protected\def\^#1{#1^^^^0302} +\protected\def\~#1{#1^^^^0303} +\protected\def\=#1{#1^^^^0304} +\protected\def\u#1{#1^^^^0306} +\protected\def\.#1{#1^^^^0307} +\protected\def\"#1{#1^^^^0308} +\protected\def\r#1{#1^^^^030a} % not in plain +\protected\def\H#1{#1^^^^030b} +\protected\def\v#1{#1^^^^030c} +\protected\def\d#1{#1^^^^0323} +\protected\def\c#1{#1^^^^0327} +\protected\def\k#1{#1^^^^0328} % not in plain +\protected\def\b#1{#1^^^^0331} + +\protected\def\*{\discretionary{\thinspace\the\textfont0\char"00D7}{}{}} + +\protected\def\t#1{% + %\quitvmode + \begingroup + \setbox0\hbox{#1}% + \setbox2\hbox\bgroup + \iffontchar\font"0361\relax + \char"0361\relax + \else + \iffontchar\font"2040\relax\else + \the\textfont0 + \fi + \char"2040 + \fi + \egroup + \dimen0\wd\ifdim\wd0>\wd2 0\else2\fi + \dimen2\dimexpr-\ht2+\ht0+.45ex\relax + \hbox to \dimen0\bgroup + \hbox to \dimen0{\hss\box0\hss}% + \hskip-\dimen0 + \hbox to \dimen0{\hss\raise\dimen2\box2\hss}% + \egroup + \endgroup} + +% Bruno's variant: +% +% \def\talign#1% +% {\leavevmode\vbox{% +% \baselineskip 0pt +% \lineskip -1ex +% \lineskiplimit 0pt +% \ialign{##\crcr#1\crcr}}} +% +% \def\t#1% +% {{\setbox0\hbox{% +% \iffontchar\font"0361\char"0361\else +% \iffontchar\font"2040\else\the\textfont0\fi\char"2040\fi}% +% \talign{\hidewidth\unhbox0\hidewidth\crcr#1}}} % for Bruno, when he tests this file with xetex: @@ -2240,9 +2344,9 @@ \catcode`@=11 - \def\sqrt{\Uradical "0 "221A } + \protected\def\sqrt{\Uradical "0 "221A } - \def\root#1\of + \protected\def\root#1\of {\setbox\rootbox\hbox\bgroup $\m@th\scriptscriptstyle{#1}$% \egroup diff --git a/tex/generic/context/luatex/luatex-mplib.lua b/tex/generic/context/luatex/luatex-mplib.lua index 976bb59f9..c610fca57 100644 --- a/tex/generic/context/luatex/luatex-mplib.lua +++ b/tex/generic/context/luatex/luatex-mplib.lua @@ -230,7 +230,14 @@ else return "" end - function metapost.load(name) + local modes = { + scaled = true, + decimal = true, + binary = true, + double = true, + } + + function metapost.load(name,mode) local mpd = { buffer = { }, verbatim = { } @@ -241,6 +248,7 @@ else make_text = function(...) return metapost.maketext (mpd,...) end, run_script = function(...) return metapost.runscript(mpd,...) end, extensions = 1, + math_mode = mode and modes[mode] and mode or "scaled", } local result if not mpx then @@ -283,9 +291,9 @@ else return true end - function metapost.process(mpx, data) + function metapost.process(format,data,mode) local converted, result = false, {} - mpx = metapost.load(mpx) + local mpx = metapost.load(format,mode) if mpx and data then local result = mpx:execute(data) if not result then @@ -300,6 +308,8 @@ else else metapost.report("mp error: unknown error, maybe no beginfig/endfig") end +-- mpx:finish() +-- mpx = nil else metapost.report("mp error: mem file not found") end @@ -452,15 +462,19 @@ else pdf_startfigure(fignum,llx,lly,urx,ury) pdf_literalcode("q") if objects then + local savedpath = nil + local savedhtap = nil 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 + local evenodd = not object.istext and object.postscript == "evenodd" pdf_literalcode("q") flushnormalpath(object.path,t,false) pdf_literalcode("W n") + pdf_literalcode(evenodd and "W* n" or "W n") elseif objecttype == "stop_clip" then pdf_literalcode("Q") miterlimit, linecap, linejoin, dashed = -1, -1, -1, false @@ -472,96 +486,147 @@ else pdf_textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) pdf_literalcode("Q") else - local cs = object.color - local cr = false - if cs and #cs > 0 then - cs, cr = metapost.colorconverter(cs) - pdf_literalcode(cs) - end - local ml = object.miterlimit - if ml and ml ~= miterlimit then - miterlimit = ml - pdf_literalcode("%f M",ml) - end - local lj = object.linejoin - if lj and lj ~= linejoin then - linejoin = lj - pdf_literalcode("%i j",lj) - end - local lc = object.linecap - if lc and lc ~= linecap then - linecap = lc - pdf_literalcode("%i J",lc) - end - local dl = object.dash - if dl then - local d = format("[%s] %i d",concat(dl.dashes or {}," "),dl.offset) - if d ~= dashed then - dashed = d - pdf_literalcode(dashed) + local evenodd, collect, both = false, false, false + local postscript = object.postscript + if not object.istext then + if postscript == "evenodd" then + evenodd = true + elseif postscript == "collect" then + collect = true + elseif postscript == "both" then + both = true + elseif postscript == "eoboth" then + evenodd = true + both = true end - elseif dashed then - pdf_literalcode("[] 0 d") - dashed = false - end - local path = object.path - local transformed, penwidth = false, 1 - local open = path and path[1].left_type and path[#path].right_type - local pen = object.pen - if pen then - if pen.type == 'elliptical' then - transformed, penwidth = pen_characteristics(object) -- boolean, value - pdf_literalcode("%f w",penwidth) - if objecttype == 'fill' then - objecttype = 'both' - end - else -- calculated by mplib itself - objecttype = 'fill' - end - end - if transformed then - pdf_literalcode("q") end - if path then - if transformed then - flushconcatpath(path,open) + if collect then + if not savedpath then + savedpath = { object.path or false } + savedhtap = { object.htap or false } else - flushnormalpath(path,open) + savedpath[#savedpath+1] = object.path or false + savedhtap[#savedhtap+1] = object.htap or false end - if objecttype == "fill" then - pdf_literalcode("h f") - elseif objecttype == "outline" then - pdf_literalcode((open and "S") or "h S") - elseif objecttype == "both" then - pdf_literalcode("h B") + else + local cs = object.color + local cr = false + if cs and #cs > 0 then + cs, cr = metapost.colorconverter(cs) + pdf_literalcode(cs) end - end - if transformed then - pdf_literalcode("Q") - end - local path = object.htap - if path then - if transformed then - pdf_literalcode("q") + local ml = object.miterlimit + if ml and ml ~= miterlimit then + miterlimit = ml + pdf_literalcode("%f M",ml) + end + local lj = object.linejoin + if lj and lj ~= linejoin then + linejoin = lj + pdf_literalcode("%i j",lj) + end + local lc = object.linecap + if lc and lc ~= linecap then + linecap = lc + pdf_literalcode("%i J",lc) + end + local dl = object.dash + if dl then + local d = format("[%s] %i d",concat(dl.dashes or {}," "),dl.offset) + if d ~= dashed then + dashed = d + pdf_literalcode(dashed) + end + elseif dashed then + pdf_literalcode("[] 0 d") + dashed = false + end + local path = object.path + local transformed, penwidth = false, 1 + local open = path and path[1].left_type and path[#path].right_type + local pen = object.pen + if pen then + if pen.type == 'elliptical' then + transformed, penwidth = pen_characteristics(object) -- boolean, value + pdf_literalcode("%f w",penwidth) + if objecttype == 'fill' then + objecttype = 'both' + end + else -- calculated by mplib itself + objecttype = 'fill' + end end if transformed then - flushconcatpath(path,open) - else - flushnormalpath(path,open) + pdf_literalcode("q") end - if objecttype == "fill" then - pdf_literalcode("h f") - elseif objecttype == "outline" then - pdf_literalcode((open and "S") or "h S") - elseif objecttype == "both" then - pdf_literalcode("h B") + if path then + if savedpath then + for i=1,#savedpath do + local path = savedpath[i] + if transformed then + flushconcatpath(path,open) + else + flushnormalpath(path,open) + end + end + savedpath = nil + end + if transformed then + flushconcatpath(path,open) + else + flushnormalpath(path,open) + end + if objecttype == "fill" then + pdf_literalcode("h f") + elseif objecttype == "outline" then + if both then + pdf_literalcode(evenodd and "h B*" or "h B") + else + pdf_literalcode(open and "S" or "h S") + end + elseif objecttype == "both" then + pdf_literalcode(evenodd and "h B*" or "h B") + end end if transformed then pdf_literalcode("Q") end - end - if cr then - pdf_literalcode(cr) + local path = object.htap + if path then + if transformed then + pdf_literalcode("q") + end + if savedhtap then + for i=1,#savedhtap do + local path = savedhtap[i] + if transformed then + flushconcatpath(path,open) + else + flushnormalpath(path,open) + end + end + savedhtap = nil + evenodd = true + end + if transformed then + flushconcatpath(path,open) + else + flushnormalpath(path,open) + end + if objecttype == "fill" then + pdf_literalcode("h f") + elseif objecttype == "outline" then + pdf_literalcode(evenodd and "h f*" or "h f") + elseif objecttype == "both" then + pdf_literalcode(evenodd and "h B*" or "h B") + end + if transformed then + pdf_literalcode("Q") + end + end + if cr then + pdf_literalcode(cr) + end end end end diff --git a/tex/generic/context/luatex/luatex-mplib.tex b/tex/generic/context/luatex/luatex-mplib.tex index f9de4b223..a99de37e4 100644 --- a/tex/generic/context/luatex/luatex-mplib.tex +++ b/tex/generic/context/luatex/luatex-mplib.tex @@ -29,6 +29,7 @@ %D \stoptyping \def\setmplibformat#1{\def\mplibformat{#1}} +\def\setmplibmode #1{\def\mplibmode {#1}} \def\setupmplibcatcodes {\catcode`\{=12 % could be optional .. not really needed @@ -39,7 +40,8 @@ \catcode`\_=12 \catcode`\%=12 \catcode`\&=12 - \catcode`\$=12 } + \catcode`\$=12 + \obeylines} \def\mplibcode {\bgroup @@ -48,11 +50,12 @@ \long\def\domplibcode#1\endmplibcode {\egroup - \directlua{metapost.process('\mplibformat',[[#1]])}} + \directlua{metapost.process('\mplibformat',[[#1]],'\mplibmode')}} %D We default to \type {plain} \METAPOST: \def\mplibformat{plain} +\def\mplibmode {scaled} %D We use a dedicated scratchbox: diff --git a/tex/generic/context/luatex/luatex-pdf.tex b/tex/generic/context/luatex/luatex-pdf.tex index 57004c6f8..b698285e3 100644 --- a/tex/generic/context/luatex/luatex-pdf.tex +++ b/tex/generic/context/luatex/luatex-pdf.tex @@ -135,20 +135,19 @@ \xdef\pdfinclusionerrorlevel {\pdfvariable inclusionerrorlevel} \xdef\pdfgentounicode {\pdfvariable gentounicode} \xdef\pdfpagebox {\pdfvariable pagebox} + \xdef\pdfmajorversion {\pdfvariable majorversion} \xdef\pdfminorversion {\pdfvariable minorversion} \xdef\pdfuniqueresname {\pdfvariable uniqueresname} - \ifnum\luatexversion>88 - \edef\pdfpkfixeddpi {\pdfvariable pkfixeddpi} - \edef\pdfignoreunknownimages {\pdfvariable ignoreunknownimages} - \fi + \xdef\pdfpkfixeddpi {\pdfvariable pkfixeddpi} + \xdef\pdfignoreunknownimages {\pdfvariable ignoreunknownimages} \xdef\pdfhorigin {\pdfvariable horigin} \xdef\pdfvorigin {\pdfvariable vorigin} \xdef\pdflinkmargin {\pdfvariable linkmargin} \xdef\pdfdestmargin {\pdfvariable destmargin} \xdef\pdfthreadmargin {\pdfvariable threadmargin} - % \xdef\pdfxformmargin {\pdfvariable xformmargin} + \xdef\pdfxformmargin {\pdfvariable xformmargin} \xdef\pdfpagesattr {\pdfvariable pagesattr} \xdef\pdfpageattr {\pdfvariable pageattr} @@ -178,6 +177,7 @@ \global\pdfinclusionerrorlevel 0 \global\pdfgentounicode 0 \global\pdfpagebox 0 + % \global\pdfmajorversion 1 \global\pdfminorversion 4 \global\pdfuniqueresname 0 @@ -187,11 +187,6 @@ \global\pdfdestmargin 0pt \global\pdfthreadmargin 0pt - % maybe - - % \global\chardef\pdftexversion 140 - % \global\def \pdftexrevision {16} - \fi \endgroup diff --git a/tex/generic/context/luatex/luatex-swiglib.lua b/tex/generic/context/luatex/luatex-swiglib.lua index 3108dd43f..cbb6798c3 100644 --- a/tex/generic/context/luatex/luatex-swiglib.lua +++ b/tex/generic/context/luatex/luatex-swiglib.lua @@ -16,16 +16,18 @@ function requireswiglib(required,version) if library then return library else - local name = string.gsub(required,"%.","/") .. libsuffix + local full = string.gsub(required,"%.","/" + local path = file.pathpart(full) + local name = file.nameonly(full) .. libsuffix local list = kpse.show_path("clua") for root in string.gmatch(list,pathsplit) do local full = false if type(version) == "string" and version ~= "" then - full = root .. "/" .. version .. "/" .. name + full = root .. "/" .. path .. "/" .. version .. "/" .. name full = lfs.isfile(full) and full end if not full then - full = root .. "/" .. name + full = root .. "/" .. path .. "/" .. name full = lfs.isfile(full) and full end if full then |