From 23b495f46b4d2e9264d54095f43774ef47d3a656 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Fri, 14 Jul 2017 21:22:10 +0200 Subject: 2017-07-14 19:41:00 --- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 2 +- tex/context/base/mkii/mult-en.mkii | 1 + tex/context/base/mkii/mult-pe.mkii | 1 + tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/cont-run.lua | 5 +- tex/context/base/mkiv/cont-run.mkiv | 1 - tex/context/base/mkiv/context.mkiv | 2 +- tex/context/base/mkiv/font-con.lua | 16 - tex/context/base/mkiv/font-ctx.lua | 8 +- tex/context/base/mkiv/font-dsp.lua | 35 +- tex/context/base/mkiv/font-ext.lua | 11 +- tex/context/base/mkiv/font-fbk.lua | 10 +- tex/context/base/mkiv/font-lib.mkvi | 2 +- tex/context/base/mkiv/font-mis.lua | 17 +- tex/context/base/mkiv/font-nod.lua | 34 +- tex/context/base/mkiv/font-otc.lua | 10 +- tex/context/base/mkiv/font-otd.lua | 2 +- tex/context/base/mkiv/font-otj.lua | 17 +- tex/context/base/mkiv/font-otl.lua | 2 +- tex/context/base/mkiv/font-oto.lua | 53 +- tex/context/base/mkiv/font-ots.lua | 1558 +++++++++++++------- tex/context/base/mkiv/font-oup.lua | 291 +++- tex/context/base/mkiv/font-shp.lua | 2 +- tex/context/base/mkiv/font-vf.lua | 206 --- tex/context/base/mkiv/font-vir.lua | 206 +++ tex/context/base/mkiv/hand-ini.mkiv | 1 + tex/context/base/mkiv/l-package.lua | 33 +- tex/context/base/mkiv/lpdf-col.lua | 34 +- tex/context/base/mkiv/lpdf-mis.lua | 5 +- tex/context/base/mkiv/lpdf-ren.lua | 7 +- tex/context/base/mkiv/lpdf-xmp.lua | 63 +- tex/context/base/mkiv/lxml-tex.lua | 23 +- tex/context/base/mkiv/m-fonts-plugins.mkiv | 23 +- tex/context/base/mkiv/meta-fnt.lua | 2 +- tex/context/base/mkiv/node-fin.lua | 115 +- tex/context/base/mkiv/node-fnt.lua | 193 +-- tex/context/base/mkiv/node-ini.mkiv | 1 + tex/context/base/mkiv/node-met.lua | 46 +- tex/context/base/mkiv/node-nut.lua | 79 +- tex/context/base/mkiv/node-ref.lua | 204 ++- tex/context/base/mkiv/node-syn.lua | 62 +- tex/context/base/mkiv/spac-chr.lua | 81 +- tex/context/base/mkiv/status-files.pdf | Bin 25688 -> 25746 bytes tex/context/base/mkiv/status-lua.pdf | Bin 425247 -> 426034 bytes tex/context/base/mkiv/strc-con.mkvi | 23 +- tex/context/base/mkiv/supp-box.lua | 1 + tex/context/base/mkiv/trac-vis.lua | 457 +++--- tex/context/base/mkiv/trac-vis.mkiv | 3 + tex/context/base/mkiv/typo-del.mkiv | 2 +- tex/context/base/mkiv/util-lib.lua | 7 + tex/context/base/mkiv/util-sbx.lua | 15 +- tex/context/base/mkiv/util-seq.lua | 2 +- tex/context/base/mkiv/util-str.lua | 39 + tex/context/base/mkiv/util-tab.lua | 4 + tex/context/interface/mkii/keys-en.xml | 1 + tex/context/interface/mkii/keys-pe.xml | 1 + tex/context/interface/mkiv/context-en.xml | 18 + tex/context/interface/mkiv/i-context.pdf | Bin 848046 -> 848182 bytes tex/context/interface/mkiv/i-description.xml | 6 + tex/context/interface/mkiv/i-enumeration.xml | 6 + tex/context/interface/mkiv/i-note.xml | 6 + tex/context/interface/mkiv/i-readme.pdf | Bin 60775 -> 60775 bytes tex/context/test/mkiv/pdf-ua.mkiv | 7 +- .../context/luatex/luatex-fonts-demo-vf-1.lua | 19 +- tex/generic/context/luatex/luatex-fonts-merged.lua | 1317 ++++++++++++----- 66 files changed, 3548 insertions(+), 1854 deletions(-) delete mode 100644 tex/context/base/mkiv/font-vf.lua create mode 100644 tex/context/base/mkiv/font-vir.lua (limited to 'tex') diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 4e4b70b10..e22f6c377 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2017.07.05 23:01} +\newcontextversion{2017.07.14 19:35} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index b80487453..9b7f9c968 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2017.07.05 23:01} +\edef\contextversion{2017.07.14 19:35} %D For those who want to use this: diff --git a/tex/context/base/mkii/mult-en.mkii b/tex/context/base/mkii/mult-en.mkii index a4838128b..e1f59ed93 100644 --- a/tex/context/base/mkii/mult-en.mkii +++ b/tex/context/base/mkii/mult-en.mkii @@ -687,6 +687,7 @@ \setinterfaceconstant{bottomoffset}{bottomoffset} \setinterfaceconstant{bottomspace}{bottomspace} \setinterfaceconstant{bottomstate}{bottomstate} +\setinterfaceconstant{break}{break} \setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{calculate} diff --git a/tex/context/base/mkii/mult-pe.mkii b/tex/context/base/mkii/mult-pe.mkii index 8b300ae73..46a715d2e 100644 --- a/tex/context/base/mkii/mult-pe.mkii +++ b/tex/context/base/mkii/mult-pe.mkii @@ -687,6 +687,7 @@ \setinterfaceconstant{bottomoffset}{آفست‌پایین} \setinterfaceconstant{bottomspace}{فضای‌پایین} \setinterfaceconstant{bottomstate}{وضعیت‌پایین} +\setinterfaceconstant{break}{break} \setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{میانگیر} \setinterfaceconstant{calculate}{محاسبه} diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index da2b24760..92d217d35 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2017.07.05 23:01} +\newcontextversion{2017.07.14 19:35} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/mkiv/cont-run.lua b/tex/context/base/mkiv/cont-run.lua index ee86c6a18..f18d06d66 100644 --- a/tex/context/base/mkiv/cont-run.lua +++ b/tex/context/base/mkiv/cont-run.lua @@ -181,7 +181,10 @@ local function processjob() local filename = environment.filename -- hm, not inputfilename ! if arguments.synctex then - directives.enable("system.synctex") + luatex.synctex.setup { + state = interfaces.variables.start, + method = interfaces.variables.max, + } end if not filename or filename == "" then diff --git a/tex/context/base/mkiv/cont-run.mkiv b/tex/context/base/mkiv/cont-run.mkiv index ef4992c69..8a6d49d7d 100644 --- a/tex/context/base/mkiv/cont-run.mkiv +++ b/tex/context/base/mkiv/cont-run.mkiv @@ -13,7 +13,6 @@ \writestatus{loading}{ConTeXt Core Macros / Runner} -\registerctxluafile{node-syn}{1.001} \registerctxluafile{cont-run}{1.001} % \enabletrackers[system.synctex.visualize] diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index c142bbc64..056ba3d32 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -41,7 +41,7 @@ %D up and the dependencies are more consistent. \edef\contextformat {\jobname} -\edef\contextversion{2017.07.05 23:01} +\edef\contextversion{2017.07.14 19:35} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/mkiv/font-con.lua b/tex/context/base/mkiv/font-con.lua index fd3b28612..1b986b6a6 100644 --- a/tex/context/base/mkiv/font-con.lua +++ b/tex/context/base/mkiv/font-con.lua @@ -555,22 +555,6 @@ function constructors.scale(tfmdata,specification) if changed then local c = changed[unicode] if c and c ~= unicode then --- while true do --- local cc = changed[c] --- if not cc then --- -- we're done, no (futher) chain --- break --- elseif cc == unicode then --- -- we probably have a bidi swap --- break --- elseif cc == c then --- -- we have a self reference, shouldn't happen --- c = nil --- break --- else --- c = cc --- end --- end if c then description = descriptions[c] or descriptions[unicode] or character character = characters[c] or character diff --git a/tex/context/base/mkiv/font-ctx.lua b/tex/context/base/mkiv/font-ctx.lua index 8f7eed4b9..eabd1d435 100644 --- a/tex/context/base/mkiv/font-ctx.lua +++ b/tex/context/base/mkiv/font-ctx.lua @@ -2543,7 +2543,7 @@ end -- a fontkern plug: -nodes.injections.installnewkern(nuts.pool.fontkern) +-- nodes.injections.installnewkern(nuts.pool.fontkern) do @@ -2599,7 +2599,7 @@ do local unsetvalue = attributes.unsetvalue - local traverse_id = nuts.traverse_id + local traverse_char = nuts.traverse_char local a_color = attributes.private('color') local a_colormodel = attributes.private('colormodel') @@ -2628,7 +2628,7 @@ do if head then head = tonut(head) local model = getattr(head,a_colormodel) or 1 - for glyph in traverse_id(glyph_code,head) do + for glyph in traverse_char(head) do local a = getprop(glyph,a_state) if a then local name = colornames[a] @@ -2679,7 +2679,7 @@ do function methods.nocolor(head,font,attr) - for n in traverse_id(glyph_code,head) do + for n in traverse_char(head) do if not font or getfont(n) == font then setattr(n,a_color,unsetvalue) end diff --git a/tex/context/base/mkiv/font-dsp.lua b/tex/context/base/mkiv/font-dsp.lua index 2e85c2438..2d3dce2fb 100644 --- a/tex/context/base/mkiv/font-dsp.lua +++ b/tex/context/base/mkiv/font-dsp.lua @@ -690,13 +690,13 @@ end local function readposition(f,format,mainoffset,getdelta) if format == 0 then - return + return false end -- a few happen often if format == 0x04 then local h = readshort(f) if h == 0 then - return + return true -- all zero else return { 0, 0, h, 0 } end @@ -705,7 +705,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 -- all zero else return { x, 0, h, 0 } end @@ -724,7 +724,7 @@ local function readposition(f,format,mainoffset,getdelta) skipshort(f,1) end if h == 0 then - return + return true -- all zero else return { 0, 0, h, 0 } end @@ -779,7 +779,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 -- all zero else return { x, y, h, v } end @@ -1521,7 +1521,7 @@ 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 } -- needs checking + hash[other] = { first, second or nil } -- needs checking else hash[other] = nil end @@ -1557,7 +1557,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 -- upto the next lookup for this combination end @@ -1591,18 +1591,27 @@ 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 = entry ~= 0 and (tableoffset + entry) or false, + -- exit = exit ~= 0 and (tableoffset + exit ) or nil, + entry ~= 0 and (tableoffset + entry) or false, + exit ~= 0 and (tableoffset + exit ) or nil, } end + -- slot 1 will become hash after loading an dit must be unique because we + -- pack the tables (packed we turn the cc-* into a zero) + 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] - -- slot 1 will become hash after loading (must be unique per lookup when packed) records[i] = { - 1, - readanchor(f,r.entry,getdelta) or nil, - readanchor(f,r.exit, getdelta) or nil, + -- 1, + cc, + -- readanchor(f,r.entry,getdelta) or false, + -- readanchor(f,r.exit, getdelta) or nil, + readanchor(f,r[1],getdelta) or false, + readanchor(f,r[2],getdelta) or nil, } end for index, newindex in next, coverage do diff --git a/tex/context/base/mkiv/font-ext.lua b/tex/context/base/mkiv/font-ext.lua index cf77ac4eb..d541521fb 100644 --- a/tex/context/base/mkiv/font-ext.lua +++ b/tex/context/base/mkiv/font-ext.lua @@ -749,15 +749,15 @@ registerafmfeature(dimensions_specification) -- -- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression -- --- local gray = { "pdf", "/Tr1 gs .75 g" } --- local black = { "pdf", "/Tr0 gs 0 g" } +-- local gray = { "pdf", "origin", "/Tr1 gs .75 g" } +-- local black = { "pdf", "origin", "/Tr0 gs 0 g" } -- sort of obsolete as we now have \showglyphs local push = { "push" } local pop = { "pop" } -local gray = { "pdf", ".75 g" } -local black = { "pdf", "0 g" } +local gray = { "pdf", "origin", ".75 g" } +local black = { "pdf", "origin", "0 g" } local downcache = { } -- handy for huge cjk fonts local rulecache = { } -- handy for huge cjk fonts @@ -1486,7 +1486,8 @@ do report_reorder("stop swapping lookups, %i lookups swapped",swapped) report_reorder() end - tfmdata.resources.sequences = sequences +-- tfmdata.resources.sequences = sequences + tfmdata.shared.reorderedsequences = sequences end end end diff --git a/tex/context/base/mkiv/font-fbk.lua b/tex/context/base/mkiv/font-fbk.lua index 8a5c1ebb7..5008887b8 100644 --- a/tex/context/base/mkiv/font-fbk.lua +++ b/tex/context/base/mkiv/font-fbk.lua @@ -300,11 +300,11 @@ end -- vf builder --- { "pdf", "q " .. s .. " 0 0 " .. s .. " 0 0 cm" }, --- { "pdf", "q 1 0 0 1 " .. -w .. " " .. -h .. " cm" }, --- { "pdf", "/Fm\XX\space Do" }, --- { "pdf", "Q" }, --- { "pdf", "Q" }, +-- { "pdf", "origin", "q " .. s .. " 0 0 " .. s .. " 0 0 cm" }, +-- { "pdf", "origin", "q 1 0 0 1 " .. -w .. " " .. -h .. " cm" }, +-- { "pdf", "origin", "/Fm\XX\space Do" }, +-- { "pdf", "origin", "Q" }, +-- { "pdf", "origin", "Q" }, -- new and experimental diff --git a/tex/context/base/mkiv/font-lib.mkvi b/tex/context/base/mkiv/font-lib.mkvi index 4ba7a7de4..4dd15812f 100644 --- a/tex/context/base/mkiv/font-lib.mkvi +++ b/tex/context/base/mkiv/font-lib.mkvi @@ -81,7 +81,7 @@ \registerctxluafile{font-lua}{1.001} -\registerctxluafile{font-vf} {1.001} +\registerctxluafile{font-vir}{1.001} \registerctxluafile{font-enh}{1.001} \registerctxluafile{good-ini}{1.001} diff --git a/tex/context/base/mkiv/font-mis.lua b/tex/context/base/mkiv/font-mis.lua index 024e32831..3325264fc 100644 --- a/tex/context/base/mkiv/font-mis.lua +++ b/tex/context/base/mkiv/font-mis.lua @@ -21,7 +21,7 @@ local readers = otf.readers if readers then - otf.version = otf.version or 3.031 + otf.version = otf.version or 3.032 otf.cache = otf.cache or containers.define("fonts", "otl", otf.version, true) function fonts.helpers.getfeatures(name,save) @@ -33,10 +33,17 @@ if readers then if data then readers.unpack(data) else - data = readers.loadfont(filename) - if data and save then - containers.write(otf.cache,cleanname,data) - end + data = readers.loadfont(filename) -- we can do a more minimal load + -- if data and save then + -- -- keep this in sync with font-otl + -- readers.compact(data) + -- readers.rehash(data,"unicodes") + -- readers.addunicodetable(data) + -- readers.extend(data) + -- readers.pack(data) + -- -- till here + -- containers.write(otf.cache,cleanname,data) + -- end end return data and data.resources and data.resources.features end diff --git a/tex/context/base/mkiv/font-nod.lua b/tex/context/base/mkiv/font-nod.lua index b629a51d1..c70763e6b 100644 --- a/tex/context/base/mkiv/font-nod.lua +++ b/tex/context/base/mkiv/font-nod.lua @@ -78,7 +78,7 @@ local copy_node_list = nuts.copy_list local hpack_node_list = nuts.hpack local flush_node_list = nuts.flush_list local traverse_nodes = nuts.traverse -local traverse_id = nuts.traverse_id +----- traverse_id = nuts.traverse_id local protect_glyphs = nuts.protect_glyphs local nodepool = nuts.pool @@ -135,22 +135,22 @@ function char_tracers.collect(head,list,tag,n) l[#l+1] = { c, f } elseif id == disc_code then -- skip --- local pre, post, replace = getdisc(head) --- if replace then --- for n in traverse_id(glyph_code,replace) do --- l[#l+1] = { c, f } --- end --- end --- if pre then --- for n in traverse_id(glyph_code,pre) do --- l[#l+1] = { c, f } --- end --- end --- if post then --- for n in traverse_id(glyph_code,post) do --- l[#l+1] = { c, f } --- end --- end + -- local pre, post, replace = getdisc(head) + -- if replace then + -- for n in traverse_id(glyph_code,replace) do + -- l[#l+1] = { c, f } + -- end + -- end + -- if pre then + -- for n in traverse_id(glyph_code,pre) do + -- l[#l+1] = { c, f } + -- end + -- end + -- if post then + -- for n in traverse_id(glyph_code,post) do + -- l[#l+1] = { c, f } + -- end + -- end else ok = false end diff --git a/tex/context/base/mkiv/font-otc.lua b/tex/context/base/mkiv/font-otc.lua index 0af588b74..b7c296809 100644 --- a/tex/context/base/mkiv/font-otc.lua +++ b/tex/context/base/mkiv/font-otc.lua @@ -412,11 +412,9 @@ local function addfeature(data,feature,specifications) local rules = list.rules local coverage = { } if rules then - local rulehash = { } - local rulesize = 0 - local sequence = { } - local nofsequences = 0 - local lookuptype = types[featuretype] + local rulehash = { } + local rulesize = 0 + local lookuptype = types[featuretype] for nofrules=1,#rules do local rule = rules[nofrules] local current = rule.current @@ -504,8 +502,10 @@ local function addfeature(data,feature,specifications) coverage[unic] = rulehash -- can now be done cleaner i think end end + sequence.n = nofsequences end end + rulehash.n = rulesize end return coverage end diff --git a/tex/context/base/mkiv/font-otd.lua b/tex/context/base/mkiv/font-otd.lua index c45278826..2633c6cae 100644 --- a/tex/context/base/mkiv/font-otd.lua +++ b/tex/context/base/mkiv/font-otd.lua @@ -268,7 +268,7 @@ function otf.dataset(tfmdata,font,attr) -- attr only when explicit (as in specia -- indexed but we can also add specific data by key in: } rl[attr] = ra - local sequences = tfmdata.resources.sequences + local sequences = tfmdata.shared.reorderedsequences or tfmdata.resources.sequences if sequences then local autoscript = (s_enabled and s_enabled.autoscript ) or (a_enabled and a_enabled.autoscript ) local autolanguage = (s_enabled and s_enabled.autolanguage) or (a_enabled and a_enabled.autolanguage) diff --git a/tex/context/base/mkiv/font-otj.lua b/tex/context/base/mkiv/font-otj.lua index 6275f81b8..a6e60a3f5 100644 --- a/tex/context/base/mkiv/font-otj.lua +++ b/tex/context/base/mkiv/font-otj.lua @@ -515,7 +515,7 @@ 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 @@ -593,7 +593,7 @@ local function inject_kerns_only(head,where) local prev = nil local next = nil local prevdisc = nil - local prevglyph = nil + -- local prevglyph = nil local pre = nil -- saves a lookup local post = nil -- saves a lookup local replace = nil -- saves a lookup @@ -655,11 +655,11 @@ local function inject_kerns_only(head,where) end end prevdisc = nil - prevglyph = current + -- prevglyph = current elseif char == false then -- other font prevdisc = nil - prevglyph = current + -- prevglyph = current elseif id == disc_code then pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) local done = false @@ -717,10 +717,10 @@ local function inject_kerns_only(head,where) if done then setdisc(current,pre,post,replace) end - prevglyph = nil + -- prevglyph = nil prevdisc = current else - prevglyph = nil + -- prevglyph = nil prevdisc = nil end prev = current @@ -966,7 +966,7 @@ local function inject_pairs_only(head,where) if keepregisteredcounts then keepregisteredcounts = false else - nofregisteredkerns = 0 + nofregisteredpairs = 0 end if trace_injections then show_result(head) @@ -1548,7 +1548,8 @@ local function injectspaces(head) factor = getthreshold(font) end - for n in traverse_id(glue_code,tonut(head)) do + -- for n in traverse_id(glue_code,tonut(head)) do + for n in traverse_char(tonut(head)) do local prev, next = getspaceboth(n) local prevchar = prev and ischar(prev) local nextchar = next and ischar(next) diff --git a/tex/context/base/mkiv/font-otl.lua b/tex/context/base/mkiv/font-otl.lua index 2661ac5c1..1cdbb450a 100644 --- a/tex/context/base/mkiv/font-otl.lua +++ b/tex/context/base/mkiv/font-otl.lua @@ -52,7 +52,7 @@ local report_otf = logs.reporter("fonts","otf loading") local fonts = fonts local otf = fonts.handlers.otf -otf.version = 3.031 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version = 3.032 -- beware: also sync font-mis.lua and in mtx-fonts 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) diff --git a/tex/context/base/mkiv/font-oto.lua b/tex/context/base/mkiv/font-oto.lua index 22bb1d74e..0009d7f5f 100644 --- a/tex/context/base/mkiv/font-oto.lua +++ b/tex/context/base/mkiv/font-oto.lua @@ -368,9 +368,13 @@ 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 + -- kern[2] is true (all zero) or a table + 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 + -- all zero + elseif kern[1] ~= 0 or kern[2] ~= 0 or kern[4] ~= 0 then -- a complex pair not suitable for basemode else kern = kern[3] @@ -400,6 +404,43 @@ local function initializehashes(tfmdata) -- already done end +local function checkmathreplacements(tfmdata,fullname) + 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 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 -- value then local starttime = trace_preparing and os.clock() @@ -416,6 +457,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 @@ -438,12 +481,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 @@ -452,6 +497,10 @@ local function featuresinitializer(tfmdata,value) end end -- + if substitutionsdone then + checkmathreplacements(tfmdata,fullname) + end + -- registerbasehash(tfmdata) end if trace_preparing then diff --git a/tex/context/base/mkiv/font-ots.lua b/tex/context/base/mkiv/font-ots.lua index a5a039525..26659721f 100644 --- a/tex/context/base/mkiv/font-ots.lua +++ b/tex/context/base/mkiv/font-ots.lua @@ -148,7 +148,6 @@ local trace_compruns = false registertracker("otf.compruns", function local trace_testruns = false registertracker("otf.testruns", function(v) trace_testruns = v end) local forcediscretionaries = false -local optimizekerns = true directives.register("otf.forcediscretionaries",function(v) forcediscretionaries = v @@ -207,7 +206,7 @@ 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 +----- traverse_id = nuts.traverse_id local set_components = nuts.set_components local take_components = nuts.take_components local count_components = nuts.count_components @@ -271,6 +270,8 @@ local factor = 0 local threshold = 0 local checkmarks = false +local discs = false + local sweepnode = nil local sweephead = { } -- we don't nil entries but false them (no collection and such) @@ -305,47 +306,56 @@ local function logwarning(...) report_direct(...) end -local f_unicode = formatters["U+%X"] -- was ["%U"] -local f_uniname = formatters["U+%X (%s)"] -- was ["%U (%s)"] -local f_unilist = formatters["% t (% t)"] +local gref do -local function gref(n) -- currently the same as in font-otb - 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 -- later we will start at 2 - local di = descriptions[ni] - num[i] = f_unicode(ni) - nam[i] = di and di.name or "-" + local f_unicode = formatters["U+%X"] -- was ["%U"] + local f_uniname = formatters["U+%X (%s)"] -- was ["%U (%s)"] + local f_unilist = formatters["% t (% t)"] + + gref = function(n) -- currently the same as in font-otb + 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 -- later we will start at 2 + local di = descriptions[ni] + num[i] = f_unicode(ni) + nam[i] = di and di.name or "-" + end end + return f_unilist(num,nam) + else + return "" end - return f_unilist(num,nam) - else - return "" 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) @@ -689,7 +699,7 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative) setchar(start,choice) else if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment) + logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment) end end return head, start, true @@ -850,36 +860,32 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje if nextchar then local krn = kerns[nextchar] if not krn and marks[nextchar] then + -- hm, needs checking i guess 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 - -- this permits a mixed table, but we could also decide to optimize this - -- in the loader and use format 'kern' - 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) - if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) - end - return head, start, true - end - end - if a and #a > 0 then + if a == true then + -- zero + elseif a then -- #a > 0 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") end end - if b and #b > 0 then + if b == true then + -- zero + start = snext -- cf spec + elseif b then -- #b > 0 local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,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") end + start = snext -- cf spec end return head, start, true elseif krn ~= 0 then @@ -1073,8 +1079,7 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st if not nextchar then break elseif marks[nextchar] then - -- should not happen (maybe warning) - nxt = getnext(nxt) + nxt = getnext(nxt) else local exit = exitanchors[3] if exit then @@ -1458,30 +1463,25 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm break elseif step.format == "pair" then local a, b = krn[1], krn[2] - if optimizekerns then - -- this permits a mixed table, but we could also decide to optimize this - -- in the loader and use format 'kern' - 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") - if trace_kerns then - logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) - end - return head, start, true - end - end - if a and #a > 0 then - local startchar = getchar(start) + if a then + -- zero + elseif a then local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,"injections") -- currentlookups flags? if trace_kerns then + 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 end - if b and #b > 0 then - local startchar = getchar(start) + if b == true then + -- zero + start = snext -- cf spec + elseif b then -- #b > 0 local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],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 -- cf spec end return head, start, true elseif krn ~= 0 then @@ -1836,20 +1836,21 @@ local noflags = { false, false, false, false } local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) 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 -- current match if chainlookups then - local nofchainlookups = #chainlookups + -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and -- #lookups can be less than #current + if size == 1 then + -- if nofchainlookups > size then -- -- bad rules -- end + local chainlookup = chainlookups[1] for j=1,#chainlookup do local chainstep = chainlookup[j] @@ -1865,7 +1866,9 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) end end + else + -- See LookupType 5: Contextual Substitution Subtable. Now it becomes messy. The -- easiest case is where #current maps on #lookups i.e. one-to-one. But what if -- we have a ligature. Cf the spec we then need to advance one character but we @@ -1876,10 +1879,27 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) -- -- Even worse are these family emoji shapes as they can have multiple lookups -- per slot (probably only for gpos). + + -- It's very unlikely that we will have skip classes here but still ... we seldom + -- enter this branch anyway. + + local skipmark + local skipligature + local skipbase + local markclass + if skipped then + local flags = sequence.flags or noflags + skipmark = flags[1] + skipligature = flags[2] + skipbase = flags[3] + markclass = sequence.markclass + end + local i = 1 local laststart = start + local nofchainlookups = #chainlookups -- useful? while start do - if skipped then + if skipped then -- hm, so we know we skip some while start do local char, id = ischar(start,currentfont) if char then @@ -1932,6 +1952,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) if not start then start = laststart end + end else -- todo: needs checking for holes in the replacements @@ -2762,11 +2783,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local checkdisc = sweeptype and getprev(head) local flags = sequence.flags or noflags local done = false + local markclass = sequence.markclass local skipmark = flags[1] local skipligature = flags[2] local skipbase = flags[3] - local markclass = sequence.markclass - local skipsome = skipmark ~= false or skipligature ~= false or skipbase ~= false or markclass + -- local skipsome = skipmark ~= false or skipligature ~= false or skipbase ~= false or markclass + local skipsome = flags[5] local skipped = false local startprev, startnext = getboth(start) @@ -2778,7 +2800,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local ck = contexts[k] local seq = ck[3] local s = #seq - local size = 1 -- f..l = mid string if s == 1 then -- this seldom happens as it makes no sense (bril, ebgaramond, husayni, minion) @@ -2793,12 +2814,10 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local l = ck[5] -- current match -- seq[f][ischar(current,currentfont)] is not nil - size = l - f + 1 - if size > 1 then + if l > f then -- before/current/after | before/current | current/after local discfound -- = nil local n = f + 1 - -- last = getnext(last) -- the second in current (first already matched) last = startnext -- the second in current (first already matched) while n <= l do if not last and (sweeptype == "post" or sweeptype == "replace") then @@ -2923,13 +2942,10 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end -- before if match and f > 1 then - -- local prev = getprev(start) - -- if prev then if startprev then local prev = startprev if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then - prev = getprev(sweepnode) - -- sweeptype = nil + prev = getprev(sweepnode) end if prev then local discfound -- = nil @@ -3074,8 +3090,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) 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) - -- sweeptype = nil + current = getnext(sweepnode) end if current then local discfound -- = nil @@ -3232,220 +3247,706 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) return head, start, done end ------------------------------- - -handlers.gsub_context = handle_contextchain -handlers.gsub_contextchain = handle_contextchain -handlers.gsub_reversecontextchain = handle_contextchain -handlers.gpos_contextchain = handle_contextchain -handlers.gpos_context = handle_contextchain - --- this needs testing - -local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode) - 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) -end - -chainprocs.gsub_context = chained_contextchain -chainprocs.gsub_contextchain = chained_contextchain -chainprocs.gsub_reversecontextchain = chained_contextchain -chainprocs.gpos_contextchain = chained_contextchain -chainprocs.gpos_context = chained_contextchain - --- experiment (needs no handler in font-otc so not now): --- --- function otf.registerchainproc(name,f) --- -- chainprocs[name] = f --- chainprocs[name] = function(head,start,stop,dataset,sequence,currentlookup,rlmode) --- local done = currentlookup.nofsteps > 0 --- if not done then --- reportzerosteps(dataset,sequence) --- else --- head, start, done = f(head,start,stop,dataset,sequence,currentlookup,rlmode) --- if not head or not start then --- reportbadsteps(dataset,sequence) --- end --- end --- return head, start, done --- end --- end - -local missing = setmetatableindex("table") - -local function logprocess(...) - if trace_steps then - registermessage(...) - 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 = { } -- we only resolve a font,script,language pair once - --- todo: pass all these 'locals' in a table - -local sequencelists = setmetatableindex(function(t,font) - local sequences = fontdata[font].resources.sequences - if not sequences or not next(sequences) then - sequences = false - end - t[font] = sequences - return sequences -end) - --- fonts.hashes.sequences = sequencelists - -do -- overcome local limit - - local autofeatures = fonts.analyzers.features - local featuretypes = otf.tables.featuretypes - local defaultscript = otf.features.checkeddefaultscript - local defaultlanguage = otf.features.checkeddefaultlanguage - - local wildcard = "*" - local default = "dflt" +-- only a bit faster but probably also a bit cleaner ... so ... - local function initialize(sequence,script,language,enabled,autoscript,autolanguage) - local features = sequence.features - if features then - local order = sequence.order - if order then - local featuretype = featuretypes[sequence.type or "unknown"] - for i=1,#order do - local kind = order[i] - local valid = enabled[kind] - if valid then - local scripts = features[kind] - local languages = scripts and ( - scripts[script] or - scripts[wildcard] or - (autoscript and defaultscript(featuretype,autoscript,scripts)) - ) - local enabled = languages and ( - languages[language] or - languages[wildcard] or - (autolanguage and defaultlanguage(featuretype,autolanguage,languages)) - ) - if enabled then - return { valid, autofeatures[kind] or false, sequence, kind } - end - end - end - else - -- can't happen - end +local function optimized_handle_contextchain(head,start,dataset,sequence,contexts,rlmode) + -- optimizing for rlmode gains nothing + local sweepnode = sweepnode + local sweeptype = sweeptype + local postreplace + local prereplace + local checkdisc + local diskseen -- = false + if sweeptype then + if sweeptype == "replace" then + postreplace = true + prereplace = true + else + postreplace = sweeptype == "post" + prereplace = sweeptype == "pre" end - return false + checkdisc = getprev(head) end - - function otf.dataset(tfmdata,font) -- generic variant, overloaded in context - local shared = tfmdata.shared - local properties = tfmdata.properties - local language = properties.language or "dflt" - local script = properties.script or "dflt" - local enabled = shared.features - local autoscript = enabled and enabled.autoscript - local autolanguage = enabled and enabled.autolanguage - local res = resolved[font] - if not res then - res = { } - resolved[font] = res - end - local rs = res[script] - if not rs then - rs = { } - res[script] = rs - end - local rl = rs[language] - if not rl then - rl = { - -- indexed but we can also add specific data by key - } - rs[language] = rl - local sequences = tfmdata.resources.sequences - if sequences then - for s=1,#sequences do - local v = enabled and initialize(sequences[s],script,language,enabled,autoscript,autolanguage) - if v then - rl[#rl+1] = v - end - end - end - end - return rl + local currentfont = currentfont + local flags = sequence.flags or noflags + local skipsome = flags[5] + local skipmark + local skipligature + local skipbase + local markclass + if skipsome then + skipmark = flags[1] + skipligature = flags[2] + skipbase = flags[3] + markclass = sequence.markclass end + local skipped -- = false + local startprev, + startnext = getboth(start) + local done -- = false -end - --- Functions like kernrun, comprun etc evolved over time and in the end look rather --- complex. It's a bit of a compromis between extensive copying and creating subruns. --- The logic has been improved a lot by Kai and Ivo who use complex fonts which --- really helped to identify border cases on the one hand and get insight in the diverse --- ways fonts implement features (not always that consistent and efficient). At the same --- time I tried to keep the code relatively efficient so that the overhead in runtime --- stays acceptable. - -local function report_disc(what,n) - report_run("%s: %s > %s",what,n,languages.serializediscretionary(n)) -end - -local function kernrun(disc,k_run,font,attr,...) - -- - -- we catch - -- - if trace_kernruns then - report_disc("kern",disc) - end - -- - local prev, next = getboth(disc) - -- - local nextstart = next - local done = false - -- - local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) - -- - local prevmarks = prev - -- - -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp - -- has happened but then it should be in the disc so basically this test indicates an error) - -- - while prevmarks do - local char = ischar(prevmarks,font) - if char and marks[char] then - prevmarks = getprev(prevmarks) + for k=1,contexts.n do -- or #contexts do + local current = start + local last = start + local ck = contexts[k] + local seq = ck[3] + -- local s = #seq + local s = seq.n -- or #seq + -- f..l = mid string + if s == 1 then + -- this seldom happens as it makes no sense (bril, ebgaramond, husayni, minion) + local char = ischar(current,currentfont) + if char and not seq[1][char] then + goto next + end else - break - end - end - -- - if prev and not ischar(prev,font) then -- and (pre or replace) - prev = false - end - if next and not ischar(next,font) then -- and (post or replace) - next = false - end - -- - -- we need to get rid of this nest mess some day .. has to be done otherwise - -- - if pre then - if k_run(pre,"injections",nil,font,attr,...) then - done = true - end + -- maybe we need a better space check (maybe check for glue or category or combination) + local f = ck[4] + local l = ck[5] + -- current match + -- seq[f][ischar(current,currentfont)] is not nil + if l > f then + -- before/current/after | before/current | current/after + local discfound -- = nil + local n = f + 1 + last = startnext -- the second in current (first already matched) + while n <= l do + if not last and postreplace then + last = getnext(sweepnode) + sweeptype = nil + end + if last then + local char, id = ischar(last,currentfont) + if char then + if skipsome then + local class = classes[char] + -- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + if class == skipmark or (markclass and class == "mark" and not markclass[char]) or class == skipligature or class == skipbase 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 + goto next + end + else + goto next + end + break + end + else + if 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 + goto next + end + else + goto next + end + break + end + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + end + else + goto next + end + break + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[last]) 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 + if n > l then + break + end + pre = getnext(pre) + else + notmatchpre[last] = true + break + end + end + if n <= l then + notmatchpre[last] = true + end + else + notmatchpre[last] = true + end + if replace then + -- so far we never entered this branch + 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 + end + break + end + end + -- why here again + if notmatchpre[last] then + goto next + end + end + -- maybe only if match + last = getnext(last) + else + goto next + end + else + goto next + end + end + end + -- before + 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 -- = nil + local n = f - 1 + while n >= 1 do + if prev then + local char, id = ischar(prev,currentfont) + if char then + if skipsome then + local class = classes[char] + -- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + if class == skipmark or (markclass and class == "mark" and not markclass[char]) or class == skipligature or class == skipbase 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 + goto next + end + else + goto next + end + break + end + else + if 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 + goto next + end + else + goto next + end + break + end + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next + end + else + goto next + end + break + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[prev]) then + -- the special case: f i where i becomes dottless i .. + 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 + if n < 1 then + break + end + posttail = getprev(posttail) + end + else + notmatchpost[prev] = true + break + end + end + if n >= 1 then + notmatchpost[prev] = true + end + else + notmatchpost[prev] = true + end + if replace then + -- we seldom enter this branch (e.g. on brill efficient) + while replacetail do + if seq[n][getchar(replacetail)] then + n = n - 1 + if replacetail == replace then + break + else + if n < 1 then + break + end + replacetail = getprev(replacetail) + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + goto next + end + break + end + end + end + end + prev = getprev(prev) + elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then + n = n - 1 + prev = getprev(prev) + else + goto next + end + else + goto next + end + end + else + goto next + end + else + goto next + end + end + -- after + 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 -- = nil + local n = l + 1 + while n <= s do + if current then + local char, id = ischar(current,currentfont) + if char then + if skipsome then + local class = classes[char] + -- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + if class == skipmark or (markclass and class == "mark" and not markclass[char]) or class == skipligature or class == skipbase then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,class) + end + current = getnext(current) -- was absent + elseif seq[n][char] then + if n < s then -- new test + current = getnext(current) -- was absent + end + n = n + 1 + else + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + end + else + goto next + end + break + end + else + if seq[n][char] then + if n < s then -- new test + current = getnext(current) -- was absent + end + n = n + 1 + else + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + end + else + goto next + end + break + end + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + end + else + goto next + end + break + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[current]) 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 + if n > s then + break + end + pre = getnext(pre) + else + notmatchpre[current] = true + break + end + end + if n <= s then + notmatchpre[current] = true + end + else + notmatchpre[current] = true + end + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + if n > s then + break + end + replace = getnext(replace) + else + notmatchreplace[current] = true + -- different than others, needs checking if "not" is okay + if not notmatchpre[current] then + goto next + end + break + end + end + else + -- skip 'm + end + current = getnext(current) + elseif id == glue_code and seq[n][32] and isspace(current,threshold,id) then + n = n + 1 + current = getnext(current) + else + goto next + end + else + goto next + end + end + else + goto next + end + end + end + if trace_contexts then + chaintrac(head,start,dataset,sequence,rlmode,ck,skipped,true) + 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 + -- else + -- next context + end + ::next:: + -- if trace_chains then + -- chaintrac(head,start,dataset,sequence,rlmode,ck,skipped,match) + -- end + end + if diskseen then + notmatchpre = { } + notmatchpost = { } + notmatchreplace = { } + end + return head, start, done +end + +directives.register("otf.optimizechains",function(v) + if v then + report_chain() + report_chain("using experimental optimized code") + report_chain() + end + local handle = v and optimized_handle_contextchain or handle_contextchain + handlers.gsub_context = handle + handlers.gsub_contextchain = handle + handlers.gsub_reversecontextchain = handle + handlers.gpos_contextchain = handle + handlers.gpos_context = handle +end) + +------------------------------ + +handlers.gsub_context = handle_contextchain +handlers.gsub_contextchain = handle_contextchain +handlers.gsub_reversecontextchain = handle_contextchain +handlers.gpos_contextchain = handle_contextchain +handlers.gpos_context = handle_contextchain + +-- this needs testing + +local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode) + 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) +end + +chainprocs.gsub_context = chained_contextchain +chainprocs.gsub_contextchain = chained_contextchain +chainprocs.gsub_reversecontextchain = chained_contextchain +chainprocs.gpos_contextchain = chained_contextchain +chainprocs.gpos_context = chained_contextchain + +-- experiment (needs no handler in font-otc so not now): +-- +-- function otf.registerchainproc(name,f) +-- -- chainprocs[name] = f +-- chainprocs[name] = function(head,start,stop,dataset,sequence,currentlookup,rlmode) +-- local done = currentlookup.nofsteps > 0 +-- if not done then +-- reportzerosteps(dataset,sequence) +-- else +-- head, start, done = f(head,start,stop,dataset,sequence,currentlookup,rlmode) +-- if not head or not start then +-- reportbadsteps(dataset,sequence) +-- end +-- end +-- return head, start, done +-- end +-- end + +local missing = setmetatableindex("table") +local logwarning = report_process +local resolved = { } -- we only resolve a font,script,language pair once + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_process(...) +end + +-- todo: pass all these 'locals' in a table + +local sequencelists = setmetatableindex(function(t,font) + local sequences = fontdata[font].resources.sequences + if not sequences or not next(sequences) then + sequences = false + end + t[font] = sequences + return sequences +end) + +-- fonts.hashes.sequences = sequencelists + +do -- overcome local limit + + local autofeatures = fonts.analyzers.features + local featuretypes = otf.tables.featuretypes + local defaultscript = otf.features.checkeddefaultscript + local defaultlanguage = otf.features.checkeddefaultlanguage + + local wildcard = "*" + local default = "dflt" + + local function initialize(sequence,script,language,enabled,autoscript,autolanguage) + local features = sequence.features + if features then + local order = sequence.order + if order then + local featuretype = featuretypes[sequence.type or "unknown"] + for i=1,#order do + local kind = order[i] + local valid = enabled[kind] + if valid then + local scripts = features[kind] + local languages = scripts and ( + scripts[script] or + scripts[wildcard] or + (autoscript and defaultscript(featuretype,autoscript,scripts)) + ) + local enabled = languages and ( + languages[language] or + languages[wildcard] or + (autolanguage and defaultlanguage(featuretype,autolanguage,languages)) + ) + if enabled then + return { valid, autofeatures[kind] or false, sequence, kind } + end + end + end + else + -- can't happen + end + end + return false + end + + function otf.dataset(tfmdata,font) -- generic variant, overloaded in context + local shared = tfmdata.shared + local properties = tfmdata.properties + local language = properties.language or "dflt" + local script = properties.script or "dflt" + local enabled = shared.features + local autoscript = enabled and enabled.autoscript + local autolanguage = enabled and enabled.autolanguage + local res = resolved[font] + if not res then + res = { } + resolved[font] = res + end + local rs = res[script] + if not rs then + rs = { } + res[script] = rs + end + local rl = rs[language] + if not rl then + rl = { + -- indexed but we can also add specific data by key + } + rs[language] = rl + local sequences = tfmdata.resources.sequences + if sequences then + for s=1,#sequences do + local v = enabled and initialize(sequences[s],script,language,enabled,autoscript,autolanguage) + if v then + rl[#rl+1] = v + end + end + end + end + return rl + end + +end + +-- Functions like kernrun, comprun etc evolved over time and in the end look rather +-- complex. It's a bit of a compromis between extensive copying and creating subruns. +-- The logic has been improved a lot by Kai and Ivo who use complex fonts which +-- really helped to identify border cases on the one hand and get insight in the diverse +-- ways fonts implement features (not always that consistent and efficient). At the same +-- time I tried to keep the code relatively efficient so that the overhead in runtime +-- stays acceptable. + +local function report_disc(what,n) + report_run("%s: %s > %s",what,n,languages.serializediscretionary(n)) +end + +local function kernrun(disc,k_run,font,attr,...) + -- + -- we catch + -- + if trace_kernruns then + report_disc("kern",disc) + end + -- + local prev, next = getboth(disc) + -- + local nextstart = next + local done = false + -- + local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) + -- + local prevmarks = prev + -- + -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp + -- has happened but then it should be in the disc so basically this test indicates an error) + -- + while prevmarks do + local char = ischar(prevmarks,font) + if char and marks[char] then + prevmarks = getprev(prevmarks) + else + break + end + end + -- + if prev and not ischar(prev,font) then -- and (pre or replace) + prev = false + end + if next and not ischar(next,font) then -- and (post or replace) + next = false + end + -- + -- we need to get rid of this nest mess some day .. has to be done otherwise + -- + if pre then + if k_run(pre,"injections",nil,font,attr,...) then + done = true + end if prev then -- local nest = getprev(pre) setlink(prev,pre) @@ -3582,12 +4083,11 @@ 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 = disc -- bug + local tail = head for i=1,d do tail = getnext(tail) if getid(tail) == disc_code then @@ -3696,7 +4196,6 @@ end -- 1{2{\oldstyle\discretionary{3}{4}{5}}6}7\par -- 1{2\discretionary{3{\oldstyle3}}{{\oldstyle4}4}{5{\oldstyle5}5}6}7\par - local nesting = 0 local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) @@ -3818,6 +4317,7 @@ 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) @@ -3865,22 +4365,18 @@ 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 - -- we could move all code inline but that makes things even more unreadable - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done = true - break - elseif not start then - -- don't ask why ... shouldn't happen - break - end + local lookupmatch = lookupcache[char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + if ok then + done = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -3918,60 +4414,56 @@ 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 - -- if we need more than ligatures we can outline the code and use functions - local s = startnext - local ss = nil - local sstop = s == stop + local lookupmatch = lookupcache[char] + if lookupmatch then + -- if we need more than ligatures we can outline the code and use functions + 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") + end + 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 - end - 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 + 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 - 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 @@ -3985,6 +4477,7 @@ 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) @@ -4002,16 +4495,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,rlmode,step,i,injection) - if ok then - return true - end + local lookupmatch = lookupcache[char] + if lookupmatch then + local h, d, ok = handler(head,n,dataset,sequence,lookupmatch,rlmode,step,i,injection) + if ok then + return true end - else - report_missing_coverage(dataset,sequence) end end end @@ -4074,6 +4563,26 @@ otf.helpers.pardirstate = pardirstate do + -- reference: + -- + -- local a = attr and getattr(start,0) + -- if a then + -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) + -- else + -- a = not attribute or getprop(start,a_state) == attribute + -- end + -- + -- used: + -- + -- local a -- happens often so no assignment is faster + -- 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 + -- This is a measurable experimental speedup (only with hyphenated text and multiple -- fonts per processor call), especially for fonts with lots of contextual lookups. @@ -4184,19 +4693,15 @@ do -- for i=1,nofsteps do -- local step = steps[i] -- local lookupcache = step.coverage - -- if lookupcache then - -- local lookupmatch = lookupcache[char] - -- if lookupmatch then - -- -- todo: disc? - -- local ok - -- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - -- if ok then - -- done = true - -- break - -- end + -- local lookupmatch = lookupcache[char] + -- if lookupmatch then + -- -- todo: disc? + -- local ok + -- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + -- if ok then + -- done = true + -- break -- end - -- else - -- report_missing_coverage(dataset,sequence) -- end -- end -- if start then @@ -4215,72 +4720,62 @@ do -- if nofsteps == 1 then -- happens often -- local step = steps[1] -- local lookupcache = step.coverage - -- if not lookupcache then - -- report_missing_coverage(dataset,sequence) - -- else - -- while start do - -- local char, id = ischar(start,font) - -- if char then - -- -- local a = attr and getattr(start,0) - -- -- if a then - -- -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) - -- -- else - -- -- a = not attribute or getprop(start,a_state) == attribute - -- -- end - -- local a -- happens often so no assignment is faster - -- 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 + -- while start do + -- local char, id = ischar(start,font) + -- if char then + -- local a -- happens often so no assignment is faster + -- if attr then + -- if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then -- a = true -- end - -- if a then - -- 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 - -- end - -- end - -- if start then - -- start = getnext(start) - -- end - -- else - -- start = getnext(start) - -- end - -- elseif char == false then - -- -- whatever glyph - -- start = getnext(start) - -- elseif id == glue_code then - -- -- happens often - -- start = getnext(start) - -- elseif id == disc_code then - -- if not discs or discs[start] == true then + -- elseif not attribute or getprop(start,a_state) == attribute then + -- a = true + -- end + -- if a then + -- local lookupmatch = lookupcache[char] + -- if lookupmatch 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) - -- else - -- start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - -- end + -- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) -- if ok then -- done = true -- end - -- else + -- end + -- 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 + -- elseif char == false then + -- -- whatever glyph + -- start = getnext(start) + -- elseif id == glue_code then + -- -- happens often + -- 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_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) + -- else + -- start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,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 -- end -- @@ -4288,12 +4783,6 @@ do -- while start do -- local char, id = ischar(start,font) -- if char then - -- -- local a = attr and getattr(start,0) - -- -- if a then - -- -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) - -- -- else - -- -- a = not attribute or getprop(start,a_state) == attribute - -- -- end -- local a -- happens often so no assignment is faster -- if attr then -- if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then @@ -4306,22 +4795,18 @@ do -- for i=1,nofsteps do -- local step = steps[i] -- local lookupcache = step.coverage - -- if lookupcache then - -- local lookupmatch = lookupcache[char] - -- if lookupmatch then - -- -- we could move all code inline but that makes things even more unreadable - -- local ok - -- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - -- if ok then - -- done = true - -- break - -- elseif not start then - -- -- don't ask why ... shouldn't happen - -- break - -- end + -- local lookupmatch = lookupcache[char] + -- if lookupmatch then + -- -- we could move all code inline but that makes things even more unreadable + -- local ok + -- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + -- if ok then + -- done = true + -- break + -- elseif not start then + -- -- don't ask why ... shouldn't happen + -- break -- end - -- else - -- report_missing_coverage(dataset,sequence) -- end -- end -- if start then @@ -4381,6 +4866,14 @@ do -- about 15% on arabtype .. then moving the a test also saves a bit (even when -- often a is not set at all so that one is a bit debatable + local otfdataset = nil -- todo: make an installer + + local getfastdics = function(t,k) + local v = usesfont(k,currentfont) + t[k] = v + return v + end + function otf.featuresprocessor(head,font,attr,direction,n) local sequences = sequencelists[font] -- temp hack @@ -4403,6 +4896,12 @@ do factor = getthreshold(font) checkmarks = tfmdata.properties.checkmarks + if not otfdataset then + otfdataset = otf.dataset + end + + discs = fastdisc and n and n > 1 and setmetatableindex(getfastdisc) -- maybe inline + elseif currentfont ~= font then report_warning("nested call with a different font, level %s, quitting",nesting) @@ -4419,7 +4918,7 @@ do -- attr = false -- end - head = tonut(head) + local head = tonut(head) if trace_steps then checkstep(head) @@ -4428,7 +4927,8 @@ do local initialrl = direction == "TRT" and -1 or 0 local done = false - local datasets = otf.dataset(tfmdata,font,attr) + -- local datasets = otf.dataset(tfmdata,font,attr) + local datasets = otfdataset(tfmdata,font,attr) local dirstack = { } -- could move outside function but we can have local runs sweephead = { } @@ -4438,12 +4938,6 @@ do -- We don't goto the next node when a disc node is created so that we can then treat -- the pre, post and replace. It's a bit of a hack but works out ok for most cases. - local discs = fastdisc and n and n > 1 and setmetatableindex(function(t,k) - local v = usesfont(k,font) - t[k] = v - return v - end) - for s=1,#datasets do local dataset = datasets[s] ----- featurevalue = dataset[1] -- todo: pass to function instead of using a global @@ -4453,7 +4947,7 @@ do local topstack = 0 local typ = sequence.type local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset - local handler = handlers[typ] + local handler = handlers[typ] -- store in dataset local steps = sequence.steps local nofsteps = sequence.nofsteps if not steps then @@ -4489,19 +4983,15 @@ do -- for i=1,#m do -- local step = m[i] local lookupcache = step.coverage - if lookupcache then - local lookupmatch = lookupcache[char] - if lookupmatch then - -- todo: disc? - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done = true - break - end + local lookupmatch = lookupcache[char] + if lookupmatch then + -- todo: disc? + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + if ok then + done = true + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -4523,74 +5013,64 @@ do if nofsteps == 1 then -- happens often local step = steps[1] local lookupcache = step.coverage - if not lookupcache then - report_missing_coverage(dataset,sequence) - else - while start do - local char, id = ischar(start,font) - if char then - local lookupmatch = lookupcache[char] - if lookupmatch then - -- local a = attr and getattr(start,0) - -- if a then - -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) - -- else - -- a = not attribute or getprop(start,a_state) == attribute - -- end - local a -- happens often so no assignment is faster - 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 + while start do + local char, id = ischar(start,font) + if char then + local lookupmatch = lookupcache[char] + if lookupmatch then + local a -- happens often so no assignment is faster + if attr then + if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then a = true end - if a then - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) - if ok then - done = true - end - if start then - start = getnext(start) - end - else - start = getnext(start) - end - else - start = getnext(start) + elseif not attribute or getprop(start,a_state) == attribute then + a = true end - elseif char == false then - -- whatever glyph - start = getnext(start) - elseif id == glue_code then - -- happens often - start = getnext(start) - elseif id == disc_code then - if not discs or discs[start] == true then + if a 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) - else - start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - end + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) if ok then done = true end + if start then + start = getnext(start) + 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 + -- whatever glyph + start = getnext(start) + elseif id == glue_code then + -- happens often + 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_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) + else + start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,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 end else @@ -4600,12 +5080,6 @@ do if char then local m = merged[char] if m then - -- local a = attr and getattr(start,0) - -- if a then - -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) - -- else - -- a = not attribute or getprop(start,a_state) == attribute - -- end local a -- happens often so no assignment is faster if attr then if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then @@ -4620,22 +5094,18 @@ do -- for i=1,#m do -- local step = m[i] local lookupcache = step.coverage - if lookupcache then - local lookupmatch = lookupcache[char] - if lookupmatch then - -- we could move all code inline but that makes things even more unreadable - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done = true - break - elseif not start then - -- don't ask why ... shouldn't happen - break - end + local lookupmatch = lookupcache[char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + if ok then + done = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -4787,7 +5257,7 @@ local function hasspacekerns(data) for i=1,#steps do local coverage = steps[i].coverage if not coverage then - -- maybe an issue + -- maybe an issue, can't happen elseif coverage[32] then return true else @@ -4852,7 +5322,7 @@ local function spaceinitializer(tfmdata,value) -- attr -- not now: analyze (simple) rules elseif coverage then -- what to do if we have no [1] but only [2] - local single = format == gpos_single + local single = format == "gpos_single" local kerns = coverage[32] if kerns then for k, v in next, kerns do @@ -4877,12 +5347,14 @@ local function spaceinitializer(tfmdata,value) -- attr left[k] = kern[3] else local one = kern[1] - if one then + if one and one ~= true then left[k] = one[3] end end end end + else + -- can't happen end end last = i diff --git a/tex/context/base/mkiv/font-oup.lua b/tex/context/base/mkiv/font-oup.lua index 5ee6df505..9f4ded5b5 100644 --- a/tex/context/base/mkiv/font-oup.lua +++ b/tex/context/base/mkiv/font-oup.lua @@ -622,7 +622,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 @@ -1067,13 +1067,14 @@ function readers.pack(data) 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 @@ -1082,8 +1083,8 @@ function readers.pack(data) 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 @@ -1127,6 +1128,84 @@ function readers.pack(data) end end + -- saves a lot on noto sans + + -- can be made more clever + + 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 + + -- -- This was an experiment to see if we can bypass the luajit limits but loading is + -- -- still an issue due to other limits so we don't use this ... actually it can + -- -- prevent a luajittex crash but i don't care too much about that as we can't use + -- -- that engine anyway then. + -- + -- local function check(t) + -- if type(t) == "table" then + -- local s = sortedkeys(t) + -- local n = #s + -- if n <= 10 then + -- return + -- end + -- local ranges = { } + -- local first, last + -- for i=1,#s do + -- local ti = s[i] + -- if not first then + -- first = ti + -- last = ti + -- elseif ti == last + 1 then + -- last = ti + -- elseif last - first < 10 then + -- -- we could permits a few exceptions + -- return + -- else + -- ranges[#ranges+1] = { first, last } + -- first, last = nil, nil + -- end + -- end + -- if #ranges > 0 then + -- return { + -- ranges = ranges + -- } + -- end + -- end + -- end + -- + -- local function pack_boolean(v) + -- local tag + -- local r = check(v) + -- if r then + -- v = r + -- tag = tabstr_normal(v) + -- else + -- tag = tabstr_boolean(v) + -- end + -- 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) -- v == number if c[v] <= criterium then @@ -1146,6 +1225,25 @@ function readers.pack(data) end end + local function pack_final_cc(v) + -- v == number + if c[v] <= criterium then + return t[v] + else + -- compact hash + 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 @@ -1192,9 +1290,9 @@ function readers.pack(data) 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 @@ -1212,15 +1310,13 @@ function readers.pack(data) return end - -- - for pass=1,2 do 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 @@ -1267,8 +1363,8 @@ function readers.pack(data) 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 + 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 end @@ -1280,7 +1376,9 @@ function readers.pack(data) step.coverage = pack_normal(c) else for g1, d1 in next, c do - c[g1] = pack_indexed(d1) + if d1 ~= true then + c[g1] = pack_indexed(d1) + end end end end @@ -1400,7 +1498,7 @@ function readers.pack(data) for i=1,#deltas do local di = deltas[i] local d = di.deltas - local r = di.regions + -- local r = di.regions for j=1,#d do d[j] = pack_indexed(d[j]) end @@ -1440,7 +1538,7 @@ function readers.pack(data) 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 @@ -1464,9 +1562,7 @@ function readers.pack(data) if kind == "gpos_pair" then local c = step.coverage if c then - if step.format == "kern" then - -- todo ! - else + if step.format ~= "kern" then for g1, d1 in next, c do for g2, d2 in next, d1 do d1[g2] = pack_normal(d2) @@ -1474,11 +1570,22 @@ function readers.pack(data) end end end --- elseif kind == "gpos_mark2base" or kind == "gpos_mark2mark" or kind == "gpos_mark2ligature" then --- local c = step.baseclasses --- for k, v in next, c do --- c[k] = pack_normal(v) --- end + -- elseif kind == "gpos_cursive" then + -- local c = step.coverage -- new + -- if c then + -- for g1, d1 in next, c do + -- c[g1] = pack_normal_cc(d1) + -- end + -- end + elseif kind == "gpos_mark2ligature" then + local c = step.baseclasses -- new + 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 @@ -1526,7 +1633,7 @@ function readers.pack(data) 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 @@ -1540,18 +1647,23 @@ function readers.pack(data) if kind == "gpos_pair" then local c = step.coverage if c then - if step.format == "kern" then - -- todo ! - else + if step.format ~= "kern" 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 - end + end end if sequences then @@ -1627,6 +1739,15 @@ function readers.unpack(data) -- end end + -- local function expandranges(t,ranges) + -- for i=1,#ranges do + -- local r = ranges[i] + -- for k=r[1],r[2] do + -- t[k] = true + -- end + -- end + -- end + local function unpackthem(sequences) for i=1,#sequences do local sequence = sequences[i] @@ -1636,6 +1757,19 @@ 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] @@ -1689,6 +1823,11 @@ function readers.unpack(data) 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 @@ -1696,12 +1835,6 @@ function readers.unpack(data) elseif kind == "gpos_mark2base" or kind == "gpos_mark2mark" then local c = step.baseclasses if c then --- for k, v in next, c do --- local tv = tables[v] --- if tv then --- c[k] = tv --- end --- end for g1, d1 in next, c do for g2, d2 in next, d1 do local tv = tables[d2] @@ -1723,14 +1856,13 @@ function readers.unpack(data) elseif kind == "gpos_mark2ligature" then local c = step.baseclasses if c then --- for k, v in next, c do --- local tv = tables[v] --- if tv then --- c[k] = tv --- end --- end for g1, d1 in next, c do for g2, d2 in next, d1 do + local tv = tables[d2] -- new + if tv then + d2 = tv + d1[g2] = d2 + end for g3, d3 in next, d2 do local tv = tables[d2[g3]] if tv then @@ -1767,6 +1899,18 @@ function readers.unpack(data) before[i] = tv end end + -- for i=1,#before do + -- local bi = before[i] + -- local tv = tables[bi] + -- if tv then + -- bi = tv + -- before[i] = bi + -- end + -- local ranges = bi.ranges + -- if ranges then + -- expandranges(bi,ranges) + -- end + -- end end local after = rule.after if after then @@ -1781,6 +1925,18 @@ function readers.unpack(data) after[i] = tv end end + -- for i=1,#after do + -- local ai = after[i] + -- local tv = tables[ai] + -- if tv then + -- ai = tv + -- after[i] = ai + -- end + -- local ranges = ai.ranges + -- if ranges then + -- expandranges(ai,ranges) + -- end + -- end end local current = rule.current if current then @@ -1795,6 +1951,18 @@ function readers.unpack(data) current[i] = tv end end + -- for i=1,#current do + -- local ci = current[i] + -- local tv = tables[ci] + -- if tv then + -- ci = tv + -- current[i] = ci + -- end + -- local ranges = ci.ranges + -- if ranges then + -- expandranges(ci,ranges) + -- end + -- end end -- local lookups = rule.lookups -- if lookups then @@ -1814,19 +1982,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 @@ -2140,11 +2295,14 @@ local function checkpairs(lookup) for g1, d1 in next, coverage do for g2, d2 in next, d1 do if d2[2] then + --- true or { a, b, c, d } kerns = false break else local v = d2[1] - if v[1] ~= 0 or v[2] ~= 0 or v[4] ~= 0 then + if v == true then + -- all zero + elseif v and (v[1] ~= 0 or v[2] ~= 0 or v[4] ~= 0) then kerns = false break end @@ -2155,7 +2313,12 @@ local function checkpairs(lookup) report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name) for g1, d1 in next, coverage do for g2, d2 in next, d1 do - d1[g2] = d2[1][3] + local v = d2[1] + if v == true then + d1[g2] = nil + elseif v then + d1[g2] = v[3] + end end end step.format = "kern" @@ -2185,6 +2348,7 @@ function readers.compact(data) allsteps = allsteps + nofsteps if nofsteps > 1 then local kind = lookup.type + local merg = merged if kind == "gsub_single" or kind == "gsub_alternate" or kind == "gsub_multiple" then merged = merged + mergesteps_1(lookup) elseif kind == "gsub_ligature" then @@ -2200,6 +2364,9 @@ function readers.compact(data) elseif kind == "gpos_mark2mark" or kind == "gpos_mark2base" or kind == "gpos_mark2ligature" then merged = merged + mergesteps_3(lookup) end + if merg ~= merged then + lookup.merged = true + end end end else @@ -2268,7 +2435,7 @@ function readers.expand(data) for i=1,#t do local step = t[i] local coverage = step.coverage - for k, v in next, coverage do + for k in next, coverage do local m = merged[k] if m then m[2] = i @@ -2291,6 +2458,7 @@ function readers.expand(data) local sequence = sequences[i] local steps = sequence.steps if steps then + local nofsteps = sequence.nofsteps setmetatableindex(steps,mergesteps) @@ -2304,7 +2472,14 @@ function readers.expand(data) sequence.markclass = markclasses[markclass] end end - local nofsteps = sequence.nofsteps + local flags = sequence.flags + if flags then + flags[5] = flags[1] ~= false -- otherwise "mark" + or flags[2] ~= false -- otherwise "base" + or flags[3] ~= false -- otherwise "ligature" + or sequence.markclass + or false + end for i=1,nofsteps do local step = steps[i] local baseclasses = step.baseclasses @@ -2373,6 +2548,7 @@ function readers.expand(data) end end if sequence[1] then -- we merge coverage into one + sequence.n = #sequence -- tiny speedup rulesize = rulesize + 1 rulehash[rulesize] = { nofrules, -- 1 @@ -2393,6 +2569,7 @@ function readers.expand(data) end end end + rulehash.n = #rulehash -- tiny speedup end end end diff --git a/tex/context/base/mkiv/font-shp.lua b/tex/context/base/mkiv/font-shp.lua index c465ec91b..918304a0b 100644 --- a/tex/context/base/mkiv/font-shp.lua +++ b/tex/context/base/mkiv/font-shp.lua @@ -362,7 +362,7 @@ local function addvariableshapes(tfmdata,key,value) -- we need inline in order to support color local bt, et = getactualtext(char.tounicode or char.unicode or unicode) char.commands = { - { "pdf", segmentstopdf(segments,factor,bt,et) } + { "pdf", "origin", segmentstopdf(segments,factor,bt,et) } } end end diff --git a/tex/context/base/mkiv/font-vf.lua b/tex/context/base/mkiv/font-vf.lua deleted file mode 100644 index ded992850..000000000 --- a/tex/context/base/mkiv/font-vf.lua +++ /dev/null @@ -1,206 +0,0 @@ -if not modules then modules = { } end modules ['font-vf'] = { - version = 1.001, - 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" -} - ---[[ldx-- -

This is very experimental code! Not yet adapted to recent changes. This will change.

---ldx]]-- - --- present in the backend but unspecified: --- --- vf.rule vf.special vf.right vf.push vf.down vf.char vf.node vf.fontid vf.pop vf.image vf.nop - -local next = next - -local allocate = utilities.storage.allocate -local setmetatableindex = table.setmetatableindex -local fastcopy = table.fastcopy - -local fonts = fonts -local constructors = fonts.constructors -local vf = constructors.handlers.vf -vf.version = 1.000 -- same as tfm - ---[[ldx-- -

We overload the reader.

---ldx]]-- - --- general code / already frozen --- --- function vf.find(name) --- name = file.removesuffix(file.basename(name)) --- if constructors.resolvevirtualtoo then --- local format = fonts.loggers.format(name) --- if format == 'tfm' or format == 'ofm' then --- if trace_defining then --- report_defining("locating vf for %a",name) --- end --- return findbinfile(name,"ovf") or "" --- else --- if trace_defining then --- report_defining("vf for %a is already taken care of",name) --- end --- return "" --- end --- else --- if trace_defining then --- report_defining("locating vf for %a",name) --- end --- return findbinfile(name,"ovf") or "" --- end --- end --- --- callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more - --- specific code (will move to other module) - -local definers = fonts.definers -local methods = definers.methods - -local variants = allocate() -local combinations = { } -local combiner = { } -local whatever = allocate() -local helpers = allocate() -local predefined = allocate { - dummy = { "comment" }, - push = { "push" }, - pop = { "pop" }, -} - -methods.variants = variants -- todo .. wrong namespace -vf.combinations = combinations -vf.combiner = combiner -vf.whatever = whatever -vf.helpers = helpers -vf.predefined = predefined - -setmetatableindex(whatever, function(t,k) local v = { } t[k] = v return v end) - -local function checkparameters(g,f) - if f and g and not g.parameters and #g.fonts > 0 then - local p = { } - for k,v in next, f.parameters do - p[k] = v - end - g.parameters = p - setmetatable(p, getmetatable(f.parameters)) - end -end - -function methods.install(tag, rules) - vf.combinations[tag] = rules - variants[tag] = function(specification) - return vf.combine(specification,tag) - end -end - -local function combine_load(g,name) - return constructors.readanddefine(name or g.specification.name,g.specification.size) -end - -local function combine_assign(g, name, from, to, start, force) - local f, id = combine_load(g,name) - if f and id then - -- optimize for whole range, then just g = f - if not from then from, to = 0, 0xFF00 end - if not to then to = from end - if not start then start = from end - local fc, gc = f.characters, g.characters - local fd, gd = f.descriptions, g.descriptions - local hn = #g.fonts+1 - g.fonts[hn] = { id = id } -- no need to be sparse - for i=from,to do - if fc[i] and (force or not gc[i]) then - gc[i] = fastcopy(fc[i],true) -- can be optimized - gc[i].commands = { { "slot", hn, start } } - gd[i] = fd[i] - end - start = start + 1 - end - checkparameters(g,f) - end -end - -local function combine_process(g,list) - if list then - for _,v in next, list do - (combiner.commands[v[1]] or nop)(g,v) - end - end -end - -local function combine_names(g,name,force) - local f, id = constructors.readanddefine(name,g.specification.size) - if f and id then - local fc, gc = f.characters, g.characters - local fd, gd = f.descriptions, g.descriptions - g.fonts[#g.fonts+1] = { id = id } -- no need to be sparse - local hn = #g.fonts - for k, v in next, fc do - if force or not gc[k] then - gc[k] = fastcopy(v,true) - gc[k].commands = { { "slot", hn, k } } - gd[i] = fd[i] - end - end - checkparameters(g,f) - end -end - -local combine_feature = function(g,v) - local key, value = v[2], v[3] - if key then - if value == nil then - value = true - end - local specification = g.specification - if specification then - local normalfeatures = specification.features.normal - if normalfeatures then - normalfeatures[key] = value -- otf? - end - end - end -end - ---~ combiner.load = combine_load ---~ combiner.assign = combine_assign ---~ combiner.process = combine_process ---~ combiner.names = combine_names ---~ combiner.feature = combine_feature - -combiner.commands = allocate { - ["initialize"] = function(g,v) combine_assign (g,g.properties.name) end, - ["include-method"] = function(g,v) combine_process (g,combinations[v[2]]) end, -- name - -- ["copy-parameters"] = function(g,v) combine_parameters(g,v[2]) end, -- name - ["copy-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],true) end, -- name, from-start, from-end, to-start - ["copy-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],true) end, -- name, from, to - ["fallback-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],false) end, -- name, from-start, from-end, to-start - ["fallback-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],false) end, -- name, from, to - ["copy-names"] = function(g,v) combine_names (g,v[2],true) end, - ["fallback-names"] = function(g,v) combine_names (g,v[2],false) end, - ["feature"] = combine_feature, -} - -function vf.combine(specification,tag) - local g = { - name = specification.name, - properties = { - virtualized = true, - }, - fonts = { - }, - characters = { - }, - descriptions = { - }, - specification = fastcopy(specification), - } - combine_process(g,combinations[tag]) - return g -end diff --git a/tex/context/base/mkiv/font-vir.lua b/tex/context/base/mkiv/font-vir.lua new file mode 100644 index 000000000..03ad7fc85 --- /dev/null +++ b/tex/context/base/mkiv/font-vir.lua @@ -0,0 +1,206 @@ +if not modules then modules = { } end modules ['font-vir'] = { + version = 1.001, + 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" +} + +--[[ldx-- +

This is very experimental code! Not yet adapted to recent changes. This will change.

+--ldx]]-- + +-- present in the backend but unspecified: +-- +-- vf.rule vf.special vf.right vf.push vf.down vf.char vf.node vf.fontid vf.pop vf.image vf.nop + +local next = next + +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex +local fastcopy = table.fastcopy + +local fonts = fonts +local constructors = fonts.constructors +local vf = constructors.handlers.vf +vf.version = 1.000 -- same as tfm + +--[[ldx-- +

We overload the reader.

+--ldx]]-- + +-- general code / already frozen +-- +-- function vf.find(name) +-- name = file.removesuffix(file.basename(name)) +-- if constructors.resolvevirtualtoo then +-- local format = fonts.loggers.format(name) +-- if format == 'tfm' or format == 'ofm' then +-- if trace_defining then +-- report_defining("locating vf for %a",name) +-- end +-- return findbinfile(name,"ovf") or "" +-- else +-- if trace_defining then +-- report_defining("vf for %a is already taken care of",name) +-- end +-- return "" +-- end +-- else +-- if trace_defining then +-- report_defining("locating vf for %a",name) +-- end +-- return findbinfile(name,"ovf") or "" +-- end +-- end +-- +-- callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more + +-- specific code (will move to other module) + +local definers = fonts.definers +local methods = definers.methods + +local variants = allocate() +local combinations = { } +local combiner = { } +local whatever = allocate() +local helpers = allocate() +local predefined = allocate { + dummy = { "comment" }, + push = { "push" }, + pop = { "pop" }, +} + +methods.variants = variants -- todo .. wrong namespace +vf.combinations = combinations +vf.combiner = combiner +vf.whatever = whatever +vf.helpers = helpers +vf.predefined = predefined + +setmetatableindex(whatever, function(t,k) local v = { } t[k] = v return v end) + +local function checkparameters(g,f) + if f and g and not g.parameters and #g.fonts > 0 then + local p = { } + for k,v in next, f.parameters do + p[k] = v + end + g.parameters = p + setmetatable(p, getmetatable(f.parameters)) + end +end + +function methods.install(tag, rules) + vf.combinations[tag] = rules + variants[tag] = function(specification) + return vf.combine(specification,tag) + end +end + +local function combine_load(g,name) + return constructors.readanddefine(name or g.specification.name,g.specification.size) +end + +local function combine_assign(g, name, from, to, start, force) + local f, id = combine_load(g,name) + if f and id then + -- optimize for whole range, then just g = f + if not from then from, to = 0, 0xFF00 end + if not to then to = from end + if not start then start = from end + local fc, gc = f.characters, g.characters + local fd, gd = f.descriptions, g.descriptions + local hn = #g.fonts+1 + g.fonts[hn] = { id = id } -- no need to be sparse + for i=from,to do + if fc[i] and (force or not gc[i]) then + gc[i] = fastcopy(fc[i],true) -- can be optimized + gc[i].commands = { { "slot", hn, start } } + gd[i] = fd[i] + end + start = start + 1 + end + checkparameters(g,f) + end +end + +local function combine_process(g,list) + if list then + for _,v in next, list do + (combiner.commands[v[1]] or nop)(g,v) + end + end +end + +local function combine_names(g,name,force) + local f, id = constructors.readanddefine(name,g.specification.size) + if f and id then + local fc, gc = f.characters, g.characters + local fd, gd = f.descriptions, g.descriptions + g.fonts[#g.fonts+1] = { id = id } -- no need to be sparse + local hn = #g.fonts + for k, v in next, fc do + if force or not gc[k] then + gc[k] = fastcopy(v,true) + gc[k].commands = { { "slot", hn, k } } + gd[i] = fd[i] + end + end + checkparameters(g,f) + end +end + +local combine_feature = function(g,v) + local key, value = v[2], v[3] + if key then + if value == nil then + value = true + end + local specification = g.specification + if specification then + local normalfeatures = specification.features.normal + if normalfeatures then + normalfeatures[key] = value -- otf? + end + end + end +end + +--~ combiner.load = combine_load +--~ combiner.assign = combine_assign +--~ combiner.process = combine_process +--~ combiner.names = combine_names +--~ combiner.feature = combine_feature + +combiner.commands = allocate { + ["initialize"] = function(g,v) combine_assign (g,g.properties.name) end, + ["include-method"] = function(g,v) combine_process (g,combinations[v[2]]) end, -- name + -- ["copy-parameters"] = function(g,v) combine_parameters(g,v[2]) end, -- name + ["copy-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],true) end, -- name, from-start, from-end, to-start + ["copy-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],true) end, -- name, from, to + ["fallback-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],false) end, -- name, from-start, from-end, to-start + ["fallback-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],false) end, -- name, from, to + ["copy-names"] = function(g,v) combine_names (g,v[2],true) end, + ["fallback-names"] = function(g,v) combine_names (g,v[2],false) end, + ["feature"] = combine_feature, +} + +function vf.combine(specification,tag) + local g = { + name = specification.name, + properties = { + virtualized = true, + }, + fonts = { + }, + characters = { + }, + descriptions = { + }, + specification = fastcopy(specification), + } + combine_process(g,combinations[tag]) + return g +end diff --git a/tex/context/base/mkiv/hand-ini.mkiv b/tex/context/base/mkiv/hand-ini.mkiv index d4bffdb14..f678a88da 100644 --- a/tex/context/base/mkiv/hand-ini.mkiv +++ b/tex/context/base/mkiv/hand-ini.mkiv @@ -60,6 +60,7 @@ \def\font_expansion_enable {\normaladjustspacing\plusthree} \def\font_expansion_enable_k{\normaladjustspacing\plustwo} +\def\font_expansion_enable_n{\normaladjustspacing\plusone} \def\font_expansion_disable {\normaladjustspacing\zerocount} \def\font_protruding_enable {\normalprotrudechars\plustwo } diff --git a/tex/context/base/mkiv/l-package.lua b/tex/context/base/mkiv/l-package.lua index 075fcde25..5f72dd6b2 100644 --- a/tex/context/base/mkiv/l-package.lua +++ b/tex/context/base/mkiv/l-package.lua @@ -23,6 +23,7 @@ local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match local package = package local searchers = package.searchers or package.loaders +local insert, remove = table.insert, table.remove -- dummies @@ -191,7 +192,25 @@ local function registerpath(tag,what,target,...) add(path) end end - return paths +end + +local function pushpath(tag,what,target,path) + local path = helpers.cleanpath(path) + insert(target,1,path) + if helpers.trace then + helpers.report("pushing %s path in front: %s",tag,path) + end +end + +local function poppath(tag,what,target) + local path = remove(target,1) + if helpers.trace then + if path then + helpers.report("popping %s path from front: %s",tag,path) + else + helpers.report("no %s path to pop",tag) + end + end end helpers.registerpath = registerpath @@ -199,10 +218,22 @@ helpers.registerpath = registerpath function package.extraluapath(...) registerpath("extra lua","lua",extraluapaths,...) end +function package.pushluapath(path) + pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() + poppath("extra lua","lua",extraluapaths) +end function package.extralibpath(...) registerpath("extra lib","lib",extralibpaths,...) end +function package.pushlibpath(path) + pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() + poppath("extra lib","lua",extralibpaths) +end -- lib loader (used elsewhere) diff --git a/tex/context/base/mkiv/lpdf-col.lua b/tex/context/base/mkiv/lpdf-col.lua index af01c7dd2..a1006f773 100644 --- a/tex/context/base/mkiv/lpdf-col.lua +++ b/tex/context/base/mkiv/lpdf-col.lua @@ -22,7 +22,7 @@ local nodeinjections = backends.pdf.nodeinjections local codeinjections = backends.pdf.codeinjections local registrations = backends.pdf.registrations -local nodepool = nodes.pool +local nodepool = nodes.nuts.pool local register = nodepool.register local pdfliteral = nodepool.pdfliteral @@ -707,25 +707,25 @@ local f_slant = formatters["q 1 0 %F 1 0 0 cm"] backends.pdf.tables.vfspecials = allocate { -- todo: distinguish between glyph and rule color - red = { "pdf", "1 0 0 rg 1 0 0 RG" }, - green = { "pdf", "0 1 0 rg 0 1 0 RG" }, - blue = { "pdf", "0 0 1 rg 0 0 1 RG" }, - gray = { "pdf", ".75 g .75 G" }, - black = { "pdf", "0 g 0 G" }, + red = { "pdf", "origin", "1 0 0 rg 1 0 0 RG" }, + green = { "pdf", "origin", "0 1 0 rg 0 1 0 RG" }, + blue = { "pdf", "origin", "0 0 1 rg 0 0 1 RG" }, + gray = { "pdf", "origin", ".75 g .75 G" }, + black = { "pdf", "origin", "0 g 0 G" }, rulecolors = { - red = { "pdf", '1 0 0 rg' }, - green = { "pdf", '0 1 0 rg' }, - blue = { "pdf", '0 0 1 rg' }, - gray = { "pdf", '.5 g' }, - black = { "pdf", '0 g' }, - palered = { "pdf", '1 .75 .75 rg' }, - palegreen = { "pdf", '.75 1 .75 rg' }, - paleblue = { "pdf", '.75 .75 1 rg' }, - palegray = { "pdf", '.75 g' }, + red = { "pdf", "origin", '1 0 0 rg' }, + green = { "pdf", "origin", '0 1 0 rg' }, + blue = { "pdf", "origin", '0 0 1 rg' }, + gray = { "pdf", "origin", '.5 g' }, + black = { "pdf", "origin", '0 g' }, + palered = { "pdf", "origin", '1 .75 .75 rg' }, + palegreen = { "pdf", "origin", '.75 1 .75 rg' }, + paleblue = { "pdf", "origin", '.75 .75 1 rg' }, + palegray = { "pdf", "origin", '.75 g' }, }, - startslant = function(a) return { "pdf", f_slant(a) } end, - stopslant = { "pdf", "Q" }, + startslant = function(a) return { "pdf", "origin", f_slant(a) } end, + stopslant = { "pdf", "origin", "Q" }, } diff --git a/tex/context/base/mkiv/lpdf-mis.lua b/tex/context/base/mkiv/lpdf-mis.lua index 91dd08afd..9d903ce96 100644 --- a/tex/context/base/mkiv/lpdf-mis.lua +++ b/tex/context/base/mkiv/lpdf-mis.lua @@ -25,9 +25,10 @@ local nodeinjections = backends.pdf.nodeinjections local codeinjections = backends.pdf.codeinjections local registrations = backends.pdf.registrations -local copy_node = node.copy +local nuts = nodes.nuts +local copy_node = nuts.copy -local nodepool = nodes.pool +local nodepool = nuts.pool local pdfliteral = nodepool.pdfliteral local register = nodepool.register diff --git a/tex/context/base/mkiv/lpdf-ren.lua b/tex/context/base/mkiv/lpdf-ren.lua index 47075ee08..a57c550fd 100644 --- a/tex/context/base/mkiv/lpdf-ren.lua +++ b/tex/context/base/mkiv/lpdf-ren.lua @@ -49,7 +49,10 @@ local addtocatalog = lpdf.addtocatalog local escaped = lpdf.escaped -local nodepool = nodes.pool +local nuts = nodes.nuts +local copy_node = nuts.copy + +local nodepool = nuts.pool local register = nodepool.register local pdfliteral = nodepool.pdfliteral @@ -62,8 +65,6 @@ local pdf_design = pdfconstant("Design") local pdf_toggle = pdfconstant("Toggle") local pdf_setocgstate = pdfconstant("SetOCGState") -local copy_node = node.copy - local pdf_print = { [v_yes] = pdfdictionary { PrintState = pdf_on }, [v_no ] = pdfdictionary { PrintState = pdf_off }, diff --git a/tex/context/base/mkiv/lpdf-xmp.lua b/tex/context/base/mkiv/lpdf-xmp.lua index 26ced4aee..a0005eb8a 100644 --- a/tex/context/base/mkiv/lpdf-xmp.lua +++ b/tex/context/base/mkiv/lpdf-xmp.lua @@ -241,6 +241,8 @@ end -- flushing +local add_xmp_blob = true directives.register("backend.xmp",function(v) add_xmp_blob = v end) + local function flushxmpinfo() commands.pushrandomseed() commands.setrandomseed(os.time()) @@ -261,42 +263,45 @@ local function flushxmpinfo() instanceid = "uuid:" .. os.uuid() end - pdfaddxmpinfo("DocumentID", documentid) - pdfaddxmpinfo("InstanceID", instanceid) - pdfaddxmpinfo("Producer", producer) - pdfaddxmpinfo("CreatorTool", creator) - pdfaddxmpinfo("CreateDate", time) - pdfaddxmpinfo("ModifyDate", time) - pdfaddxmpinfo("MetadataDate", time) - pdfaddxmpinfo("PTEX.Fullbanner", fullbanner) - pdfaddtoinfo("Producer", producer) pdfaddtoinfo("Creator", creator) pdfaddtoinfo("CreationDate", time) pdfaddtoinfo("ModDate", time) -- pdfaddtoinfo("PTEX.Fullbanner", fullbanner) -- no checking done on existence - local blob = xml.tostring(xml.first(xmp or valid_xmp(),"/x:xmpmeta")) - local md = pdfdictionary { - Subtype = pdfconstant("XML"), - Type = pdfconstant("Metadata"), - } - if trace_xmp then - report_xmp("data flushed, see log file") - logs.pushtarget("logfile") - report_xmp("start xmp blob") - logs.newline() - logs.writer(blob) - logs.newline() - report_xmp("stop xmp blob") - logs.poptarget() - end - blob = format(xpacket,blob) - if not verbose and pdf.getcompresslevel() > 0 then - blob = gsub(blob,">%s+<","><") + if add_xmp_blob then + + pdfaddxmpinfo("DocumentID", documentid) + pdfaddxmpinfo("InstanceID", instanceid) + pdfaddxmpinfo("Producer", producer) + pdfaddxmpinfo("CreatorTool", creator) + pdfaddxmpinfo("CreateDate", time) + pdfaddxmpinfo("ModifyDate", time) + pdfaddxmpinfo("MetadataDate", time) + pdfaddxmpinfo("PTEX.Fullbanner", fullbanner) + + local blob = xml.tostring(xml.first(xmp or valid_xmp(),"/x:xmpmeta")) + local md = pdfdictionary { + Subtype = pdfconstant("XML"), + Type = pdfconstant("Metadata"), + } + if trace_xmp then + report_xmp("data flushed, see log file") + logs.pushtarget("logfile") + report_xmp("start xmp blob") + logs.newline() + logs.writer(blob) + logs.newline() + report_xmp("stop xmp blob") + logs.poptarget() + end + blob = format(xpacket,blob) + if not verbose and pdf.getcompresslevel() > 0 then + blob = gsub(blob,">%s+<","><") + end + local r = pdfflushstreamobject(blob,md,false) -- uncompressed + lpdf.addtocatalog("Metadata",pdfreference(r)) end - local r = pdfflushstreamobject(blob,md,false) -- uncompressed - lpdf.addtocatalog("Metadata",pdfreference(r)) commands.poprandomseed() -- hack end diff --git a/tex/context/base/mkiv/lxml-tex.lua b/tex/context/base/mkiv/lxml-tex.lua index f79aa708f..76a20fbef 100644 --- a/tex/context/base/mkiv/lxml-tex.lua +++ b/tex/context/base/mkiv/lxml-tex.lua @@ -34,6 +34,8 @@ local commands = commands local context = context local contextsprint = context.sprint -- with catcodes (here we use fast variants, but with option for tracing) +local synctex = luatex.synctex + local implement = interfaces.implement local xmlelements = xml.elements @@ -486,13 +488,8 @@ local noffiles = 0 local nofconverted = 0 local linenumbers = false --- directives.register("lxml.linenumbers", function(v) --- linenumbers = v --- end) - -directives.register("system.synctex.xml",function(v) - linenumbers = v -end) +synctex.registerenabler (function() linenumbers = true end) +synctex.registerdisabler(function() linenumbers = false end) function xml.load(filename,settings) noffiles, nofconverted = noffiles + 1, nofconverted + 1 @@ -713,13 +710,8 @@ local setfilename = false local trace_name = false local report_name = logs.reporter("lxml") -directives.register("system.synctex.xml",function(v) - if v then - setfilename = luatex.synctex.setfilename - else - setfilename = false - end -end) +synctex.registerenabler (function() setfilename = synctex.setfilename end) +synctex.registerdisabler(function() setfilename = false end) local function syncfilename(e,where) local cf = e.cf @@ -1050,9 +1042,6 @@ xml.cprint = cprint local xmlcprint = cprint -- calls ct mathml -> will be re function lxml.main(id) local root = getid(id) --- if setfilename then --- syncfilename(root,"main") --- end xmlserialize(root,xmltexhandler) -- the real root (@rt@) end diff --git a/tex/context/base/mkiv/m-fonts-plugins.mkiv b/tex/context/base/mkiv/m-fonts-plugins.mkiv index 7678f820c..85411025f 100644 --- a/tex/context/base/mkiv/m-fonts-plugins.mkiv +++ b/tex/context/base/mkiv/m-fonts-plugins.mkiv @@ -19,6 +19,8 @@ %D below should work in the generic loader too. It's anyhow an illustration of %D how \type {ffi} can work be used in a practical application. +% \enabletrackers[resolvers.ffilib] + \registerctxluafile{font-txt}{1.001} % generic text handler \registerctxluafile{font-phb}{1.001} % harfbuzz plugin @@ -201,10 +203,11 @@ [arabic-uniscribe] [mode=plug, features=harfbuzz, - script=arab,language=dflt,ccmp=yes, - init=yes,medi=yes,fina=yes,isol=yes, - liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, - mark=yes,mkmk=yes,kern=yes,curs=yes, + script=arab,language=dflt, +% ccmp=yes, +% init=yes,medi=yes,fina=yes,isol=yes, +% liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, +% mark=yes,mkmk=yes,kern=yes,curs=yes, shaper=uniscribe] \starttexdefinition RunLatinTest #1#2#3#4#5 @@ -320,7 +323,7 @@ context.RunLatinTest (name, 100, "context", "base", "latin") context.RunLatinTest (name, 100, "context", "node", "latin") context.RunLatinTest (name, 100, "harfbuzz", "native", "latin") - -- context.RunLatinTest (name, 100, "harfbuzz", "uniscribe", "latin") + context.RunLatinTest (name, 100, "harfbuzz", "uniscribe", "latin") -- context.RunLatinTest (name, 1, "context", "text", "latin") -- context.RunLatinTest (name, 1, "harfbuzz", "binary", "latin") @@ -336,7 +339,8 @@ -- ARABIC local list = { - "arabtype" + "arabtype", + "husayni", } data.timings["arabic"] = { @@ -353,7 +357,7 @@ context.RunArabicTest (name, 100, "context", "base", "arabic") context.RunArabicTest (name, 100, "context", "node", "arabic") context.RunArabicTest (name, 100, "harfbuzz", "native", "arabic") - -- context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "arabic") + context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "arabic") -- context.RunArabicTest (name, 1, "context", "text", "arabic") -- context.RunArabicTest (name, 1, "harfbuzz", "binary", "arabic") @@ -369,7 +373,8 @@ -- MIXED local list = { - "arabtype" + "arabtype", + "husayni" } data.timings["mixed"] = { @@ -386,7 +391,7 @@ context.RunArabicTest (name, 100, "context", "base", "mixed") context.RunArabicTest (name, 100, "context", "node", "mixed") context.RunArabicTest (name, 100, "harfbuzz", "native", "mixed") - -- context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "mixed") + context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "mixed") -- context.RunArabicTest (name, 1, "context", "text", "mixed") -- context.RunArabicTest (name, 1, "harfbuzz", "binary", "mixed") diff --git a/tex/context/base/mkiv/meta-fnt.lua b/tex/context/base/mkiv/meta-fnt.lua index ea7f6f2bc..e7e83c694 100644 --- a/tex/context/base/mkiv/meta-fnt.lua +++ b/tex/context/base/mkiv/meta-fnt.lua @@ -66,7 +66,7 @@ local flusher = { if inline then characters[slot] = { commands = { - { "pdf", code }, + { "pdf", "origin", code }, } } else diff --git a/tex/context/base/mkiv/node-fin.lua b/tex/context/base/mkiv/node-fin.lua index ffb2ae49e..5d01f6e5a 100644 --- a/tex/context/base/mkiv/node-fin.lua +++ b/tex/context/base/mkiv/node-fin.lua @@ -124,12 +124,6 @@ function nodes.installattributehandler(plugin) return loadstripped(template)() end --- for the moment: - -local function copied(n) - return copy_node(tonut(n)) -end - -- the injectors local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger @@ -163,13 +157,13 @@ function states.finalize(namespace,attribute,head) -- is this one ok? if id == hlist_code or id == vlist_code then local content = getlist(head) if content then - local list = insert_node_before(content,content,copied(nsnone)) -- two return values + local list = insert_node_before(content,content,copy_node(nsnone)) -- two return values if list ~= content then setlist(head,list) end end else - head = insert_node_before(head,head,copied(nsnone)) + head = insert_node_before(head,head,copy_node(nsnone)) end return tonode(head), true, true end @@ -178,7 +172,7 @@ end -- we need to deal with literals too (reset as well as oval) -local function process(namespace,attribute,head,inheritance,default) -- one attribute +local function process(attribute,head,inheritance,default) -- one attribute local stack = head local done = false local check = false @@ -201,7 +195,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr if nstrigger and getattr(stack,nstrigger) then local outer = getattr(stack,attribute) if outer ~= inheritance then - local list, ok = process(namespace,attribute,content,inheritance,outer) + local list, ok = process(attribute,content,inheritance,outer) if content ~= list then setlist(stack,list) end @@ -209,7 +203,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr done = true end else - local list, ok = process(namespace,attribute,content,inheritance,default) + local list, ok = process(attribute,content,inheritance,default) if content ~= list then setlist(stack,list) end @@ -218,7 +212,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr end end else - local list, ok = process(namespace,attribute,content,inheritance,default) + local list, ok = process(attribute,content,inheritance,default) if content ~= list then setlist(stack,list) end @@ -237,12 +231,12 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr if c then if default and c == inheritance then if current ~= default then - head = insert_node_before(head,stack,copied(nsdata[default])) + head = insert_node_before(head,stack,copy_node(nsdata[default])) current = default done = true end elseif current ~= c then - head = insert_node_before(head,stack,copied(nsdata[c])) + head = insert_node_before(head,stack,copy_node(nsdata[c])) current = c done = true end @@ -259,7 +253,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr if nstrigger and getattr(stack,nstrigger) then local outer = getattr(stack,attribute) if outer ~= inheritance then - local list, ok = process(namespace,attribute,leader,inheritance,outer) + local list, ok = process(attribute,leader,inheritance,outer) if leader ~= list then setleader(stack,list) end @@ -267,7 +261,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr done = true end else - local list, ok = process(namespace,attribute,leader,inheritance,default) + local list, ok = process(attribute,leader,inheritance,default) if leader ~= list then setleader(stack,list) end @@ -276,7 +270,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr end end else - local list, ok = process(namespace,attribute,leader,inheritance,default) + local list, ok = process(attribute,leader,inheritance,default) if leader ~= list then setleader(stack,list) end @@ -290,12 +284,12 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr end elseif default and inheritance then if current ~= default then - head = insert_node_before(head,stack,copied(nsdata[default])) + head = insert_node_before(head,stack,copy_node(nsdata[default])) current = default done = true end elseif current > 0 then - head = insert_node_before(head,stack,copied(nsnone)) + head = insert_node_before(head,stack,copy_node(nsnone)) current = 0 done = true end @@ -307,7 +301,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr end states.process = function(namespace,attribute,head,default) - local head, done = process(namespace,attribute,tonut(head),default) + local head, done = process(attribute,tonut(head),default) return tonode(head), done end @@ -317,7 +311,8 @@ end -- state changes while the main state stays the same (like two glyphs following -- each other with the same color but different color spaces e.g. \showcolor) -local function selective(namespace,attribute,head,inheritance,default) -- two attributes +local function selective(attribute,head,inheritance,default) -- two attributes + -- local head = head local stack = head local done = false local check = false @@ -340,7 +335,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at if nstrigger and getattr(stack,nstrigger) then local outer = getattr(stack,attribute) if outer ~= inheritance then - local list, ok = selective(namespace,attribute,content,inheritance,outer) + local list, ok = selective(attribute,content,inheritance,outer) if content ~= list then setlist(stack,list) end @@ -348,7 +343,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at done = true end else - local list, ok = selective(namespace,attribute,content,inheritance,default) + local list, ok = selective(attribute,content,inheritance,default) if content ~= list then setlist(stack,list) end @@ -357,7 +352,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at end end else - local list, ok = selective(namespace,attribute,content,inheritance,default) + local list, ok = selective(attribute,content,inheritance,default) if content ~= list then setlist(stack,list) end @@ -377,7 +372,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at if default and c == inheritance then if current ~= default then local data = nsdata[default] - head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector])) + head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector])) current = default if ok then done = true @@ -385,9 +380,11 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at end else local s = getattr(stack,nsselector) + -- local s = nsforced or getattr(stack,nsselector) if current ~= c or current_selector ~= s then local data = nsdata[c] - head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector])) + head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector])) + -- head = insert_node_before(head,stack,copy_node(data[s or nsselector])) current = c current_selector = s if ok then @@ -400,7 +397,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at if nstrigger and getattr(stack,nstrigger) then local outer = getatribute(stack,attribute) if outer ~= inheritance then - local list, ok = selective(namespace,attribute,leader,inheritance,outer) + local list, ok = selective(attribute,leader,inheritance,outer) if leader ~= list then setleader(stack,list) end @@ -408,7 +405,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at done = true end else - local list, ok = selective(namespace,attribute,leader,inheritance,default) + local list, ok = selective(attribute,leader,inheritance,default) if leader ~= list then setleader(stack,list) end @@ -417,7 +414,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at end end else - local list, ok = selective(namespace,attribute,leader,inheritance,default) + local list, ok = selective(attribute,leader,inheritance,default) if leader ~= list then setleader(stack,list) end @@ -431,12 +428,12 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at elseif default and inheritance then if current ~= default then local data = nsdata[default] - head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector])) + head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector])) current = default done = true end elseif current > 0 then - head = insert_node_before(head,stack,copied(nsnone)) + head = insert_node_before(head,stack,copy_node(nsnone)) current, current_selector, done = 0, 0, true end check = false @@ -447,8 +444,8 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at end states.selective = function(namespace,attribute,head,default) - local head, done = selective(namespace,attribute,tonut(head),default) - return tonode(head), done + local head = selective(attribute,tonut(head),default) + return tonode(head), true end -- Ideally the next one should be merged with the previous but keeping it separate is @@ -460,7 +457,7 @@ end -- Todo: make a better stacker. Keep track (in attribute) about nesting level. Not -- entirely trivial and a generic solution is nicer (compares to the exporter). -local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise +local function stacked(attribute,head,default) -- no triggering, no inheritance, but list-wise local stack = head local done = false local current = default or 0 @@ -485,16 +482,16 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below local p = current current = a - head = insert_node_before(head,stack,copied(nsdata[a])) - local list = stacked(namespace,attribute,content,current) -- two return values + head = insert_node_before(head,stack,copy_node(nsdata[a])) + local list = stacked(attribute,content,current) -- two return values if content ~= list then setlist(stack,list) end - head, stack = insert_node_after(head,stack,copied(nsnone)) + head, stack = insert_node_after(head,stack,copy_node(nsnone)) current = p done = true else - local list, ok = stacked(namespace,attribute,content,current) + local list, ok = stacked(attribute,content,current) if content ~= list then setlist(stack,list) -- only if ok end @@ -503,7 +500,7 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in end end else - local list, ok = stacked(namespace,attribute,content,current) + local list, ok = stacked(attribute,content,current) if content ~= list then setlist(stack,list) -- only if ok end @@ -520,13 +517,13 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in local a = getattr(stack,attribute) if a then if current ~= a then - head = insert_node_before(head,stack,copied(nsdata[a])) + head = insert_node_before(head,stack,copy_node(nsdata[a])) depth = depth + 1 current = a done = true end if leader then - local list, ok = stacked(namespace,attribute,content,current) + local list, ok = stacked(attribute,content,current) if leader ~= list then setleader(stack,list) -- only if ok end @@ -538,7 +535,7 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in elseif default > 0 then -- elseif current > 0 then - head = insert_node_before(head,stack,copied(nsnone)) + head = insert_node_before(head,stack,copy_node(nsnone)) depth = depth - 1 current = 0 done = true @@ -548,20 +545,20 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in stack = getnext(stack) end while depth > 0 do - head = insert_node_after(head,stack,copied(nsnone)) + head = insert_node_after(head,stack,copy_node(nsnone)) depth = depth - 1 end return head, done end states.stacked = function(namespace,attribute,head,default) - local head, done = stacked(namespace,attribute,tonut(head),default) + local head, done = stacked(attribute,tonut(head),default) return tonode(head), done end -- experimental -local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise +local function stacker(attribute,head,default) -- no triggering, no inheritance, but list-wise -- nsbegin() local stacked = false @@ -589,15 +586,15 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in elseif nslistwise then local a = getattr(current,attribute) if a and attrib ~= a and nslistwise[a] then -- viewerlayer - head = insert_node_before(head,current,copied(nsdata[a])) - local list = stacker(namespace,attribute,content,a) + head = insert_node_before(head,current,copy_node(nsdata[a])) + local list = stacker(attribute,content,a) if list ~= content then setlist(current,list) end done = true - head, current = insert_node_after(head,current,copied(nsnone)) + head, current = insert_node_after(head,current,copy_node(nsnone)) else - local list, ok = stacker(namespace,attribute,content,attrib) + local list, ok = stacker(attribute,content,attrib) if content ~= list then setlist(current,list) end @@ -606,7 +603,7 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in end end else - local list, ok = stacker(namespace,attribute,content,default) + local list, ok = stacker(attribute,content,default) if list ~= content then setlist(current,list) end @@ -633,7 +630,7 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in done = true if leader then -- tricky as a leader has to be a list so we cannot inject before - local list, ok = stacker(namespace,attribute,leader,attrib) + local list, ok = stacker(attribute,leader,attrib) if ok then done = true end @@ -647,21 +644,19 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in current = getnext(current) end -if stacked then - - local n = nsend() - while n do - head = insert_node_after(head,previous,tonut(n)) - n = nsend() + if stacked then + local n = nsend() + while n do + head = insert_node_after(head,previous,tonut(n)) + n = nsend() + end end -end - return head, done end states.stacker = function(namespace,attribute,head,default) - local head, done = stacker(namespace,attribute,tonut(head),default) + local head, done = stacker(attribute,tonut(head),default) nsreset() return tonode(head), done end diff --git a/tex/context/base/mkiv/node-fnt.lua b/tex/context/base/mkiv/node-fnt.lua index 0f89e581f..c0d7eecc8 100644 --- a/tex/context/base/mkiv/node-fnt.lua +++ b/tex/context/base/mkiv/node-fnt.lua @@ -56,6 +56,7 @@ local getfield = nuts.getfield ----- getdisc = nuts.getdisc local setchar = nuts.setchar local setlink = nuts.setlink +local setnext = nuts.setnext local setfield = nuts.setfield local setprev = nuts.setprev @@ -71,6 +72,9 @@ local disc_code = nodecodes.disc local boundary_code = nodecodes.boundary local word_boundary = nodes.boundarycodes.word +local protect_glyphs = nuts.protect_glyphs +local unprotect_glyphs = nuts.unprotect_glyphs + local setmetatableindex = table.setmetatableindex -- some tests with using an array of dynamics[id] and processes[id] demonstrated @@ -212,8 +216,10 @@ function handlers.characters(head,groupcode,size,packtype,direction) local done = false local variants = nil local redundant = nil - local none = false local nuthead = tonut(head) + local lastfont = nil + local lastproc = nil + local lastnone = nil local a, u, b, r, e = 0, 0, 0, 0, 0 @@ -224,54 +230,91 @@ function handlers.characters(head,groupcode,size,packtype,direction) -- There is no gain in checking for a single glyph and then having a fast path. On the -- metafun manual (with some 2500 single char lists) the difference is just noise. + local function protectnone() + protect_glyphs(firstnone,lastnone) + firstnone = nil + end + + local function setnone(n) + if firstnone then + protectnone() + end + if basefont then + basefont[2] = getprev(n) + basefont = false + end + if not firstnone then + firstnone = n + end + lastnone = n + end + + local function setbase(n) + if firstnone then + protectnone() + end + if force_basepass then + if basefont then + basefont[2] = getprev(n) + end + b = b + 1 + basefont = { n, false } + basefonts[b] = basefont + end + end + + local function setnode(n,font,attr) -- we could use prevfont and prevattr when we set then first + if firstnone then + protectnone() + end + if basefont then + basefont[2] = getprev(n) + basefont = false + end + if attr > 0 then + local used = attrfonts[font] + if not used then + used = { } + attrfonts[font] = used + end + if not used[attr] then + local fd = setfontdynamics[font] + if fd then + used[attr] = fd[attr] + a = a + 1 + end + end + else + local used = usedfonts[font] + if not used then + lastfont = font + lastproc = fontprocesses[font] + if lastproc then + usedfonts[font] = lastproc + u = u + 1 + end + end + end + end + for n in traverse_char(nuthead) do local font = getfont(n) - local attr = (none and prevattr) or getattr(n,0) or 0 -- zero attribute is reserved for fonts in context + -- local attr = (none and prevattr) or getattr(n,0) or 0 -- zero attribute is reserved for fonts in context + local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context if font ~= prevfont or attr ~= prevattr then prevfont = font prevattr = attr variants = fontvariants[font] - none = fontmodes[font] == "none" - if none then - -- skip - -- variants = false - protect_glyph(n) + local fontmode = fontmodes[font] + if fontmode == "none" then + setnone(n) + elseif fontmode == "base" then + setbase(n) else - if basefont then - basefont[2] = getprev(n) - end - if attr > 0 then - local used = attrfonts[font] - if not used then - used = { } - attrfonts[font] = used - end - if not used[attr] then - local fd = setfontdynamics[font] - if fd then - used[attr] = fd[attr] - a = a + 1 - elseif force_basepass then - b = b + 1 - basefont = { n, false } - basefonts[b] = basefont - end - end - else - local used = usedfonts[font] - if not used then - local fp = fontprocesses[font] - if fp then - usedfonts[font] = fp - u = u + 1 - elseif force_basepass then - b = b + 1 - basefont = { n, false } - basefonts[b] = basefont - end - end - end + setnode(n,font,attr) end + elseif firstnone then + lastnone = n end if variants then local char = getchar(n) @@ -309,6 +352,10 @@ function handlers.characters(head,groupcode,size,packtype,direction) end end + if firstnone then + protectnone() + end + if force_boundaryrun then -- we can inject wordboundaries and then let the hyphenator do its work @@ -376,43 +423,29 @@ function handlers.characters(head,groupcode,size,packtype,direction) local none = false for n in traverse_char(r) do local font = getfont(n) - local attr = (none and prevattr) or getattr(n,0) or 0 -- zero attribute is reserved for fonts in context + local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context if font ~= prevfont or attr ~= prevattr then prevfont = font prevattr = attr - none = fontmodes[font] == "none" -- very unlikely that we run into disc nodes in none mode - if none then - -- skip - -- variants = false - protect_glyph(n) - elseif attr > 0 then - local used = attrfonts[font] - if not used then - used = { } - attrfonts[font] = used - end - if not used[attr] then - local fd = setfontdynamics[font] - if fd then - used[attr] = fd[attr] - a = a + 1 - end - end + local fontmode = fontmodes[font] + if fontmode == "none" then + setnone(n) + elseif fontmode == "base" then + setbase(n) else - local used = usedfonts[font] - if not used then - local fp = fontprocesses[font] - if fp then - usedfonts[font] = fp - u = u + 1 - end - end + setnode(n,font,attr) end + elseif firstnone then + -- lastnone = n + lastnone = nil end -- we assume one font for now (and if there are more and we get into issues then -- we can always remove the break) break end + if firstnone then + protectnone() + end elseif expanders then local subtype = getsubtype(d) if subtype == automatic_code or subtype == explicit_code then @@ -432,11 +465,9 @@ function handlers.characters(head,groupcode,size,packtype,direction) if u == 0 then -- skip elseif u == 1 then - local font, processors = next(usedfonts) - -- local attr = a == 0 and false or 0 -- 0 is the savest way local attr = a > 0 and 0 or false -- 0 is the savest way - for i=1,#processors do - local h, d = processors[i](head,font,attr,direction) + for i=1,#lastproc do + local h, d = lastproc[i](head,lastfont,attr,direction) if d then if h then head = h @@ -549,19 +580,5 @@ function handlers.characters(head,groupcode,size,packtype,direction) return head, true end -local d_protect_glyphs = nuts.protect_glyphs -local d_unprotect_glyphs = nuts.unprotect_glyphs - -handlers.protectglyphs = function(n) return d_protect_glyphs (tonut(n)) end -handlers.unprotectglyphs = function(n) return d_unprotect_glyphs(tonut(n)) end - --- function handlers.protectglyphs(h) --- local h = tonut(h) --- for n in traverse_id(disc_code,h) do --- local pre, post, replace = getdisc(n) --- if pre then d_protect_glyphs(pre) end --- if post then d_protect_glyphs(post) end --- if replace then d_protect_glyphs(replace) end --- end --- return d_protect_glyphs(h) --- end +handlers.protectglyphs = function(n) protect_glyphs (tonut(n)) return n, true end +handlers.unprotectglyphs = function(n) unprotect_glyphs(tonut(n)) return n, true end diff --git a/tex/context/base/mkiv/node-ini.mkiv b/tex/context/base/mkiv/node-ini.mkiv index 369b06ab2..0f5a2df7e 100644 --- a/tex/context/base/mkiv/node-ini.mkiv +++ b/tex/context/base/mkiv/node-ini.mkiv @@ -36,6 +36,7 @@ %registerctxluafile{node-prp}{1.001} % makes no sense (yet) \registerctxluafile{node-ppt}{1.001} \registerctxluafile{node-scn}{1.001} +\registerctxluafile{node-syn}{1.001} \newcount\c_node_tracers_show_box % box number diff --git a/tex/context/base/mkiv/node-met.lua b/tex/context/base/mkiv/node-met.lua index e6d0f4689..2686aa990 100644 --- a/tex/context/base/mkiv/node-met.lua +++ b/tex/context/base/mkiv/node-met.lua @@ -110,8 +110,9 @@ nodes.set_attribute = node.set_attribute nodes.find_attribute = node.find_attribute nodes.unset_attribute = node.unset_attribute -nodes.protect_glyphs = node.protect_glyphs nodes.protect_glyph = node.protect_glyph +nodes.protect_glyphs = node.protect_glyphs +nodes.unprotect_glyph = node.unprotect_glyph nodes.unprotect_glyphs = node.unprotect_glyphs nodes.kerning = node.kerning nodes.ligaturing = node.ligaturing @@ -669,3 +670,46 @@ end nodes.keys = keys -- [id][subtype] nodes.fields = nodefields -- (n) + +-- for the moment (pre 6380) + +if not nodes.unprotect_glyph then + + local protect_glyph = nodes.protect_glyph + local protect_glyphs = nodes.protect_glyphs + local unprotect_glyph = nodes.unprotect_glyph + local unprotect_glyphs = nodes.unprotect_glyphs + + local getnext = nodes.getnext + local setnext = nodes.setnext + + function nodes.protectglyphs(first,last) + if first == last then + return protect_glyph(first) + elseif last then + local nxt = getnext(last) + setnext(last) + local f, b = protect_glyphs(first) + setnext(last,nxt) + return f, b + else + return protect_glyphs(first) + end + end + + function nodes.unprotectglyphs(first,last) + if first == last then + return unprotect_glyph(first) + elseif last then + local nxt = getnext(last) + setnext(last) + local f, b = unprotect_glyphs(first) + setnext(last,nxt) + return f, b + else + return unprotect_glyphs(first) + end + end + +end + diff --git a/tex/context/base/mkiv/node-nut.lua b/tex/context/base/mkiv/node-nut.lua index 787afd888..87df5d5b2 100644 --- a/tex/context/base/mkiv/node-nut.lua +++ b/tex/context/base/mkiv/node-nut.lua @@ -283,8 +283,9 @@ nuts.has_attribute = direct.has_attribute nuts.set_attribute = direct.set_attribute nuts.unset_attribute = direct.unset_attribute -nuts.protect_glyphs = direct.protect_glyphs nuts.protect_glyph = direct.protect_glyph +nuts.protect_glyphs = direct.protect_glyphs +nuts.unprotect_glyph = direct.unprotect_glyph nuts.unprotect_glyphs = direct.unprotect_glyphs nuts.ligaturing = direct.ligaturing nuts.kerning = direct.kerning @@ -409,18 +410,32 @@ local d_setlink = direct.setlink local d_setboth = direct.setboth local d_getboth = direct.getboth +-- local function remove(head,current,free_too) +-- local t = current +-- head, current = d_remove_node(head,current) +-- if not t then +-- -- forget about it +-- elseif free_too then +-- d_flush_node(t) +-- t = nil +-- else +-- d_setboth(t) -- (t,nil,nil) +-- end +-- return head, current, t +-- end + local function remove(head,current,free_too) - local t = current - head, current = d_remove_node(head,current) - if not t then - -- forget about it - elseif free_too then - d_flush_node(t) - t = nil - else - d_setboth(t) -- (t,nil,nil) + if current then + local h, c = d_remove_node(head,current) + if free_too then + d_flush_node(current) + return h, c + else + d_setboth(current) + return h, c, current + end end - return head, current, t + return head, current end -- alias @@ -902,3 +917,45 @@ if not nuts.uses_font then end +-- for the moment (pre 6380) + +if not nuts.unprotect_glyph then + + local protect_glyph = nuts.protect_glyph + local protect_glyphs = nuts.protect_glyphs + local unprotect_glyph = nuts.unprotect_glyph + local unprotect_glyphs = nuts.unprotect_glyphs + + local getnext = nuts.getnext + local setnext = nuts.setnext + + function nuts.protectglyphs(first,last) + if first == last then + return protect_glyph(first) + elseif last then + local nxt = getnext(last) + setnext(last) + local f, b = protect_glyphs(first) + setnext(last,nxt) + return f, b + else + return protect_glyphs(first) + end + end + + function nuts.unprotectglyphs(first,last) + if first == last then + return unprotect_glyph(first) + elseif last then + local nxt = getnext(last) + setnext(last) + local f, b = unprotect_glyphs(first) + setnext(last,nxt) + return f, b + else + return unprotect_glyphs(first) + end + end + +end + diff --git a/tex/context/base/mkiv/node-ref.lua b/tex/context/base/mkiv/node-ref.lua index b313a00b6..f2f660e67 100644 --- a/tex/context/base/mkiv/node-ref.lua +++ b/tex/context/base/mkiv/node-ref.lua @@ -371,61 +371,30 @@ end -- we need to do vlists differently local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,txtdir) -- main - if head then - local current, first, last, firstdir, reference = head, nil, nil, nil, nil - pardir = pardir or "===" - txtdir = txtdir or "===" - while current do - local id = getid(current) - if id == hlist_code or id == vlist_code then - local r = getattr(current,attribute) - -- test \goto{test}[page(2)] test \gotobox{test}[page(2)] - -- test \goto{\TeX}[page(2)] test \gotobox{\hbox {x} \hbox {x}}[page(2)] - -- if r and (not skip or r >) skip then -- maybe no > test - -- inject_list(id,current,r,make,stack,pardir,txtdir) - -- end - if r then - if not reference then - reference, first, last, firstdir = r, current, current, txtdir - elseif r == reference then - -- same link - last = current - elseif (done[reference] or 0) == 0 then - if not skip or r > skip then -- maybe no > test - head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) - reference, first, last, firstdir = nil, nil, nil, nil - end - else - reference, first, last, firstdir = r, current, current, txtdir - end - done[r] = (done[r] or 0) + 1 - end - local list = getlist(current) - if list then - local h, ok - h, ok , pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) - if h ~= current then - setlist(current,h) - end - end - if r then - done[r] = done[r] - 1 - end - elseif id == dir_code then - txtdir = getdir(current) - elseif id == localpar_code then - pardir = getdir(current) - elseif id == glue_code and getsubtype(current) == leftskip_code then -- any glue at the left? - -- - else - local r = getattr(current,attribute) - if not r then - -- just go on, can be kerns - elseif not reference then + local first, last, firstdir, reference + if not pardir then + pardir = "===" + end + if not texdir then + txtdir = "===" + end + local current = head + while current do + local id = getid(current) + if id == hlist_code or id == vlist_code then + local r = getattr(current,attribute) + -- test \goto{test}[page(2)] test \gotobox{test}[page(2)] + -- test \goto{\TeX}[page(2)] test \gotobox{\hbox {x} \hbox {x}}[page(2)] + -- if r and (not skip or r >) skip then -- maybe no > test + -- inject_list(id,current,r,make,stack,pardir,txtdir) + -- end + if r then + if not reference then reference, first, last, firstdir = r, current, current, txtdir elseif r == reference then + -- same link last = current - elseif (done[reference] or 0) == 0 then -- or id == glue_code and getsubtype(current) == right_skip_code + elseif (done[reference] or 0) == 0 then if not skip or r > skip then -- maybe no > test head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) reference, first, last, firstdir = nil, nil, nil, nil @@ -433,53 +402,89 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx else reference, first, last, firstdir = r, current, current, txtdir end + done[r] = (done[r] or 0) + 1 end - current = getnext(current) - end - if reference and (done[reference] or 0) == 0 then - head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) - end - end - return head, true, pardir, txtdir -end - -local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular ! - if head then - pardir = pardir or "===" - txtdir = txtdir or "===" - local current = head - while current do - local id = getid(current) - if id == hlist_code or id == vlist_code then - local r = getattr(current,attribute) - if r and not done[r] then - done[r] = true - inject_list(id,current,r,make,stack,pardir,txtdir) + local list = getlist(current) + if list then + local h, ok + h, ok, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) + if h ~= current then + setlist(current,h) end - local list = getlist(current) - if list then - local h = inject_area(list,attribute,make,stack,done,current,pardir,txtdir) - if h ~= current then - setlist(current,h) - end + end + if r then + done[r] = done[r] - 1 + end + elseif id == glue_code and getsubtype(current) == leftskip_code then -- any glue at the left? + -- + elseif id == dir_code then + txtdir = getdir(current) + elseif id == localpar_code then -- only test at begin + pardir = getdir(current) + else + local r = getattr(current,attribute) + if not r then + -- just go on, can be kerns + elseif not reference then + reference, first, last, firstdir = r, current, current, txtdir + elseif r == reference then + last = current + elseif (done[reference] or 0) == 0 then -- or id == glue_code and getsubtype(current) == right_skip_code + if not skip or r > skip then -- maybe no > test + head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) + reference, first, last, firstdir = nil, nil, nil, nil end - elseif id == dir_code then - txtdir = getdir(current) - elseif id == localpar_code then - pardir = getdir(current) else - local r = getattr(current,attribute) - if r and not done[r] then - done[r] = true - head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir) - end + reference, first, last, firstdir = r, current, current, txtdir end - current = getnext(current) end + current = getnext(current) end - return head, true + if reference and (done[reference] or 0) == 0 then + head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) + end + return head, true, pardir, txtdir end +-- local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular ! +-- if not pardir then +-- pardir = "===" +-- end +-- if not texdir then +-- txtdir = "===" +-- end +-- local current = head +-- while current do +-- local id = getid(current) +-- if id == hlist_code or id == vlist_code then +-- local r = getattr(current,attribute) +-- if r and not done[r] then +-- done[r] = true +-- inject_list(id,current,r,make,stack,pardir,txtdir) +-- end +-- local list = getlist(current) +-- if list then +-- local h = inject_area(list,attribute,make,stack,done,current,pardir,txtdir) +-- if h ~= current then +-- setlist(current,h) +-- end +-- end +-- elseif id == dir_code then +-- txtdir = getdir(current) +-- elseif id == localpar_code then +-- pardir = getdir(current) +-- else +-- local r = getattr(current,attribute) +-- if r and not done[r] then +-- done[r] = true +-- head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir) +-- end +-- end +-- current = getnext(current) +-- end +-- return head, true +-- end + -- tracing: todo: use predefined colors local register_color = colors.register @@ -669,17 +674,9 @@ local function makereference(width,height,depth,reference) -- height and depth a end end --- function nodes.references.handler(head) --- if topofstack > 0 then --- return inject_areas(head,attribute,makereference,stack,done) --- else --- return head, false --- end --- end - function nodes.references.handler(head) - if topofstack > 0 then - head = tonut(head) + if head and topofstack > 0 then + local head = tonut(head) local head, done = inject_areas(head,attribute,makereference,stack,done) return tonode(head), done else @@ -791,7 +788,7 @@ local function makedestination(width,height,depth,reference) end -- function nodes.destinations.handler(head) --- if topofstack > 0 then +-- if head and topofstack > 0 then -- return inject_area(head,attribute,makedestination,stack,done) -- singular -- else -- return head, false @@ -799,8 +796,8 @@ end -- end function nodes.destinations.handler(head) - if topofstack > 0 then - head = tonut(head) + if head and topofstack > 0 then + local head = tonut(head) local head, done = inject_areas(head,attribute,makedestination,stack,done) return tonode(head), done else @@ -808,7 +805,6 @@ function nodes.destinations.handler(head) end end - -- will move function references.mark(reference,h,d,view) diff --git a/tex/context/base/mkiv/node-syn.lua b/tex/context/base/mkiv/node-syn.lua index bd8cc7964..1c070c45f 100644 --- a/tex/context/base/mkiv/node-syn.lua +++ b/tex/context/base/mkiv/node-syn.lua @@ -69,6 +69,7 @@ local kerncodes = nodes.kerncodes local glyph_code = nodecodes.glyph local disc_code = nodecodes.disc local glue_code = nodecodes.glue +local penalty_code = nodecodes.penalty local kern_code = nodecodes.kern ----- rule_code = nodecodes.rule local hlist_code = nodecodes.hlist @@ -81,7 +82,7 @@ local insert_after = nuts.insert_after local nodepool = nuts.pool local new_latelua = nodepool.latelua local new_rule = nodepool.rule -local new_hlist = nodepool.hlist +local new_kern = nodepool.kern local getdimensions = nuts.dimensions local getrangedimensions = nuts.rangedimensions @@ -321,7 +322,8 @@ local function inject(head,first,last,tag,line) d = depth end if trace then - head = insert_before(head,first,new_hlist(new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth))) + head = insert_before(head,first,new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth)) + head = insert_before(head,first,new_kern(-w)) end head = x_hlist(head,first,tag,line,w,h,d) return head @@ -389,7 +391,8 @@ local function inject(parent,head,first,last,tag,line) d = depth end if trace then - head = insert_before(head,first,new_hlist(new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth))) + head = insert_before(head,first,new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth)) + head = insert_before(head,first,new_kern(-w)) end head = x_hlist(head,first,tag,line,w,h,d) return head @@ -451,6 +454,8 @@ local function collect_max(head,parent) break end id = nil -- so no test later on + elseif id == penalty_code then + -- go on (and be nice for math) else if tag > 0 then head = inject(parent,head,first,last,tag,line) @@ -516,16 +521,28 @@ function synctex.stop() end end +local enablers = { } +local disablers = { } + +function synctex.registerenabler(f) + enablers[#enablers+1] = f +end +function synctex.registerdisabler(f) + disablers[#disablers+1] = f +end + function synctex.enable() if not enabled then enabled = true set_synctex_mode(3) -- we want details if not used then - directives.enable("system.synctex.xml") nodes.tasks.appendaction("shipouts", "after", "luatex.synctex.collect") report_system("synctex functionality is enabled, expect 5-10 pct runtime overhead!") used = true end + for i=1,#enablers do + enablers[i](true) + end end end @@ -534,6 +551,9 @@ function synctex.disable() set_synctex_mode(0) report_system("synctex functionality is disabled!") enabled = false + for i=1,#disablers do + disablers[i](false) + end end end @@ -563,14 +583,6 @@ end luatex.registerstopactions(synctex.finish) -directives.register("system.synctex", function(v) - if v then - synctex.enable() - else - synctex.disable() - end -end) - statistics.register("synctex tracing",function() if used then return string.format("%i referenced files, %i files ignored, %i objects flushed, logfile: %s", @@ -595,26 +607,28 @@ interfaces.implement { actions = synctex.resetfilename, } +function synctex.setup(t) + if t.method == interfaces.variables.max then + collect = collect_max + else + collect = collect_min + end + if t.state == interfaces.variables.start then + synctex.enable() + else + synctex.disable() + end +end + interfaces.implement { name = "setupsynctex", + actions = synctex.setup, arguments = { { { "state" }, { "method" }, }, }, - actions = function(t) - if t.method == interfaces.variables.max then - collect = collect_max - else - collect = collect_min - end - if t.state == interfaces.variables.start then - synctex.enable() - else - synctex.disable() - end - end } interfaces.implement { diff --git a/tex/context/base/mkiv/spac-chr.lua b/tex/context/base/mkiv/spac-chr.lua index fe402ed87..b8a53d2a1 100644 --- a/tex/context/base/mkiv/spac-chr.lua +++ b/tex/context/base/mkiv/spac-chr.lua @@ -48,6 +48,7 @@ local insert_node_before = nuts.insert_before local insert_node_after = nuts.insert_after local remove_node = nuts.remove local traverse_id = nuts.traverse_id +local traverse_char = nuts.traverse_char local tasks = nodes.tasks @@ -148,16 +149,37 @@ function characters.replacenbsp(head,original) return head, current end +-- function characters.replacenbspaces(head) +-- for current in traverse_id(glyph_code,head) do +-- if getchar(current) == 0x00A0 then +-- local h = nbsp(head,current) +-- if h then +-- head = remove_node(h,current,true) +-- end +-- end +-- end +-- return head +-- end + function characters.replacenbspaces(head) - for current in traverse_id(glyph_code,head) do + local head = tonut(head) + local wipe = false + for current in traverse_id(glyph_code,head) do -- can be anytiem so no traverse_char if getchar(current) == 0x00A0 then + if wipe then + head = remove_node(h,current,true) + wipe = false + end local h = nbsp(head,current) if h then - head = remove_node(h,current,true) + wipe = current end end end - return head + if wipe then + head = remove_node(h,current,true) + end + return tonode(head) end -- This initialization might move someplace else if we need more of it. The problem is that @@ -283,29 +305,60 @@ local methods = { characters.methods = methods -function characters.handler(head) -- todo: use traverse_id - head = tonut(head) - local current = head - local done = false - while current do - local char, id = isglyph(current) +-- function characters.handler(head) -- todo: use traverse_id +-- head = tonut(head) +-- local current = head +-- local done = false +-- while current do +-- local char, id = isglyph(current) +-- if char then +-- local next = getnext(current) +-- local method = methods[char] +-- if method then +-- if trace_characters then +-- report_characters("replacing character %C, description %a",char,lower(chardata[char].description)) +-- end +-- local h = method(head,current) +-- if h then +-- head = remove_node(h,current,true) +-- end +-- done = true +-- end +-- current = next +-- else +-- current = getnext(current) +-- end +-- end +-- return tonode(head), done +-- end + +-- for current, char, font in traverse_char_data(head) will save 0.015 on a 300 page doc + +function characters.handler(head) + local head = tonut(head) + local wipe = false + for current in traverse_char(head) do + local char = getchar(current) if char then - local next = getnext(current) local method = methods[char] if method then + if wipe then + head = remove_node(head,wipe,true) + wipe = false + end if trace_characters then report_characters("replacing character %C, description %a",char,lower(chardata[char].description)) end local h = method(head,current) if h then - head = remove_node(h,current,true) + wipe = current end done = true end - current = next - else - current = getnext(current) end end + if wipe then + head = remove_node(head,wipe,true) + end return tonode(head), done end diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf index 72baf08cc..83c6b914d 100644 Binary files a/tex/context/base/mkiv/status-files.pdf and b/tex/context/base/mkiv/status-files.pdf differ diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf index b33293a23..9950ca3a6 100644 Binary files a/tex/context/base/mkiv/status-lua.pdf and b/tex/context/base/mkiv/status-lua.pdf differ diff --git a/tex/context/base/mkiv/strc-con.mkvi b/tex/context/base/mkiv/strc-con.mkvi index 18ce17355..522026556 100644 --- a/tex/context/base/mkiv/strc-con.mkvi +++ b/tex/context/base/mkiv/strc-con.mkvi @@ -73,6 +73,8 @@ \c!display=\v!yes, \c!width=7\emwidth, \c!distance=\emwidth, + \c!stretch=.50, + \c!shrink=.25, \c!titledistance=.5\emwidth, %c!hang=, %c!sample=, @@ -410,8 +412,12 @@ \installcommandhandler \??constructionalternative {constructionalternative} \??constructionalternative \setupconstructionalternative - [\c!width=\constructionparameter\c!width, - \c!distance=\constructionparameter\c!distance] + [%c!left=, % no inheritance + %c!right=,% no inheritance + \c!width=\constructionparameter\c!width, + \c!distance=\constructionparameter\c!distance, + \c!stretch=\constructionparameter\c!stretch, + \c!shrink=\constructionparameter\c!shrink] \defineconstructionalternative [\v!left] @@ -463,7 +469,7 @@ \defineconstructionalternative [\v!hanging] - [%\c!width=\v!fit, + [%\c!width=.75\emwidth \c!renderingsetup=\??constructionrenderings:\v!hanging] \defineconstructionalternative @@ -778,7 +784,10 @@ \flushconstructionheadbox \ifconditional\c_strc_constructions_distance_none \else \nobreak - \hskip\constructionsheaddistance \s!plus .5\constructionsheaddistance \s!minus .25\constructionsheaddistance\relax + \hskip\constructionsheaddistance + \s!plus \constructionalternativeparameter\c!stretch\constructionsheaddistance + \s!minus\constructionalternativeparameter\c!shrink \constructionsheaddistance + \relax \fi \useconstructionstyleandcolor\c!style\c!color \ignorespaces @@ -822,9 +831,9 @@ \ifconditional\c_strc_constructions_distance_none \else % we used to have just a kern, but long lines look bad then \scratchdistance\ifdim\constructionsheaddistance=\zeropoint .75\emwidth\else\constructionsheaddistance\fi - \hskip \scratchdistance - \s!plus .25\scratchdistance - \s!minus.25\scratchdistance + \hskip \scratchdistance + \s!plus \constructionalternativeparameter\c!stretch\scratchdistance + \s!minus\constructionalternativeparameter\c!shrink \scratchdistance \fi \ifhmode \ifx\p_strc_constructions_width\v!line diff --git a/tex/context/base/mkiv/supp-box.lua b/tex/context/base/mkiv/supp-box.lua index b9bf0ccf0..fc206b6d2 100644 --- a/tex/context/base/mkiv/supp-box.lua +++ b/tex/context/base/mkiv/supp-box.lua @@ -350,6 +350,7 @@ implement { local result = new_hlist() setlist(result,head) setbox(target,result) + -- setbox(target,new_hlist(head)) end } diff --git a/tex/context/base/mkiv/trac-vis.lua b/tex/context/base/mkiv/trac-vis.lua index d58114d71..38bad03e2 100644 --- a/tex/context/base/mkiv/trac-vis.lua +++ b/tex/context/base/mkiv/trac-vis.lua @@ -6,11 +6,11 @@ if not modules then modules = { } end modules ['trac-vis'] = { license = "see context related readme files" } -local string, number, table = string, number, table local node, nodes, attributes, fonts, tex = node, nodes, attributes, fonts, tex local type = type local gmatch = string.gmatch local formatters = string.formatters +local compactfloat = number.compactfloat -- This module started out in the early days of mkiv and luatex with -- visualizing kerns related to fonts. In the process of cleaning up the @@ -166,6 +166,7 @@ local trace_user local trace_math local trace_italic local trace_discretionary +local trace_expansion local report_visualize = logs.reporter("visualize") @@ -190,10 +191,11 @@ local modes = { italic = 8192, origin = 16384, discretionary = 32768, + expansion = 65536, } local usedfont, exheight, emwidth -local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_italic, l_origin, l_discretionary +local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_italic, l_origin, l_discretionary, l_expansion local enabled = false local layers = { } @@ -243,6 +245,7 @@ local function enable() l_italic = layers.italic l_origin = layers.origin l_discretionary = layers.discretionary + l_expansion = layers.expansion enableaction("shipouts","nodes.visualizers.handler") report_visualize("enabled") enabled = true @@ -355,6 +358,7 @@ local c_white = "trace:w" local c_math = "trace:r" local c_origin = "trace:o" local c_discretionary = "trace:o" +local c_expansion = "trace:o" local c_positive_d = "trace:db" local c_negative_d = "trace:dr" @@ -369,6 +373,7 @@ local c_white_d = "trace:dw" local c_math_d = "trace:dr" local c_origin_d = "trace:do" local c_discretionary_d = "trace:do" +local c_expansion_d = "trace:do" local function sometext(str,layer,color,textcolor,lap) -- we can just paste verbatim together .. no typesteting needed local text = hpack_string(str,usedfont) @@ -435,6 +440,78 @@ local fontkern do end +local glyphexpansion do + + local f_cache = caches["glyphexpansion"] + + glyphexpansion = function(head,current) + local extra = getfield(current,"expansion_factor") + if extra ~= 0 then + extra = extra / 1000 + local info = f_cache[extra] + if info then + -- print("hit fontkern") + else + local text = hpack_string(compactfloat(extra,"%.1f"),usedfont) + local rule = new_rule(emwidth/fraction,exheight,2*exheight) + local list = getlist(text) + if extra > 0 then + setlistcolor(list,c_positive_d) + elseif extra < 0 then + setlistcolor(list,c_negative_d) + end + setlisttransparency(list,c_text_d) + setcolor(rule,c_text_d) + settransparency(rule,c_text_d) + setshift(text,1.5 * exheight) + info = new_hlist(setlink(rule,text)) + setattr(info,a_layer,l_expansion) + f_cache[extra] = info + end + head = insert_node_before(head,current,copy_list(info)) + return head, current + end + return head, current + end + +end + +local kernexpansion do + + local f_cache = caches["kernexpansion"] + + kernexpansion = function(head,current) + local extra = getfield(current,"expansion_factor") + if extra ~= 0 then + extra = extra / 1000 + local info = f_cache[extra] + if info then + -- print("hit fontkern") + else + local text = hpack_string(compactfloat(extra,"%.1f"),usedfont) + local rule = new_rule(emwidth/fraction,exheight,4*exheight) + local list = getlist(text) + if extra > 0 then + setlistcolor(list,c_positive_d) + elseif extra < 0 then + setlistcolor(list,c_negative_d) + end + setlisttransparency(list,c_text_d) + setcolor(rule,c_text_d) + settransparency(rule,c_text_d) + setshift(text,3.5 * exheight) + info = new_hlist(setlink(rule,text)) + setattr(info,a_layer,l_expansion) + f_cache[extra] = info + end + head = insert_node_before(head,current,copy_list(info)) + return head, current + end + return head, current + end + +end + local whatsit do local w_cache = caches["whatsit"] @@ -929,151 +1006,161 @@ local ruledpenalty do end -local function visualize(head,vertical,forced,parent) - local trace_hbox = false - local trace_vbox = false - local trace_vtop = false - local trace_kern = false - local trace_glue = false - local trace_penalty = false - local trace_fontkern = false - local trace_strut = false - local trace_whatsit = false - local trace_glyph = false - local trace_simple = false - local trace_user = false - local trace_math = false - local trace_italic = false - local trace_origin = false - local current = head - local previous = nil - local attr = unsetvalue - local prev_trace_fontkern = nil - while current do - local id = getid(current) - local a = forced or getattr(current,a_visual) or unsetvalue - if a ~= attr then - prev_trace_fontkern = trace_fontkern - if a == unsetvalue then - trace_hbox = false - trace_vbox = false - trace_vtop = false - trace_kern = false - trace_glue = false - trace_penalty = false - trace_fontkern = false - trace_strut = false - trace_whatsit = false - trace_glyph = false - trace_simple = false - trace_user = false - trace_math = false - trace_italic = false - trace_origin = false - trace_discretionary = false - else -- dead slow: - trace_hbox = hasbit(a, 1) - trace_vbox = hasbit(a, 2) - trace_vtop = hasbit(a, 4) - trace_kern = hasbit(a, 8) - trace_glue = hasbit(a, 16) - trace_penalty = hasbit(a, 32) - trace_fontkern = hasbit(a, 64) - trace_strut = hasbit(a, 128) - trace_whatsit = hasbit(a, 256) - trace_glyph = hasbit(a, 512) - trace_simple = hasbit(a, 1024) - trace_user = hasbit(a, 2048) - trace_math = hasbit(a, 4096) - trace_italic = hasbit(a, 8192) - trace_origin = hasbit(a,16384) - trace_discretionary = hasbit(a,32768) - end - attr = a - end - if trace_strut then - setattr(current,a_layer,l_strut) - elseif id == glyph_code then - if trace_glyph then - head, current = ruledglyph(head,current,previous) - end - elseif id == disc_code then - if trace_discretionary then - head, current = ruleddiscretionary(head,current) - end - local pre, post, replace = getdisc(current) - if pre then - pre = visualize(pre,false,a,parent) - end - if post then - post = visualize(post,false,a,parent) - end - if replace then - replace = visualize(replace,false,a,parent) +do + + local function visualize(head,vertical,forced,parent) + local trace_hbox = false + local trace_vbox = false + local trace_vtop = false + local trace_kern = false + local trace_glue = false + local trace_penalty = false + local trace_fontkern = false + local trace_strut = false + local trace_whatsit = false + local trace_glyph = false + local trace_simple = false + local trace_user = false + local trace_math = false + local trace_italic = false + local trace_origin = false + local current = head + local previous = nil + local attr = unsetvalue + local prev_trace_fontkern = nil + local prev_trace_expansion = nil + while current do + local id = getid(current) + local a = forced or getattr(current,a_visual) or unsetvalue + if a ~= attr then + prev_trace_fontkern = trace_fontkern + prev_trace_expansion = trace_expansion + if a == unsetvalue then + trace_hbox = false + trace_vbox = false + trace_vtop = false + trace_kern = false + trace_glue = false + trace_penalty = false + trace_fontkern = false + trace_strut = false + trace_whatsit = false + trace_glyph = false + trace_simple = false + trace_user = false + trace_math = false + trace_italic = false + trace_origin = false + trace_discretionary = false + trace_expansion = false + else -- dead slow: + trace_hbox = hasbit(a, 1) + trace_vbox = hasbit(a, 2) + trace_vtop = hasbit(a, 4) + trace_kern = hasbit(a, 8) + trace_glue = hasbit(a, 16) + trace_penalty = hasbit(a, 32) + trace_fontkern = hasbit(a, 64) + trace_strut = hasbit(a, 128) + trace_whatsit = hasbit(a, 256) + trace_glyph = hasbit(a, 512) + trace_simple = hasbit(a, 1024) + trace_user = hasbit(a, 2048) + trace_math = hasbit(a, 4096) + trace_italic = hasbit(a, 8192) + trace_origin = hasbit(a,16384) + trace_discretionary = hasbit(a,32768) + trace_expansion = hasbit(a,65536) + end + attr = a end - setdisc(current,pre,post,replace) - elseif id == kern_code then - local subtype = getsubtype(current) - if subtype == font_kern_code then - if trace_fontkern or prev_trace_fontkern then - head, current = fontkern(head,current) + if trace_strut then + setattr(current,a_layer,l_strut) + elseif id == glyph_code then + if trace_glyph then + head, current = ruledglyph(head,current,previous) end - else -- if subtype == user_kern_code then - if trace_italic then - head, current = ruleditalic(head,current) - elseif trace_kern then - head, current = ruledkern(head,current,vertical) + if trace_expansion then + head, current = glyphexpansion(head,current) + end + elseif id == disc_code then + if trace_discretionary then + head, current = ruleddiscretionary(head,current) + end + local pre, post, replace = getdisc(current) + if pre then + pre = visualize(pre,false,a,parent) + end + if post then + post = visualize(post,false,a,parent) + end + if replace then + replace = visualize(replace,false,a,parent) + end + setdisc(current,pre,post,replace) + elseif id == kern_code then + local subtype = getsubtype(current) + if subtype == font_kern_code then + if trace_fontkern or prev_trace_fontkern then + head, current = fontkern(head,current) + end + if trace_expansion or prev_trace_expansion then + head, current = kernexpansion(head,current) + end + else -- if subtype == user_kern_code then + if trace_italic then + head, current = ruleditalic(head,current) + elseif trace_kern then + head, current = ruledkern(head,current,vertical) + end + end + elseif id == glue_code then + local content = getleader(current) + if content then + setleader(current,visualize(content,false,nil,parent)) + elseif trace_glue then + head, current = ruledglue(head,current,vertical,parent) + end + elseif id == penalty_code then + if trace_penalty then + head, current = ruledpenalty(head,current,vertical) + end + elseif id == hlist_code then + local content = getlist(current) + if content then + setlist(current,visualize(content,false,nil,current)) + end + if trace_hbox then + head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent) + end + elseif id == vlist_code then + local content = getlist(current) + if content then + setlist(current,visualize(content,true,nil,current)) + end + if trace_vtop then + head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple,previous,trace_origin,parent) + elseif trace_vbox then + head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple,previous,trace_origin,parent) + end + elseif id == whatsit_code then + if trace_whatsit then + head, current = whatsit(head,current) + end + elseif id == user_code then + if trace_user then + head, current = user(head,current) + end + elseif id == math_code then + if trace_math then + head, current = math(head,current) end end - elseif id == glue_code then - local content = getleader(current) - if content then - setleader(current,visualize(content,false,nil,parent)) - elseif trace_glue then - head, current = ruledglue(head,current,vertical,parent) - end - elseif id == penalty_code then - if trace_penalty then - head, current = ruledpenalty(head,current,vertical) - end - elseif id == hlist_code then - local content = getlist(current) - if content then - setlist(current,visualize(content,false,nil,current)) - end - if trace_hbox then - head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent) - end - elseif id == vlist_code then - local content = getlist(current) - if content then - setlist(current,visualize(content,true,nil,current)) - end - if trace_vtop then - head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple,previous,trace_origin,parent) - elseif trace_vbox then - head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple,previous,trace_origin,parent) - end - elseif id == whatsit_code then - if trace_whatsit then - head, current = whatsit(head,current) - end - elseif id == user_code then - if trace_user then - head, current = user(head,current) - end - elseif id == math_code then - if trace_math then - head, current = math(head,current) - end + previous = current + current = getnext(current) end - previous = current - current = getnext(current) + return head end - return head -end - -do local function cleanup() for tag, cache in next, caches do @@ -1101,20 +1188,20 @@ do luatex.registerstopactions(cleanup) -end - -function visualizers.box(n) - if usedfont then - starttiming(visualizers) - local box = getbox(n) - if box then - setlist(box,visualize(getlist(box),getid(box) == vlist_code)) + function visualizers.box(n) + if usedfont then + starttiming(visualizers) + local box = getbox(n) + if box then + setlist(box,visualize(getlist(box),getid(box) == vlist_code)) + end + stoptiming(visualizers) + return head, true + else + return head, false end - stoptiming(visualizers) - return head, true - else - return head, false end + end do @@ -1162,40 +1249,44 @@ end) -- interface -local implement = interfaces.implement +do -implement { - name = "setvisual", - arguments = "string", - actions = visualizers.setvisual -} + local implement = interfaces.implement -implement { - name = "setvisuals", - arguments = "string", - actions = visualizers.setvisual -} + implement { + name = "setvisual", + arguments = "string", + actions = visualizers.setvisual + } -implement { - name = "getvisual", - arguments = "string", - actions = { setvisual, context } -} + implement { + name = "setvisuals", + arguments = "string", + actions = visualizers.setvisual + } implement { - name = "setvisuallayer", - arguments = "string", - actions = visualizers.setlayer -} + name = "getvisual", + arguments = "string", + actions = { setvisual, context } + } -implement { - name = "markvisualfonts", - arguments = "integer", - actions = visualizers.markfonts -} + implement { + name = "setvisuallayer", + arguments = "string", + actions = visualizers.setlayer + } -implement { - name = "setvisualfont", - arguments = "integer", - actions = visualizers.setfont -} + implement { + name = "markvisualfonts", + arguments = "integer", + actions = visualizers.markfonts + } + + implement { + name = "setvisualfont", + arguments = "integer", + actions = visualizers.setfont + } + +end diff --git a/tex/context/base/mkiv/trac-vis.mkiv b/tex/context/base/mkiv/trac-vis.mkiv index 894408222..fbe59a875 100644 --- a/tex/context/base/mkiv/trac-vis.mkiv +++ b/tex/context/base/mkiv/trac-vis.mkiv @@ -119,6 +119,9 @@ \unexpanded\def\showfontkerns {\clf_setvisual{fontkern}} +\unexpanded\def\showfontexpansion + {\clf_setvisual{expansion}} + \unexpanded\def\setvisualizerfont {\dosingleempty\syst_visualizers_setfont} diff --git a/tex/context/base/mkiv/typo-del.mkiv b/tex/context/base/mkiv/typo-del.mkiv index 84fe2c469..b7ebb28d9 100644 --- a/tex/context/base/mkiv/typo-del.mkiv +++ b/tex/context/base/mkiv/typo-del.mkiv @@ -369,7 +369,7 @@ %\c!language=\v!local, \c!repeat=\v!no] -\def\typo_delimited_repeat_ideed +\def\typo_delimited_repeat_indeed {\relax\ifcase\delimitedtextlevel\else \typo_delimited_handle_middle\c!middle \fi} diff --git a/tex/context/base/mkiv/util-lib.lua b/tex/context/base/mkiv/util-lib.lua index ab26afe46..5c3f5f3cf 100644 --- a/tex/context/base/mkiv/util-lib.lua +++ b/tex/context/base/mkiv/util-lib.lua @@ -343,10 +343,17 @@ We use the same lookup logic for ffi loading. local trace_ffilib = false local savedffiload = ffi.load + -- local pushlibpath = package.pushlibpath + -- local poplibpath = package.poplibpath + -- ffi.savedload = savedffiload trackers.register("resolvers.ffilib", function(v) trace_ffilib = v end) + -- pushlibpath(pathpart(name)) + -- local message, library = pcall(savedffiload,nameonly(name)) + -- poplibpath() + local function locateindeed(name) local message, library = pcall(savedffiload,removesuffix(name)) if type(message) == "userdata" then diff --git a/tex/context/base/mkiv/util-sbx.lua b/tex/context/base/mkiv/util-sbx.lua index 66a650875..9318ee38a 100644 --- a/tex/context/base/mkiv/util-sbx.lua +++ b/tex/context/base/mkiv/util-sbx.lua @@ -28,6 +28,7 @@ local concat = string.concat local unquoted = string.unquoted local optionalquoted = string.optionalquoted local basename = file.basename +local nameonly = file.nameonly local sandbox = sandbox local validroots = { } @@ -122,9 +123,9 @@ local function registerlibrary(name) return end if validlibraries == true then - validlibraries = { [name] = true } + validlibraries = { [nameonly(name)] = true } else - validlibraries[name] = true + validlibraries[nameonly(name)] = true end elseif name == true then validlibraries = { } @@ -562,9 +563,9 @@ if FFISUPPORTED and ffi then end end - local load = ffi.load + local fiiload = ffi.load - if load then + if fiiload then local reported = { } @@ -573,10 +574,10 @@ if FFISUPPORTED and ffi then -- all blocked elseif validlibraries == true then -- all permitted - return load(name,...) - elseif validlibraries[name] then + return fiiload(name,...) + elseif validlibraries[nameonly(name)] then -- 'name' permitted - return load(name,...) + return fiiload(name,...) else -- 'name' not permitted end diff --git a/tex/context/base/mkiv/util-seq.lua b/tex/context/base/mkiv/util-seq.lua index 5836f5eca..d302ff276 100644 --- a/tex/context/base/mkiv/util-seq.lua +++ b/tex/context/base/mkiv/util-seq.lua @@ -361,6 +361,6 @@ function sequencers.nodeprocessor(t,nofarguments) -- todo: handle 'kind' in plug end end local processor = #calls > 0 and formatters[nostate and template_yes_nostate or template_yes_state](concat(vars,"\n"),args,concat(calls,"\n")) or template_nop --- print(processor) + -- print(processor) return processor end diff --git a/tex/context/base/mkiv/util-str.lua b/tex/context/base/mkiv/util-str.lua index cebbc6be2..6d8f198ab 100644 --- a/tex/context/base/mkiv/util-str.lua +++ b/tex/context/base/mkiv/util-str.lua @@ -423,6 +423,27 @@ end -- print(number.formatted(12345678,true)) -- print(number.formatted(1234.56,"!","?")) +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("-") @@ -1183,3 +1204,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,...) -- write + r = r + 1 + result[r] = str == nil and fmt or formatters[fmt](str,...) + end, + function(connector) -- flush + if result then + local str = concat(result,connector) + result, r = { }, 0 + return str + end + end +end diff --git a/tex/context/base/mkiv/util-tab.lua b/tex/context/base/mkiv/util-tab.lua index fb2702228..05038f2fb 100644 --- a/tex/context/base/mkiv/util-tab.lua +++ b/tex/context/base/mkiv/util-tab.lua @@ -616,6 +616,10 @@ local is_simple_table = table.is_simple_table -- return nil -- end +-- In order to overcome the luajit (65K constant) limitation I tried a split approach, +-- i.e. outputting the first level tables as locals but that failed with large cjk +-- fonts too so I removed that ... just use luatex instead. + local function serialize(root,name,specification) if type(specification) == "table" then diff --git a/tex/context/interface/mkii/keys-en.xml b/tex/context/interface/mkii/keys-en.xml index a1c935db8..be455e9fc 100644 --- a/tex/context/interface/mkii/keys-en.xml +++ b/tex/context/interface/mkii/keys-en.xml @@ -693,6 +693,7 @@ + diff --git a/tex/context/interface/mkii/keys-pe.xml b/tex/context/interface/mkii/keys-pe.xml index 91f778c5e..4033b45f8 100644 --- a/tex/context/interface/mkii/keys-pe.xml +++ b/tex/context/interface/mkii/keys-pe.xml @@ -693,6 +693,7 @@ + diff --git a/tex/context/interface/mkiv/context-en.xml b/tex/context/interface/mkiv/context-en.xml index b6308269d..ce9a81b5a 100644 --- a/tex/context/interface/mkiv/context-en.xml +++ b/tex/context/interface/mkiv/context-en.xml @@ -7361,6 +7361,12 @@ + + + + + + @@ -8081,6 +8087,12 @@ + + + + + + @@ -25269,6 +25281,12 @@ + + + + + + diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf index 42aaf874c..22a0fdd48 100644 Binary files a/tex/context/interface/mkiv/i-context.pdf and b/tex/context/interface/mkiv/i-context.pdf differ diff --git a/tex/context/interface/mkiv/i-description.xml b/tex/context/interface/mkiv/i-description.xml index 6f836f326..66da76acc 100644 --- a/tex/context/interface/mkiv/i-description.xml +++ b/tex/context/interface/mkiv/i-description.xml @@ -84,6 +84,12 @@ + + + + + + diff --git a/tex/context/interface/mkiv/i-enumeration.xml b/tex/context/interface/mkiv/i-enumeration.xml index 2469a9713..575d192a5 100644 --- a/tex/context/interface/mkiv/i-enumeration.xml +++ b/tex/context/interface/mkiv/i-enumeration.xml @@ -130,6 +130,12 @@ + + + + + + diff --git a/tex/context/interface/mkiv/i-note.xml b/tex/context/interface/mkiv/i-note.xml index 339bb58ad..734702264 100644 --- a/tex/context/interface/mkiv/i-note.xml +++ b/tex/context/interface/mkiv/i-note.xml @@ -138,6 +138,12 @@ + + + + + + diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf index 1ad38df61..3c36718a9 100644 Binary files a/tex/context/interface/mkiv/i-readme.pdf and b/tex/context/interface/mkiv/i-readme.pdf differ diff --git a/tex/context/test/mkiv/pdf-ua.mkiv b/tex/context/test/mkiv/pdf-ua.mkiv index e048b9717..e83bd88b9 100644 --- a/tex/context/test/mkiv/pdf-ua.mkiv +++ b/tex/context/test/mkiv/pdf-ua.mkiv @@ -13,7 +13,7 @@ \setuptagging[state=start] % should be set automatically, if 'tagging = true' \setuplanguage[en] - + % DisplayDocTitle key is not set to true % % [from PDFUA-Reference-09_(English-invoice).pdf] @@ -21,6 +21,7 @@ % <>/PageMode/UseOutlines/Outlines 43 0 R/Lang(en)/StructTreeRoot 2 0 R/MarkInfo<>/OutputIntents[ 128 0 R]/Metadata 127 0 R>> % endobj % this may interfer with other ViewerPreferences settings? + \startluacode lpdf.addtocatalog("ViewerPreferences",lpdf.dictionary{DisplayDocTitle = true}) \stopluacode @@ -29,13 +30,13 @@ \starttext Warning: This document may contain errors and traces of hazelnuts. - + \blank[big] \startMPcode fill fullcircle scaled 3cm withcolor red; \stopMPcode - + \blank[big] The \quote{Matterhorn Protocol} gives a condensed overview of the possible failure conditions 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 1fc1c79f7..1e4256003 100644 --- a/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua +++ b/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua @@ -8,12 +8,12 @@ if not modules then modules = { } end modules ['luatex-fonts-demo-vf-1'] = { local identifiers = fonts.hashes.identifiers -local defaults = { - { "pdf", "0 g" }, - { "pdf", "1 0 0 rg" }, - { "pdf", "0 1 0 rg" }, - { "pdf", "0 0 1 rg" }, - { "pdf", "0 0 1 rg" }, +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) @@ -28,9 +28,6 @@ return function(specification) { id = id2 }, { id = id3 }, } - local color = { - [0] = defaults, - } local chars = { identifiers[id1].characters, identifiers[id2].characters, @@ -40,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-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 51e10df43..725d23268 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 : 07/05/17 23:01:18 +-- merge date : 07/14/17 19:35:39 do -- begin closure to overcome local limits and interference @@ -3762,6 +3762,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("-") @@ -4274,6 +4289,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 @@ -14796,12 +14826,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 @@ -14810,7 +14840,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 @@ -14829,7 +14859,7 @@ 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 @@ -14875,7 +14905,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 @@ -15537,7 +15567,7 @@ 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 end @@ -15573,7 +15603,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 @@ -15605,17 +15635,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 @@ -17881,7 +17914,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 @@ -18270,13 +18303,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 @@ -18284,8 +18318,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 @@ -18326,6 +18360,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] @@ -18342,6 +18390,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 @@ -18387,9 +18451,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 @@ -18407,7 +18471,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 @@ -18444,8 +18508,8 @@ function readers.pack(data) 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 + 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 end @@ -18457,7 +18521,9 @@ function readers.pack(data) step.coverage=pack_normal(c) else for g1,d1 in next,c do - c[g1]=pack_indexed(d1) + if d1~=true then + c[g1]=pack_indexed(d1) + end end end end @@ -18565,7 +18631,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 @@ -18597,7 +18662,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 @@ -18619,8 +18684,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~="kern" then for g1,d1 in next,c do for g2,d2 in next,d1 do d1[g2]=pack_normal(d2) @@ -18628,6 +18692,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 @@ -18670,7 +18743,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] @@ -18683,13 +18756,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~="kern" 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 @@ -18758,6 +18837,19 @@ 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] @@ -18811,6 +18903,11 @@ function readers.unpack(data) 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 @@ -18841,6 +18938,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 @@ -18917,19 +19019,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 @@ -19220,7 +19309,8 @@ local function checkpairs(lookup) break else local v=d2[1] - if v[1]~=0 or v[2]~=0 or v[4]~=0 then + if v==true then + elseif v and (v[1]~=0 or v[2]~=0 or v[4]~=0) then kerns=false break end @@ -19231,7 +19321,12 @@ local function checkpairs(lookup) report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name) for g1,d1 in next,coverage do for g2,d2 in next,d1 do - d1[g2]=d2[1][3] + local v=d2[1] + if v==true then + d1[g2]=nil + elseif v then + d1[g2]=v[3] + end end end step.format="kern" @@ -19260,6 +19355,7 @@ function readers.compact(data) allsteps=allsteps+nofsteps if nofsteps>1 then local kind=lookup.type + local merg=merged if kind=="gsub_single" or kind=="gsub_alternate" or kind=="gsub_multiple" then merged=merged+mergesteps_1(lookup) elseif kind=="gsub_ligature" then @@ -19275,6 +19371,9 @@ function readers.compact(data) elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then merged=merged+mergesteps_3(lookup) end + if merg~=merged then + lookup.merged=true + end end end else @@ -19334,7 +19433,7 @@ function readers.expand(data) for i=1,#t do local step=t[i] local coverage=step.coverage - for k,v in next,coverage do + for k in next,coverage do local m=merged[k] if m then m[2]=i @@ -19353,6 +19452,7 @@ function readers.expand(data) local sequence=sequences[i] local steps=sequence.steps if steps then + local nofsteps=sequence.nofsteps setmetatableindex(steps,mergesteps) local kind=sequence.type local markclass=sequence.markclass @@ -19364,7 +19464,14 @@ function readers.expand(data) sequence.markclass=markclasses[markclass] end end - local nofsteps=sequence.nofsteps + local flags=sequence.flags + if flags then + flags[5]=flags[1]~=false + or flags[2]~=false + or flags[3]~=false + or sequence.markclass + or false + end for i=1,nofsteps do local step=steps[i] local baseclasses=step.baseclasses @@ -19432,6 +19539,7 @@ function readers.expand(data) end end if sequence[1] then + sequence.n=#sequence rulesize=rulesize+1 rulehash[rulesize]={ nofrules, @@ -19451,6 +19559,7 @@ function readers.expand(data) end end end + rulehash.n=#rulehash end end end @@ -19492,7 +19601,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.031 +otf.version=3.032 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) @@ -20420,9 +20529,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 @@ -20447,6 +20558,42 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist end local function initializehashes(tfmdata) end +local function checkmathreplacements(tfmdata,fullname) + 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 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() @@ -20463,6 +20610,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 @@ -20483,12 +20632,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 @@ -20496,6 +20647,9 @@ local function featuresinitializer(tfmdata,value) end end end + if substitutionsdone then + checkmathreplacements(tfmdata,fullname) + end registerbasehash(tfmdata) end if trace_preparing then @@ -20926,7 +21080,7 @@ 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 @@ -20993,7 +21147,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 @@ -21049,10 +21202,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 @@ -21104,10 +21255,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 @@ -21331,7 +21480,7 @@ local function inject_pairs_only(head,where) if keepregisteredcounts then keepregisteredcounts=false else - nofregisteredkerns=0 + nofregisteredpairs=0 end if trace_injections then show_result(head) @@ -21802,7 +21951,7 @@ local function injectspaces(head) threshold, factor=getthreshold(font) end - for n in traverse_id(glue_code,tonut(head)) do + for n in traverse_char(tonut(head)) do local prev,next=getspaceboth(n) local prevchar=prev and ischar(prev) local nextchar=next and ischar(next) @@ -22313,7 +22462,6 @@ local trace_discruns=false registertracker("otf.discruns",function(v) trace_disc 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 forcediscretionaries=false -local optimizekerns=true directives.register("otf.forcediscretionaries",function(v) forcediscretionaries=v end) @@ -22365,7 +22513,6 @@ 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 @@ -22410,6 +22557,7 @@ local currentfont=false local factor=0 local threshold=0 local checkmarks=false +local discs=false local sweepnode=nil local sweephead={} local notmatchpre={} @@ -22430,44 +22578,51 @@ end local function logwarning(...) report_direct(...) end -local f_unicode=formatters["U+%X"] -local f_uniname=formatters["U+%X (%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 (% 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 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 "-" + end end + return f_unilist(num,nam) + else + return "" end - return f_unilist(num,nam) - else - return "" 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 @@ -22757,7 +22912,7 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative) setchar(start,choice) else if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment) + logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment) end end return head,start,true @@ -22910,28 +23065,23 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje 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) - if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) - end - return head,start,true - end - end - if a and #a>0 then + if a==true then + elseif a 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") end end - if b and #b>0 then + if b==true then + start=snext + elseif b then local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,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") end + start=snext end return head,start,true elseif krn~=0 then @@ -23412,28 +23562,23 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm 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") - if trace_kerns then - logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) - end - return head,start,true - end - end - if a and #a>0 then - local startchar=getchar(start) + if a then + elseif a then local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,"injections") if trace_kerns then + 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 end - if b and #b>0 then - local startchar=getchar(start) + if b==true then + start=snext + elseif b then local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],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 end return head,start,true elseif krn~=0 then @@ -23742,12 +23887,9 @@ end local noflags={ false,false,false,false } local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) 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 @@ -23765,10 +23907,22 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) end end else + local skipmark + local skipligature + local skipbase + local markclass + if skipped then + local flags=sequence.flags or noflags + skipmark=flags[1] + skipligature=flags[2] + skipbase=flags[3] + markclass=sequence.markclass + end local i=1 local laststart=start + local nofchainlookups=#chainlookups while start do - if skipped then + if skipped then while start do local char,id=ischar(start,currentfont) if char then @@ -24155,11 +24309,11 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local checkdisc=sweeptype and getprev(head) local flags=sequence.flags or noflags local done=false + local markclass=sequence.markclass local skipmark=flags[1] local skipligature=flags[2] local skipbase=flags[3] - local markclass=sequence.markclass - local skipsome=skipmark~=false or skipligature~=false or skipbase~=false or markclass + local skipsome=flags[5] local skipped=false local startprev, startnext=getboth(start) @@ -24170,7 +24324,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) 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 and not seq[1][char] then @@ -24179,8 +24332,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) else local f=ck[4] local l=ck[5] - size=l-f+1 - if size>1 then + if l>f then local discfound local n=f+1 last=startnext @@ -24596,100 +24748,555 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end return head,start,done end -handlers.gsub_context=handle_contextchain -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 steps=currentlookup.steps - local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) - end - return handle_contextchain(head,start,dataset,sequence,currentlookup,rlmode) -end -chainprocs.gsub_context=chained_contextchain -chainprocs.gsub_contextchain=chained_contextchain -chainprocs.gsub_reversecontextchain=chained_contextchain -chainprocs.gpos_contextchain=chained_contextchain -chainprocs.gpos_context=chained_contextchain -local missing=setmetatableindex("table") -local function logprocess(...) - if trace_steps then - registermessage(...) - 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 - sequences=false - end - t[font]=sequences - return sequences -end) -do - local autofeatures=fonts.analyzers.features - local featuretypes=otf.tables.featuretypes - local defaultscript=otf.features.checkeddefaultscript - local defaultlanguage=otf.features.checkeddefaultlanguage - local wildcard="*" - local default="dflt" - local function initialize(sequence,script,language,enabled,autoscript,autolanguage) - local features=sequence.features - if features then - local order=sequence.order - if order then - local featuretype=featuretypes[sequence.type or "unknown"] - for i=1,#order do - local kind=order[i] - local valid=enabled[kind] - if valid then - local scripts=features[kind] - local languages=scripts and ( - scripts[script] or - scripts[wildcard] or - (autoscript and defaultscript(featuretype,autoscript,scripts)) - ) - local enabled=languages and ( - languages[language] or - languages[wildcard] or - (autolanguage and defaultlanguage(featuretype,autolanguage,languages)) - ) - if enabled then - return { valid,autofeatures[kind] or false,sequence,kind } - end - end - end - else - end +local function optimized_handle_contextchain(head,start,dataset,sequence,contexts,rlmode) + local sweepnode=sweepnode + local sweeptype=sweeptype + local postreplace + local prereplace + local checkdisc + local diskseen + if sweeptype then + if sweeptype=="replace" then + postreplace=true + prereplace=true + else + postreplace=sweeptype=="post" + prereplace=sweeptype=="pre" end - return false + checkdisc=getprev(head) end - function otf.dataset(tfmdata,font) - local shared=tfmdata.shared - local properties=tfmdata.properties - local language=properties.language or "dflt" - local script=properties.script or "dflt" - local enabled=shared.features - local autoscript=enabled and enabled.autoscript - local autolanguage=enabled and enabled.autolanguage - local res=resolved[font] - if not res then - res={} - resolved[font]=res - end + local currentfont=currentfont + local flags=sequence.flags or noflags + local skipsome=flags[5] + local skipmark + local skipligature + local skipbase + local markclass + if skipsome then + skipmark=flags[1] + skipligature=flags[2] + skipbase=flags[3] + markclass=sequence.markclass + end + local skipped + local startprev, + startnext=getboth(start) + local done + for k=1,contexts.n do + local current=start + local last=start + local ck=contexts[k] + local seq=ck[3] + local s=seq.n + if s==1 then + local char=ischar(current,currentfont) + if char and not seq[1][char] then + goto next + end + else + local f=ck[4] + local l=ck[5] + if l>f then + local discfound + local n=f+1 + last=startnext + while n<=l do + if not last and postreplace then + last=getnext(sweepnode) + sweeptype=nil + end + if last then + local char,id=ischar(last,currentfont) + if char then + if skipsome then + local class=classes[char] + if class==skipmark or (markclass and class=="mark" and not markclass[char]) or class==skipligature or class==skipbase then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,class) + end + last=getnext(last) + elseif seq[n][char] then + if nl then + break + end + pre=getnext(pre) + else + notmatchpre[last]=true + break + end + end + if n<=l then + notmatchpre[last]=true + 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 + end + break + end + end + if notmatchpre[last] then + goto next + end + end + last=getnext(last) + else + goto next + end + else + goto next + end + 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 skipsome then + local class=classes[char] + if class==skipmark or (markclass and class=="mark" and not markclass[char]) or class==skipligature or class==skipbase 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 + goto next + end + else + goto next + end + break + end + else + if 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 + goto next + end + else + goto next + end + break + end + end + elseif char==false then + if discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next + end + else + 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 + if n<1 then + break + end + posttail=getprev(posttail) + end + else + notmatchpost[prev]=true + break + end + end + if n>=1 then + notmatchpost[prev]=true + end + else + 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 + if n<1 then + break + end + replacetail=getprev(replacetail) + end + else + notmatchreplace[prev]=true + if notmatchpost[prev] then + goto next + end + break + end + end + end + end + prev=getprev(prev) + elseif id==glue_code and seq[n][32] and isspace(prev,threshold,id) then + n=n-1 + prev=getprev(prev) + else + goto next + end + else + goto next + end + end + else + goto next + end + else + goto next + 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 skipsome then + local class=classes[char] + if class==skipmark or (markclass and class=="mark" and not markclass[char]) or class==skipligature or class==skipbase then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,class) + end + current=getnext(current) + elseif seq[n][char] then + if ns then + break + end + pre=getnext(pre) + else + notmatchpre[current]=true + break + end + end + if n<=s then + notmatchpre[current]=true + end + 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 + end + replace=getnext(replace) + else + notmatchreplace[current]=true + if not notmatchpre[current] then + goto next + end + break + end + end + else + end + current=getnext(current) + elseif id==glue_code and seq[n][32] and isspace(current,threshold,id) then + n=n+1 + current=getnext(current) + else + goto next + end + else + goto next + end + end + else + goto next + end + end + end + if trace_contexts then + chaintrac(head,start,dataset,sequence,rlmode,ck,skipped,true) + 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 + end + ::next:: + end + if diskseen then + notmatchpre={} + notmatchpost={} + notmatchreplace={} + end + return head,start,done +end +directives.register("otf.optimizechains",function(v) + if v then + report_chain() + report_chain("using experimental optimized code") + report_chain() + end + local handle=v and optimized_handle_contextchain or handle_contextchain + handlers.gsub_context=handle + handlers.gsub_contextchain=handle + handlers.gsub_reversecontextchain=handle + handlers.gpos_contextchain=handle + handlers.gpos_context=handle +end) +handlers.gsub_context=handle_contextchain +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 steps=currentlookup.steps + local nofsteps=currentlookup.nofsteps + if nofsteps>1 then + reportmoresteps(dataset,sequence) + end + return handle_contextchain(head,start,dataset,sequence,currentlookup,rlmode) +end +chainprocs.gsub_context=chained_contextchain +chainprocs.gsub_contextchain=chained_contextchain +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(...) + end + report_process(...) +end +local sequencelists=setmetatableindex(function(t,font) + local sequences=fontdata[font].resources.sequences + if not sequences or not next(sequences) then + sequences=false + end + t[font]=sequences + return sequences +end) +do + local autofeatures=fonts.analyzers.features + local featuretypes=otf.tables.featuretypes + local defaultscript=otf.features.checkeddefaultscript + local defaultlanguage=otf.features.checkeddefaultlanguage + local wildcard="*" + local default="dflt" + local function initialize(sequence,script,language,enabled,autoscript,autolanguage) + local features=sequence.features + if features then + local order=sequence.order + if order then + local featuretype=featuretypes[sequence.type or "unknown"] + for i=1,#order do + local kind=order[i] + local valid=enabled[kind] + if valid then + local scripts=features[kind] + local languages=scripts and ( + scripts[script] or + scripts[wildcard] or + (autoscript and defaultscript(featuretype,autoscript,scripts)) + ) + local enabled=languages and ( + languages[language] or + languages[wildcard] or + (autolanguage and defaultlanguage(featuretype,autolanguage,languages)) + ) + if enabled then + return { valid,autofeatures[kind] or false,sequence,kind } + end + end + end + else + end + end + return false + end + function otf.dataset(tfmdata,font) + local shared=tfmdata.shared + local properties=tfmdata.properties + local language=properties.language or "dflt" + local script=properties.script or "dflt" + local enabled=shared.features + local autoscript=enabled and enabled.autoscript + local autolanguage=enabled and enabled.autolanguage + local res=resolved[font] + if not res then + res={} + resolved[font]=res + end local rs=res[script] if not rs then rs={} @@ -24857,12 +25464,10 @@ 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 d0 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 @@ -25049,6 +25654,7 @@ 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 a @@ -25093,20 +25699,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,step,i) + if ok then + done=true + break + elseif not start then + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -25139,59 +25741,55 @@ 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") + end + 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 - end - 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 + 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 - 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 @@ -25204,6 +25802,7 @@ 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 a @@ -25220,16 +25819,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,rlmode,step,i,injection) - if ok then - return true - end + local lookupmatch=lookupcache[char] + if lookupmatch then + local h,d,ok=handler(head,n,dataset,sequence,lookupmatch,rlmode,step,i,injection) + if ok then + return true end - else - report_missing_coverage(dataset,sequence) end end end @@ -25278,6 +25873,12 @@ otf.helpers.pardirstate=pardirstate do local fastdisc=true directives.register("otf.fastdisc",function(v) fastdisc=v end) + local otfdataset=nil + local getfastdics=function(t,k) + local v=usesfont(k,currentfont) + t[k]=v + return v + end function otf.featuresprocessor(head,font,attr,direction,n) local sequences=sequencelists[font] if not sequencelists then @@ -25295,25 +25896,24 @@ do threshold, factor=getthreshold(font) checkmarks=tfmdata.properties.checkmarks + if not otfdataset then + otfdataset=otf.dataset + end + discs=fastdisc and n and n>1 and setmetatableindex(getfastdisc) 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) + local 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 datasets=otfdataset(tfmdata,font,attr) local dirstack={} sweephead={} - local discs=fastdisc and n and n>1 and setmetatableindex(function(t,k) - local v=usesfont(k,font) - t[k]=v - return v - end) for s=1,#datasets do local dataset=datasets[s] local attribute=dataset[2] @@ -25322,7 +25922,7 @@ do local topstack=0 local typ=sequence.type local gpossing=typ=="gpos_single" or typ=="gpos_pair" - local handler=handlers[typ] + local handler=handlers[typ] local steps=sequence.steps local nofsteps=sequence.nofsteps if not steps then @@ -25350,18 +25950,14 @@ do for i=m[1],m[2] 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 - end + 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 end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -25383,66 +25979,62 @@ do if nofsteps==1 then local step=steps[1] local lookupcache=step.coverage - if not lookupcache then - report_missing_coverage(dataset,sequence) - else - while start do - local char,id=ischar(start,font) - if char then - local lookupmatch=lookupcache[char] - if lookupmatch 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 + while start do + local char,id=ischar(start,font) + if char then + local lookupmatch=lookupcache[char] + if lookupmatch then + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then a=true end - if a then - local ok - head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) - if ok then - done=true - end - if start then - start=getnext(start) - end - else - start=getnext(start) - end - else - start=getnext(start) + elseif not attribute or getprop(start,a_state)==attribute then + a=true end - elseif char==false then - start=getnext(start) - elseif id==glue_code then - start=getnext(start) - elseif id==disc_code then - if not discs or discs[start]==true then + if a 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) - else - start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - end + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) if ok then done=true end + if start then + start=getnext(start) + 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 + 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_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) + else + start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,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 end else @@ -25464,20 +26056,16 @@ do for i=m[1],m[2] 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,step,i) + if ok then + done=true + break + elseif not start then + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -25659,7 +26247,7 @@ local function spaceinitializer(tfmdata,value) local format=step.format if rules then elseif coverage then - local single=format==gpos_single + local single=format=="gpos_single" local kerns=coverage[32] if kerns then for k,v in next,kerns do @@ -25684,12 +26272,13 @@ local function spaceinitializer(tfmdata,value) left[k]=kern[3] else local one=kern[1] - if one then + if one and one~=true then left[k]=one[3] end end end end + else end end last=i @@ -28530,8 +29119,6 @@ local function addfeature(data,feature,specifications) 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] @@ -28617,8 +29204,10 @@ local function addfeature(data,feature,specifications) coverage[unic]=rulehash end end + sequence.n=nofsequences end end + rulehash.n=rulesize end return coverage end -- cgit v1.2.3