From 9191d12efe40ce045f76b695fc5c02fa6a1a7d6a Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Fri, 9 Apr 2021 20:43:45 +0200 Subject: 2021-04-09 19:56:00 --- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 2 +- tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/context.mkiv | 2 +- tex/context/base/mkiv/font-imp-dimensions.lua | 5 +- tex/context/base/mkiv/font-ots.lua | 564 ++++++++++---------- tex/context/base/mkiv/font-ott.lua | 6 +- tex/context/base/mkiv/mult-low.lua | 2 + tex/context/base/mkiv/mult-prm.lua | 1 + tex/context/base/mkiv/spac-ver.lua | 21 +- tex/context/base/mkiv/status-files.pdf | Bin 25407 -> 26155 bytes tex/context/base/mkiv/status-lua.pdf | Bin 257519 -> 257718 bytes tex/context/base/mkxl/cont-new.mkxl | 2 +- tex/context/base/mkxl/context.mkxl | 2 +- tex/context/base/mkxl/font-ots.lmt | 569 ++++++++++---------- tex/context/base/mkxl/lang-ini.lmt | 572 ++++++++++++++++----- tex/context/base/mkxl/lang-ini.mkxl | 16 +- tex/context/base/mkxl/lpdf-pde.lmt | 2 +- tex/context/base/mkxl/mlib-svg.lmt | 6 +- tex/context/base/mkxl/spac-ver.lmt | 21 +- tex/context/base/mkxl/syst-ini.mkxl | 4 + tex/context/base/mkxl/toks-aux.lmt | 30 +- tex/context/modules/mkiv/s-languages-goodies.lmt | 67 +++ tex/context/modules/mkiv/s-languages-goodies.mkxl | 21 +- tex/context/patterns/lmtx/lang-de.llg | 4 +- tex/context/patterns/lmtx/lang-en.llg | 453 ++++++++++++++++ tex/generic/context/luatex/luatex-fonts-merged.lua | 516 +++++++++---------- 27 files changed, 1884 insertions(+), 1008 deletions(-) create mode 100644 tex/context/patterns/lmtx/lang-en.llg (limited to 'tex') diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 8abe3c057..a0151c54d 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{2021.03.31 18:00} +\newcontextversion{2021.04.09 19:54} %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 aa9e7e08f..5313df6e6 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{2021.03.31 18:00} +\edef\contextversion{2021.04.09 19:54} %D For those who want to use this: diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index 241e31a2e..e81cbb7d9 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.03.31 18:00} +\newcontextversion{2021.04.09 19:54} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index f878dd195..5062c3a10 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -45,7 +45,7 @@ %D {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2021.03.31 18:00} +\edef\contextversion{2021.04.09 19:54} %D Kind of special: diff --git a/tex/context/base/mkiv/font-imp-dimensions.lua b/tex/context/base/mkiv/font-imp-dimensions.lua index 760ce3719..66e218e0f 100644 --- a/tex/context/base/mkiv/font-imp-dimensions.lua +++ b/tex/context/base/mkiv/font-imp-dimensions.lua @@ -42,6 +42,7 @@ local function initialize(tfmdata,key,value) local newwidth = false local newheight = false local newdepth = false + local newshift = false if value == "strut" then newheight = gettexdimen("strutht") newdepth = gettexdimen("strutdp") @@ -55,6 +56,7 @@ local function initialize(tfmdata,key,value) newwidth = spec[1] newheight = spec[2] newdepth = spec[3] + newshift = spec[4] local quad = parameters.quad or 0 local ascender = parameters.ascender or 0 local descender = parameters.descender or 0 @@ -87,6 +89,7 @@ local function initialize(tfmdata,key,value) parameters.x_heigth = (ascender + descender) / 2 end end + -- todo: hshift too if newwidth or newheight or newdepth then for unicode, character in next, characters do local oldwidth = character.width @@ -101,7 +104,7 @@ local function initialize(tfmdata,key,value) character.depth = depth if oldwidth ~= width then local commands = character.commands - local hshift = rightcommand[(width - oldwidth) / 2] + local hshift = rightcommand[newshift or ((width - oldwidth) / 2)] if commands then character.commands = prependcommands ( commands, diff --git a/tex/context/base/mkiv/font-ots.lua b/tex/context/base/mkiv/font-ots.lua index 880bcb6d5..56a91dbbb 100644 --- a/tex/context/base/mkiv/font-ots.lua +++ b/tex/context/base/mkiv/font-ots.lua @@ -2480,115 +2480,247 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts) goto next end - local s = seq.n -- or #seq - local l = ck[5] -- last current - local current = start - local last = start - + local s = seq.n -- or #seq if s == 1 then - goto next - end + -- bit weird case: why use a chain, but it is a hit + else + local l = ck[5] -- last current + local current = start + local last = start - -- current match + -- current match - 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 postreplace and not last then - last = getnext(sweepnode) - sweeptype = nil - end - if last then - local char, id = ischar(last,currentfont) - if char then - if skiphash and skiphash[char] then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - last = getnext(last) - elseif seq[n][char] then - if n < l then + 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 postreplace and not last then + last = getnext(sweepnode) + sweeptype = nil + end + if last then + local char, id = ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end last = getnext(last) - end - n = n + 1 - elseif discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next + elseif seq[n][char] then + if n < l then + last = getnext(last) + end + n = n + 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + else + break + end else - break - end - else - goto next - end - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then goto next + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + else + break + end else - break + goto next end - else - goto next - end - elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[last]) then - discseen = true - discfound = last - notmatchpre[last] = nil - notmatchpost[last] = true - notmatchreplace[last] = nil - local pre, post, replace = getdisc(last) - if pre then - local n = n - while pre do - if seq[n][getchar(pre)] then - n = n + 1 - if n > l then + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[last]) then + discseen = true + discfound = last + notmatchpre[last] = nil + notmatchpost[last] = true + notmatchreplace[last] = nil + local pre, post, replace = getdisc(last) + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + if n > l then + break + end + pre = getnext(pre) + else + notmatchpre[last] = true break end - pre = getnext(pre) - else + end + if n <= l then notmatchpre[last] = true - break end - end - if n <= l then + 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 + else + break + end + end + end + -- why here again + if notmatchpre[last] then + goto next + end + end + -- maybe only if match + last = getnext(last) else - notmatchpre[last] = true + goto next 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 + 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 skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev = getprev(prev) + elseif seq[n][char] then + if n > 1 then + prev = getprev(prev) + end + n = n - 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next + else + break + end + else + goto next end - replace = getnext(replace) - else - notmatchreplace[last] = true - if notmatchpre[last] then + 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 .. + discseen = true + discfound = prev + notmatchpre[prev] = true + notmatchpost[prev] = nil + notmatchreplace[prev] = nil + local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) + -- weird test: needs checking + if pre ~= start and post ~= start and replace ~= start then + if post then + local n = n + while posttail do + if seq[n][getchar(posttail)] then + n = n - 1 + if posttail == post or n < 1 then + break + else + posttail = getprev(posttail) + end + 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 or n < 1 then + break + else + replacetail = getprev(replacetail) + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + goto next + else + break + end + end + end + else + -- notmatchreplace[prev] = true -- not according to Kai + end + end + prev = getprev(prev) + -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then + -- elseif seq[n][32] and spaces[prev] then + -- n = n - 1 + -- prev = getprev(prev) + elseif id == glue_code then + local sn = seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then + n = n - 1 + prev = getprev(prev) else - break + goto next end + elseif seq[n][0xFFFC] then + n = n - 1 + prev = getprev(prev) + else + goto next end - end - -- why here again - if notmatchpre[last] then + else goto next end end - -- maybe only if match - last = getnext(last) else goto next end @@ -2596,37 +2728,35 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s goto next end end - end - -- before + -- after - if f > 1 then - if startprev then - local prev = startprev - if prereplace and prev == checkdisc then - prev = getprev(sweepnode) + if s > l then + local current = last and getnext(last) + if not current and postreplace then + current = getnext(sweepnode) end - if prev then + if current then local discfound -- = nil - local n = f - 1 - while n >= 1 do - if prev then - local char, id = ischar(prev,currentfont) + local n = l + 1 + while n <= s do + if current then + local char, id = ischar(current,currentfont) if char then if skiphash and skiphash[char] then skipped = true if trace_skips then show_skip(dataset,sequence,char,ck,classes[char]) end - prev = getprev(prev) + current = getnext(current) -- was absent elseif seq[n][char] then - if n > 1 then - prev = getprev(prev) + if n < s then -- new test + current = getnext(current) -- was absent end - n = n - 1 + n = n + 1 elseif discfound then notmatchreplace[discfound] = true - if notmatchpost[discfound] then + if notmatchpre[discfound] then goto next else break @@ -2637,217 +2767,87 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s elseif char == false then if discfound then notmatchreplace[discfound] = true - if notmatchpost[discfound] then + if notmatchpre[discfound] then goto next + else + break 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 .. - discseen = true - discfound = prev - notmatchpre[prev] = true - notmatchpost[prev] = nil - notmatchreplace[prev] = nil - local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) - -- weird test: needs checking - if pre ~= start and post ~= start and replace ~= start then - if post then - local n = n - while posttail do - if seq[n][getchar(posttail)] then - n = n - 1 - if posttail == post or n < 1 then - break - else - posttail = getprev(posttail) - end - else - notmatchpost[prev] = true + -- elseif id == disc_code and (not discs or discs[current]) then + discseen = true + discfound = current + notmatchpre[current] = nil + notmatchpost[current] = true + notmatchreplace[current] = nil + local pre, post, replace = getdisc(current) + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + if n > s then break + else + pre = getnext(pre) end + else + notmatchpre[current] = true + break 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 or n < 1 then - break - else - replacetail = getprev(replacetail) - 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 else - notmatchreplace[prev] = true - if notmatchpost[prev] then - goto next - else - break - end + replace = getnext(replace) + end + else + notmatchreplace[current] = true + if notmatchpre[current] then + goto next + else + break end end - else - -- notmatchreplace[prev] = true -- not according to Kai end + else + -- notmatchreplace[current] = true -- not according to Kai end - prev = getprev(prev) - -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then - -- elseif seq[n][32] and spaces[prev] then - -- n = n - 1 - -- prev = getprev(prev) + current = getnext(current) elseif id == glue_code then local sn = seq[n] - if (sn[32] and spaces[prev]) or sn[0xFFFC] then - n = n - 1 - prev = getprev(prev) + if (sn[32] and spaces[current]) or sn[0xFFFC] then + n = n + 1 + current = getnext(current) else goto next end elseif seq[n][0xFFFC] 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 skiphash and skiphash[char] then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - 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 - elseif discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next - else - break - end - else - goto next - end - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next - else - break - end - else - goto next - end - elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[current]) then - discseen = true - discfound = current - notmatchpre[current] = nil - notmatchpost[current] = true - notmatchreplace[current] = nil - local pre, post, replace = getdisc(current) - if pre then - local n = n - while pre do - if seq[n][getchar(pre)] then - n = n + 1 - if n > s then - break - else - pre = getnext(pre) - end - 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 - else - replace = getnext(replace) - end - else - notmatchreplace[current] = true - if notmatchpre[current] then - goto next - else - break - end - end - end - else - -- notmatchreplace[current] = true -- not according to Kai - end - current = getnext(current) - elseif id == glue_code then - local sn = seq[n] - if (sn[32] and spaces[current]) or sn[0xFFFC] then n = n + 1 current = getnext(current) else goto next end - elseif seq[n][0xFFFC] then - n = n + 1 - current = getnext(current) else goto next end - else - goto next end + else + goto next end - else - goto next end end diff --git a/tex/context/base/mkiv/font-ott.lua b/tex/context/base/mkiv/font-ott.lua index f4d7e05a1..aa1defd6a 100644 --- a/tex/context/base/mkiv/font-ott.lua +++ b/tex/context/base/mkiv/font-ott.lua @@ -821,13 +821,13 @@ local features = allocate { ["dist"] = "distances", ["dlig"] = "discretionary ligatures", ["dnom"] = "denominators", - ["dtls"] = "dotless forms", -- math + ["dtls"] = "dotless forms", -- math ["expt"] = "expert forms", ["falt"] = "final glyph alternates", ["fin2"] = "terminal forms #2", ["fin3"] = "terminal forms #3", ["fina"] = "terminal forms", - ["flac"] = "flattened accents over capitals", -- math + ["flac"] = "flattened accents over capitals", -- math ["frac"] = "fractions", ["fwid"] = "full width", ["half"] = "half forms", @@ -836,7 +836,7 @@ local features = allocate { ["hist"] = "historical forms", ["hkna"] = "horizontal kana alternates", ["hlig"] = "historical ligatures", - ["hngl"] = "hangul", + ["hngl"] = "hangul", -- depricated ["hojo"] = "hojo kanji forms", ["hwid"] = "half width", ["init"] = "initial forms", diff --git a/tex/context/base/mkiv/mult-low.lua b/tex/context/base/mkiv/mult-low.lua index ee924b469..05e5f28e5 100644 --- a/tex/context/base/mkiv/mult-low.lua +++ b/tex/context/base/mkiv/mult-low.lua @@ -174,6 +174,8 @@ return { "frozenflagcode", "tolerantflagcode", "protectedflagcode", "primitiveflagcode", "permanentflagcode", "noalignedflagcode", "immutableflagcode", "mutableflagcode", "globalflagcode", "overloadedflagcode", "immediateflagcode", "conditionalflagcode", "valueflagcode", "instanceflagcode", -- + "prewordcode", "postwordcode", + -- "continuewhenlmtxmode" }, ["helpers"] = { diff --git a/tex/context/base/mkiv/mult-prm.lua b/tex/context/base/mkiv/mult-prm.lua index a6b81af7e..c2b11df3d 100644 --- a/tex/context/base/mkiv/mult-prm.lua +++ b/tex/context/base/mkiv/mult-prm.lua @@ -443,6 +443,7 @@ return { "tolerant", "tpack", "tracingfonts", + "tracinghyphenation", "tracingmath", "undent", "unletfrozen", diff --git a/tex/context/base/mkiv/spac-ver.lua b/tex/context/base/mkiv/spac-ver.lua index 2fc926321..efa1dd967 100644 --- a/tex/context/base/mkiv/spac-ver.lua +++ b/tex/context/base/mkiv/spac-ver.lua @@ -2128,19 +2128,20 @@ do end end - local trace = false - local last = nil - local vmode_code = tex.modelevels.vertical - local temp_code = nodecodes.temp - local getnest = tex.getnest - local getlist = tex.getlist + local trace = false + local last = nil + local vmode_code = tex.modelevels.vertical + local temp_code = nodecodes.temp + local texgetnest = tex.getnest + local texgetlist = tex.getlist + local getnodetail = nodes.tail trackers.register("vspacing.forcestrutdepth",function(v) trace = v end) -- abs : negative is inner function vspacing.checkstrutdepth(depth) - local nest = getnest() + local nest = texgetnest() if abs(nest.mode) == vmode_code and nest.head then local tail = nest.tail local id = tail.id @@ -2149,10 +2150,10 @@ do tail.depth = depth end nest.prevdepth = depth - elseif id == temp_code and getnest("ptr") == 0 then - local head = getlist("page_head") + elseif id == temp_code and texgetnest("ptr") == 0 then + local head = texgetlist("page_head") if head then - tail = nodes.tail(head) + tail = getnodetail(head) if tail and tail.id == hlist_code then if tail.depth < depth then tail.depth = depth diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf index 3b842839c..cffa6fdb6 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 a47f44479..fea56a359 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/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index ba8893ea6..87175b22d 100644 --- a/tex/context/base/mkxl/cont-new.mkxl +++ b/tex/context/base/mkxl/cont-new.mkxl @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.03.31 18:00} +\newcontextversion{2021.04.09 19:54} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl index 1ace620b2..84bdd0277 100644 --- a/tex/context/base/mkxl/context.mkxl +++ b/tex/context/base/mkxl/context.mkxl @@ -29,7 +29,7 @@ %D {YYYY.MM.DD HH:MM} format. \immutable\edef\contextformat {\jobname} -\immutable\edef\contextversion{2021.03.31 18:00} +\immutable\edef\contextversion{2021.04.09 19:54} %overloadmode 1 % check frozen / warning %overloadmode 2 % check frozen / error diff --git a/tex/context/base/mkxl/font-ots.lmt b/tex/context/base/mkxl/font-ots.lmt index d5d04e978..be5b0ce40 100644 --- a/tex/context/base/mkxl/font-ots.lmt +++ b/tex/context/base/mkxl/font-ots.lmt @@ -2524,115 +2524,246 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts) goto next end - local s = seq.n -- or #seq - local l = ck[5] -- last current - local current = start - local last = start - + local s = seq.n -- or #seq if s == 1 then - goto next - end - - -- current match + -- bit weird case: why use a chain, but it is a hit + else + local l = ck[5] -- last current + local current = start + local last = start - 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 postreplace and not last then - last = getnext(sweepnode) - sweeptype = nil - end - if last then - local nxt, char, id = isnextchar(last,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) - if char then - if skiphash and skiphash[char] then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - last = nxt - elseif seq[n][char] then - if n < l then + 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 postreplace and not last then + last = getnext(sweepnode) + sweeptype = nil + end + if last then + local nxt, char, id = isnextchar(last,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end last = nxt - end - n = n + 1 - elseif discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next + elseif seq[n][char] then + if n < l then + last = nxt + end + n = n + 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + else + break + end else - break - end - else - goto next - end - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then goto next + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + else + break + end else - break + goto next end - else - goto next - end - elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[last]) then - discseen = true - discfound = last - notmatchpre[last] = nil - notmatchpost[last] = true - notmatchreplace[last] = nil - local pre, post, replace = getdisc(last) - if pre then - local n = n - while pre do - if seq[n][getchar(pre)] then - n = n + 1 - if n > l then + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[last]) then + discseen = true + discfound = last + notmatchpre[last] = nil + notmatchpost[last] = true + notmatchreplace[last] = nil + local pre, post, replace = getdisc(last) + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + if n > l then + break + end + pre = getnext(pre) + else + notmatchpre[last] = true break end - pre = getnext(pre) - else + end + if n <= l then notmatchpre[last] = true - break end - end - if n <= l then + 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 + else + break + end + end + end + -- why here again + if notmatchpre[last] then + goto next + end + end + -- maybe only if match + last = nxt else - notmatchpre[last] = true + goto next 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 + 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 prv, char, id = isprevchar(prev,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev = prv + elseif seq[n][char] then + if n > 1 then + prev = prv + end + n = n - 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next + else + break + end + else + goto next end - replace = getnext(replace) - else - notmatchreplace[last] = true - if notmatchpre[last] then + 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 .. + discseen = true + discfound = prev + notmatchpre[prev] = true + notmatchpost[prev] = nil + notmatchreplace[prev] = nil + local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) + -- weird test: needs checking + if pre ~= start and post ~= start and replace ~= start then + if post then + local n = n + while posttail do + if seq[n][getchar(posttail)] then + n = n - 1 + if posttail == post or n < 1 then + break + else + posttail = getprev(posttail) + end + 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 or n < 1 then + break + else + replacetail = getprev(replacetail) + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + goto next + else + break + end + end + end + else + -- notmatchreplace[prev] = true -- not according to Kai + end + end + -- prev = getprev(prev) + prev = prv + -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then + -- elseif seq[n][32] and spaces[prev] then + -- n = n - 1 + -- prev = prv + elseif id == glue_code then + local sn = seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then + n = n - 1 + prev = prv else - break + goto next end + elseif seq[n][0xFFFC] then + n = n - 1 + prev = prv + else + goto next end - end - -- why here again - if notmatchpre[last] then + else goto next end end - -- maybe only if match - last = nxt else goto next end @@ -2640,37 +2771,35 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s goto next end end - end - -- before + -- after - if f > 1 then - if startprev then - local prev = startprev - if prereplace and prev == checkdisc then - prev = getprev(sweepnode) + if s > l then + local current = last and getnext(last) + if not current and postreplace then + current = getnext(sweepnode) end - if prev then + if current then local discfound -- = nil - local n = f - 1 - while n >= 1 do - if prev then - local prv, char, id = isprevchar(prev,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) + local n = l + 1 + while n <= s do + if current then + local nxt, char, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) if char then if skiphash and skiphash[char] then skipped = true if trace_skips then show_skip(dataset,sequence,char,ck,classes[char]) end - prev = prv + current = nxt -- was absent elseif seq[n][char] then - if n > 1 then - prev = prv + if n < s then -- new test + current = nxt -- was absent end - n = n - 1 + n = n + 1 elseif discfound then notmatchreplace[discfound] = true - if notmatchpost[discfound] then + if notmatchpre[discfound] then goto next else break @@ -2681,222 +2810,90 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s elseif char == false then if discfound then notmatchreplace[discfound] = true - if notmatchpost[discfound] then + if notmatchpre[discfound] then goto next + else + break 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 .. - discseen = true - discfound = prev - notmatchpre[prev] = true - notmatchpost[prev] = nil - notmatchreplace[prev] = nil - local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) - -- weird test: needs checking - if pre ~= start and post ~= start and replace ~= start then - if post then - local n = n - while posttail do - if seq[n][getchar(posttail)] then - n = n - 1 - if posttail == post or n < 1 then - break - else - posttail = getprev(posttail) - end - else - notmatchpost[prev] = true + -- elseif id == disc_code and (not discs or discs[current]) then + discseen = true + discfound = current + notmatchpre[current] = nil + notmatchpost[current] = true + notmatchreplace[current] = nil + local pre, post, replace = getdisc(current) + -- no detailed checking here, todo ? + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + if n > s then break + else + pre = getnext(pre) end + else + notmatchpre[current] = true + break 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 or n < 1 then - break - else - replacetail = getprev(replacetail) - 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 else - notmatchreplace[prev] = true - if notmatchpost[prev] then - goto next - else - break - end + replace = getnext(replace) + end + else + notmatchreplace[current] = true + if notmatchpre[current] then + goto next + else + break end end - else - -- notmatchreplace[prev] = true -- not according to Kai end + else + -- notmatchreplace[current] = true -- not according to Kai end - -- prev = getprev(prev) - prev = prv - -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then - -- elseif seq[n][32] and spaces[prev] then - -- n = n - 1 - -- prev = prv + current = getnext(current) elseif id == glue_code then local sn = seq[n] - if (sn[32] and spaces[prev]) or sn[0xFFFC] then - n = n - 1 - prev = prv + if (sn[32] and spaces[current]) or sn[0xFFFC] then + n = n + 1 + current = nxt else goto next end elseif seq[n][0xFFFC] then - n = n - 1 - prev = prv - 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 nxt, char, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) - if char then - if skiphash and skiphash[char] then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - current = nxt -- was absent - elseif seq[n][char] then - if n < s then -- new test - current = nxt -- was absent - end - n = n + 1 - elseif discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next - else - break - end - else - goto next - end - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next - else - break - end - else - goto next - end - elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[current]) then - discseen = true - discfound = current - notmatchpre[current] = nil - notmatchpost[current] = true - notmatchreplace[current] = nil - local pre, post, replace = getdisc(current) - -- no detailed checking here, todo ? - if pre then - local n = n - while pre do - if seq[n][getchar(pre)] then - n = n + 1 - if n > s then - break - else - pre = getnext(pre) - end - 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 - else - replace = getnext(replace) - end - else - notmatchreplace[current] = true - if notmatchpre[current] then - goto next - else - break - end - end - end - else - -- notmatchreplace[current] = true -- not according to Kai - end - current = getnext(current) - elseif id == glue_code then - local sn = seq[n] - if (sn[32] and spaces[current]) or sn[0xFFFC] then n = n + 1 current = nxt else goto next end - elseif seq[n][0xFFFC] then - n = n + 1 - current = nxt else goto next end - else - goto next end + else + goto next end - else - goto next end end - if trace_contexts then chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode) end diff --git a/tex/context/base/mkxl/lang-ini.lmt b/tex/context/base/mkxl/lang-ini.lmt index 7364eb7c7..a0bed0d0a 100644 --- a/tex/context/base/mkxl/lang-ini.lmt +++ b/tex/context/base/mkxl/lang-ini.lmt @@ -19,10 +19,12 @@ if not modules then modules = { } end modules ['lang-ini'] = { -- todo: no foo:bar but foo(bar,...) +-- https://wortschatz.uni-leipzig.de/de/download/German : lots of lists + local type, tonumber, next = type, tonumber, next local utfbyte, utflength = utf.byte, utf.length local format, gsub, gmatch, find = string.format, string.gsub, string.gmatch, string.find -local concat, sortedkeys, sortedpairs, keys, insert = table.concat, table.sortedkeys, table.sortedpairs, table.keys, table.insert +local concat, sortedkeys, sortedpairs, keys, insert, tohash = table.concat, table.sortedkeys, table.sortedpairs, table.keys, table.insert, table.tohash local setmetatableindex = table.setmetatableindex local utfvalues, strip, utfcharacters = string.utfvalues, string.strip, utf.characters @@ -35,6 +37,7 @@ local settings_to_set = utilities.parsers.settings_to_set local trace_patterns = false trackers.register("languages.patterns", function(v) trace_patterns = v end) local trace_goodies = false trackers.register("languages.goodies", function(v) trace_goodies = v end) +local trace_applied = false trackers.register("languages.applied", function(v) trace_applied = v end) local report_initialization = logs.reporter("languages","initialization") local report_goodies = logs.reporter("languages","goodies") @@ -246,7 +249,7 @@ end -- 2'2 conflicts with 4' ... and luatex barks on it -local P, S, R, Cs, Ct, lpegmatch, lpegpatterns = lpeg.P, lpeg.S, lpeg.R, lpeg.Cs, lpeg.Ct, lpeg.match, lpeg.patterns +local P, S, R, C, Cs, Ct, lpegmatch, lpegpatterns = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.match, lpeg.patterns local utfsplit = utf.split @@ -558,6 +561,15 @@ function languages.setexceptions(tag,str) end end +function languages.setpatterns(tag,str) + local data, instance = resolve(tag) + if data then + str = strip(str) -- we need to strip leading spaces + collecthjcodes(data,str) + instance:patterns(str) + end +end + local function setwordhandler(tag,action) local data, instance = resolve(tag) if data then @@ -569,6 +581,8 @@ languages.setwordhandler = setwordhandler function languages.setoptions(tag,str) languages.addgoodiesdata(tag,{ { words = str } }) + -- for now: + languages.setgoodieshandler { tag = tag, goodies = tag } end function languages.hyphenate(tag,str) @@ -594,6 +608,23 @@ do local nuts = nodes.nuts local nextglyph = nuts.traversers.glyph local setoptions = nuts.setoptions + + local getnext = nuts.getnext + local getprev = nuts.getprev + local setchar = nuts.setchar + local setnext = nuts.setnext + local setlink = nuts.setlink + local setfield = nuts.setfield + local setdisc = nuts.setdisc + local getprop = nuts.getprop + local setprop = nuts.setprop + local setattrlist = nuts.setattrlist + + local new_disc = nuts.pool.disc + local new_glyph = nuts.pool.glyph + local copy_node = nuts.copy + local flush_list = nuts.flush_list + local glyphoptioncodes = tex.glyphoptioncodes local lower = characters.lower @@ -602,21 +633,6 @@ do local report = logs.reporter("languages","goodies") -local getnext = nuts.getnext -local getprev = nuts.getprev -local setchar = nuts.setchar -local setnext = nuts.setnext -local setlink = nuts.setlink -local setdisc = nuts.setdisc -local getprop = nuts.getprop -local setprop = nuts.setprop -local setattrlist = nuts.setattrlist - -local new_disc = nuts.pool.disc -local new_glyph = nuts.pool.glyph -local copy_node = nuts.copy -local flush_list = nuts.flush_list - -- can be shared local goodiesdata = setmetatableindex(function(t,k) @@ -624,6 +640,7 @@ local flush_list = nuts.flush_list properties = { }, replacements = { }, characters = { }, + exceptions = { }, } t[k] = v return v @@ -631,11 +648,15 @@ local flush_list = nuts.flush_list -- can be a helper - local function setcompound(current,id) + local compound_disc_code = tex.discoptioncodes.preword | tex.discoptioncodes.postword + + local function setcompound(current,id,first,last,lh,rh) local prev = getprev(current) - local language = tolang(id) - local prechar = prehyphenchar(language) - local postchar = posthyphenchar(language) + -- local language = tolang(id) + -- local prechar = prehyphenchar(language) + -- local postchar = posthyphenchar(language) + local prechar = prehyphenchar(id) + local postchar = posthyphenchar(id) local pre = prechar and copy_node(current) local post = postchar and copy_node(current) local disc = new_disc() @@ -649,8 +670,26 @@ local flush_list = nuts.flush_list setoptions(disc,0x3) -- todo foo_code setdisc(disc,pre,post) setlink(prev,disc,current) + if lh then + setfield(first,"rhmin",rh) + end + + if rh then + setfield(current,"lhmin",lh) + end + end + local setcompounds = setmetatableindex(function(t,l) + local v = setmetatableindex(function(t,r) + local v = function(current,id,first,last) return setcompound(current,id,first,last,l,r) end + t[r] = v + return v + end) + t[l] = v + return v + end) + local function replaceword(first,last,old,new,oldlen) local oldlen = utflength(old) local newlen = utflength(new) @@ -689,14 +728,22 @@ local flush_list = nuts.flush_list end end + -- local optioncodes = table.copy(glyphoptioncodes) + -- + -- optioncodes.nokerns = optioncodes.noleftkern | optioncodes.norightkern + -- optioncodes.noligatures = optioncodes.noleftligature | optioncodes.norightligature + + local lh, rh = false, false + local cache = setmetatableindex(function(t,k) local v = 0 if k == "compound" then - v = setcompound + v = setcompounds[lh][rh] else v = 0 for s in gmatch(k,"%w+") do local o = glyphoptioncodes[s] + -- local o = optioncodes[s] if o then v = v | o end @@ -720,18 +767,74 @@ local flush_list = nuts.flush_list end end --- statistics.starttiming(languages) --- statistics.stoptiming(languages) + -- statistics.starttiming(languages) + -- statistics.stoptiming(languages) + + -- 1: restart 2: exceptions+patterns 3: patterns *: next word + + local sequencers = utilities.sequencers + local newsequencer = sequencers.new + local appendgroup = sequencers.appendgroup + local appendaction = sequencers.appendaction + local enableaction = sequencers.enableaction + local disableaction = sequencers.disableaction + + local template = { + arguments = "s", + returnvalues = "r,i", + results = "r,i", + } + + local registeredactions = setmetatableindex ( function(t,tag) + local actions = newsequencer(template) + appendgroup(actions,"user") + t[tag] = actions + return actions + end ) + + languages.registeredactions = registeredactions + + function languages.installhandler(tag,func) + local todo = not rawget(registeredactions,tag) + local actions = registeredactions[tag] + appendaction(actions,"user",func) + enableaction(actions,func) + report("installing handler %a for language %a",func,tag) + if todo then + setwordhandler(tag,function(n,original,remapped,length,first,last) + local runner = actions.runner + if runner then + if getprop(first,"replaced") then + -- maybe some deadcycles + else + local r, result = runner(original) + if not r or original == r then + return result or 0 + else + setprop(first,"replaced",true) + replaceword(first,last,original,r,length) + return 1 + end + end + end + return 2 + end) + end + end + + local appliedoptions = setmetatableindex("table") + languages.appliedoptions = appliedoptions languages.setgoodieshandler = function(specification) -- will become a table specifier if type(specification) == "table" then local tag = specification.tag local goodies = specification.goodies or tag - local result = specification.result or false + local result = specification.result or 2 local data = goodiesdata[goodies] local properties = data.properties local replacements = data.replacements local characters = data.characters + local exceptions = data.exceptions local replacer = nil local d, instance = resolve(tag) local done = false @@ -757,54 +860,107 @@ local flush_list = nuts.flush_list end done = true end + if type(exceptions) == "table" and next(exceptions) then + done = true + else + exceptions = false + end if done then - setwordhandler(tag, - function(n,original,remapped,length,first,last) - local restart = nil - local o = properties[remapped] - if o then - if trace_goodies then - report("properties: %s %s",original,remapped) - end - local index = 0 - for g, c in nextglyph, first do - index = index + 1 - local oi = o[index] - if oi == setcompound then - setcompound(g,n) - restart = getprev(first) - elseif oi then - setoptions(g,oi) - end - if g == last then - break + local registered = registeredactions[tag] + local applied = appliedoptions[tag] + setwordhandler(tag,function(n,original,remapped,length,first,last) + local runner = registered.runner + if runner then + if getprop(first,"replaced") then + -- maybe some deadcycles + else + local r, result = runner(original) + if not r then + if trace_goodies then + report_goodies("kept by runner: %s => %s, result %i",original,remapped, result or 0) end - end - elseif replacer then - -- todo: check lengths so that we can avoid a check - if getprop(first,"replaced") then - -- maybe some deadcycles - else - local r = lpegmatch(replacer,original) - if original == r then + return result or 0 + elseif original == r then + if result then if trace_goodies then - report_goodies("kept: %s => %s",original,remapped) + report_goodies("kept by runner: %s => %s, result %i",original,remapped, result) end + return result else if trace_goodies then - report_goodies("replaced: %s => %s => %s",original,remapped,r) + report_goodies("kept by runner: %s => %s, continue",original,remapped) end - setprop(first,"replaced",true) - replaceword(first,last,original,r,length) - restart = getprev(first) end + else + if trace_goodies then + report_goodies("replaced by runner: %s => %s => %s, restart",original,remapped,r) + end + setprop(first,"replaced",true) + replaceword(first,last,original,r,length) + return 1 + end + end + end + local result = 2 + local o = properties[remapped] + if o then + if trace_goodies then + report("properties: %s %s",original,remapped) + end + if trace_applied then + applied[original] = (applied[original] or 0) + 1 + end + local index = 0 + for g, c in nextglyph, first do + index = index + 1 + local oi = o[index] + if oi then + if type(oi) == "function" then + oi(g,n,first,last) -- maybe return value + result = 1 + else + setoptions(g,oi) + end + end + if g == last then + break end + end + elseif replacer then + -- todo: check lengths so that we can avoid a check + if getprop(first,"replaced") then + -- maybe some deadcycles else + local r = lpegmatch(replacer,original) + if original == r then + if trace_goodies then + report_goodies("kept: %s => %s",original,remapped) + end + else + if trace_goodies then + report_goodies("replaced: %s => %s => %s",original,remapped,r) + end + setprop(first,"replaced",true) + replaceword(first,last,original,r,length) + result = 1 + end + end + elseif exceptions then + local exception = exceptions[original] + if exception then if trace_goodies then - report_goodies("ignored: %s => %s",original,remapped) + report_goodies("exception: %s => %s",original,exception) end + result = exception + else + result = 3 + end + else + if trace_goodies then + report_goodies("ignored: %s => %s",original,remapped) end - return result, restart + end + return result end) elseif trace_goodies then report_goodies("nothing useable in %a for %a",goodies,tag) @@ -814,84 +970,111 @@ local flush_list = nuts.flush_list end end - -- local function xreplacer(t,n) - -- local i = 0 - -- local h = table.tohash(n) - -- if #h == 0 then - -- local p = Cs((utfchartabletopattern(t) / t + utf8char)^0) - -- return function(s) - -- return lpegmatch(p,s) - -- end - -- else - -- local f = function(k) i = i + 1 if h[i] then return t[k] else return k end end - -- local p = Cs((utfchartabletopattern(t) / f + utf8char)^0) - -- return function(s) - -- i = 0 - -- return lpegmatch(p,s) - -- end - -- end - -- end - - local function blockligature(v,n) - if n > 0 then - local vv = v[n-1] - if vv then - v[n-1] = vv .. " norightligature" - else - v[n-1] = "norightligature" + local norightligature_option = glyphoptioncodes.norightligature + local noleftligature_option = glyphoptioncodes.noleftligature + local norightkern_option = glyphoptioncodes.norightkern + local noleftkern_option = glyphoptioncodes.noleftkern + + local function applyaction(oc,v,n) + if oc == "noligature" then + if n > 0 then + local vv = v[n-1] + if vv then + v[n-1] = vv | norightligature_option + else + v[n-1] = norightligature_option + end + end + v[n] = noleftligature_option + elseif oc == "compound" then + if n > 1 then + -- v[n] = setcompound + v[n] = setcompounds[lh][rh] + return true + end + elseif oc == "nokern" then + if n > 0 then + local vv = v[n-1] + if vv then + v[n-1] = vv | norightkern_option + else + v[n-1] = norightkern_option + end + end + v[n] = noleftkern_option + elseif oc == "noleftkern" then + v[n] = noleftkern_option + elseif oc == "norightkern" then + if n > 0 then + local vv = v[n-1] + if vv then + v[n-1] = vv | norightkern_option + else + v[n-1] = norightkern_option + end + end + else + for s in gmatch(oc,"%w+") do + if applyaction(s,v,n) then + return + end end end - v[n] = "noleftligature" end - local function analyzed(m,t,k) + -- a|b : a:norightligature b:noleftligature + -- a=b : a:norightkern b:noleftkern + -- ab : a:norightkern + -- a+b : compound + + local actions = { + ["|"] = "noligature", + ["="] = "nokern", + ["<"] = "noleftkern", + [">"] = "norightkern", + ["+"] = "compound", + } + + local function analyzed(m,a,t,k) local v = { } local n = 1 if m == true then for c in gmatch(k,".") do - if c == "|" then - blockligature(v,n) - elseif c == "=" then - v[n] = setcompound - else + local ac = a[c] + if not ac then n = n + 1 + else + applyaction(ac,v,n) end end elseif type(m) == "number" then local i = 0 for c in gmatch(k,".") do - if c == "|" then - i = i + 1 - if i == m then - blockligature(v,n) - break - end - elseif c == "=" then + local ac = a[c] + if not ac then + n = n + 1 + else i = i + 1 if i == m then - v[n] = setcompound + applyaction(ac,v,n) break end - else - n = n + 1 end end elseif type(m) == "table" then + -- happens here, otherwise no stable caching key, we could hash these too + m = tohash(m) local i = 0 - m = table.tohash(m) for c in gmatch(k,".") do - if c == "|" then - i = i + 1 - if m[i] then - blockligature(v,n) - end - elseif c == "=" then + local ac = a[c] + if not ac then + n = n + 1 + else i = i + 1 if m[i] then - v[n] = setcompound + applyaction(ac,v,n) end - else - n = n + 1 end end else @@ -902,26 +1085,87 @@ local flush_list = nuts.flush_list end local cache = setmetatableindex(function(t,m) - local v = setmetatableindex(function(t,k) - return analyzed(m,t,k) + local v = setmetatableindex(function(t,a) + local v = setmetatableindex(function(t,k) + return analyzed(m,a,t,k) + end) + t[m] = v + return v end) t[m] = v return v end) - local replace1 = Cs ( ( S("|=")/"" + lpeg.patterns.utf8character )^0 ) - local replace2 = Cs ( ( S("|=") + lpeg.patterns.utf8character/".")^0 ) + -- maybe also a skip symbol + + local replace1 = Cs ( ( S("|=<>+.0123456789")/"" + lpegpatterns.utf8character )^0 ) + local replace2 = Cs ( ( S("|=<>+.0123456789") + lpegpatterns.utf8character/".")^0 ) local function stripped(str) - str = gsub(str,"[%-%%]+[^\n]+\n","") + -- todo : lpeg + str = gsub(str,"%-%-[^\n]+\n","") + str = gsub(str,"%%[^\n]+\n","") str = gsub(str,"%s+"," ") + str = gsub(str,"^%s+","") + str = gsub(str,"%s+$","") return str end + local registerexceptions do + + local lbrace = P("{") + local rbrace = P("}") + local lbracket = P("[") + local rbracket = P("]") + local lparent = P("(") + local rparent = P(")") + local hyphen = P("-") + + local p = Cs ( ( + lbrace * ((1-rbrace)^0) * rbrace + * lbrace * ((1-rbrace)^0) * rbrace + * lbrace * C((1-rbrace)^0) * rbrace * (lparent * C((1-rparent)^0) * rparent)^0 / function(a,b) return b or a end + + (lbracket * (1-rbracket)^0 * rbracket) / "" + + hyphen / "" + + lpegpatterns.utf8character + )^0 ) + + registerexceptions = function(target,str) + local kind = type(str) + if kind == "string" then + for v in gmatch(stripped(str),"%S+") do + local k = lpegmatch(p,v) + if k ~= v then + target[k] = v + end + end + elseif kind == "table" then + local n = #str + if n > 0 then + for i=1,n do + local v = str[i] + local k = lpegmatch(p,v) + if k ~= v then + target[k] = v + end + end + else + -- maybe check for sanity + for k, v in next, str do + target[k] = v + end + end + end + end + + end + function languages.strippedgoodiewords(str) return lpegmatch(replace1,stripped(str)) end + local splitter = lpeg.tsplitat(" ") + local function addgoodies(tag,list,filename) local np = 0 local nd = 0 @@ -932,25 +1176,44 @@ local flush_list = nuts.flush_list local properties = data.properties local replacements = data.replacements local characters = data.characters + local exceptions = data.exceptions if filename then if not data.goodies then data.goodies = { } end - table.insert(data.goodies,filename) + insert(data.goodies,filename) end -- + lh = false + rh = false + -- for i=1,nl do local l = list[i] if type(l) == "table" then - local w = l.words - local p = l.patterns - local c = l.characters + local w = l.words + local p = l.patterns + local c = l.characters + local e = l.exceptions + lh = l.left or false -- for practical reasons these are semi-global + rh = l.right or false -- for practical reasons these are semi-global if c then for v in utfvalues(c) do characters[v] = true end end if w then + local prefixes = l.prefixes + local nofprefixes = 0 + local suffixes = l.suffixes + local nofsuffixes = 0 + if prefixes then + prefixes = lpegmatch(splitter,lower(stripped(prefixes))) + nofprefixes = #prefixes + end + if suffixes then + suffixes = lpegmatch(splitter,lower(stripped(suffixes))) + nofsuffixes = #suffixes + end w = lower(stripped(w)) if p then local pattern = Cs((utfchartabletopattern(p) / p + 1)^0) @@ -959,19 +1222,72 @@ local flush_list = nuts.flush_list else nd = nd + 1 end - local cach = cache[l.matches or true] - for s in gmatch(w,"%S+") do - properties[lpegmatch(replace1,s)] = cach[lpegmatch(replace2,s)] - nw = nw + 1 + local m = l.matches + if not m then + m = true + end + local a = l.actions + if a then + setmetatableindex(a,actions) + else + a = actions + end + local cach = cache[m][a] + if nofprefixes > 0 then + if nofsuffixes > 0 then + for wrd in gmatch(w,"%S+") do + properties[lpegmatch(replace1,wrd)] = cach[lpegmatch(replace2,wrd)] + nw = nw + 1 + for i=1,nofprefixes do + local tmp = prefixes[i] .. wrd + for i=1,nofsuffixes do + local str = tmp .. suffixes[i] + properties[lpegmatch(replace1,str)] = cach[lpegmatch(replace2,str)] + nw = nw + 1 + end + end + end + else + for wrd in gmatch(w,"%S+") do + properties[lpegmatch(replace1,wrd)] = cach[lpegmatch(replace2,wrd)] + nw = nw + 1 + for i=1,nofprefixes do + local str = prefixes[i] .. wrd + properties[lpegmatch(replace1,str)] = cach[lpegmatch(replace2,str)] + nw = nw + 1 + end + end + end + elseif nofsuffixes > 0 then + for wrd in gmatch(w,"%S+") do + properties[lpegmatch(replace1,wrd)] = cach[lpegmatch(replace2,wrd)] + nw = nw + 1 + for i=1,nofsuffixes do + local str = wrd .. suffixes[i] + properties[lpegmatch(replace1,str)] = cach[lpegmatch(replace2,str)] + nw = nw + 1 + end + end + else + for wrd in gmatch(w,"%S+") do + properties[lpegmatch(replace1,wrd)] = cach[lpegmatch(replace2,wrd)] + nw = nw + 1 + end end elseif p then for k, v in next, p do -- todo: warning overload replacements[k] = v end + elseif e then + registerexceptions(exceptions,e) end end end + + lh = false + rh = false + return { np = np, nd = nd, nw = nw, nl = nl } end @@ -1155,6 +1471,12 @@ implement { arguments = "2 strings" } +implement { + name = "setlanguagepatterns", + actions = languages.setpatterns, + arguments = "2 strings" +} + implement { name = "setlanguageoptions", actions = languages.setoptions, diff --git a/tex/context/base/mkxl/lang-ini.mkxl b/tex/context/base/mkxl/lang-ini.mkxl index 6af61077b..c4a109f3b 100644 --- a/tex/context/base/mkxl/lang-ini.mkxl +++ b/tex/context/base/mkxl/lang-ini.mkxl @@ -740,10 +740,22 @@ \clf_setlanguageexceptions{\askedlanguage}{#2}% \endgroup} +\permanent\let\stoppatterns\relax + +\tolerant\permanent\protected\def\startpatterns[#1]#:#2\stoppatterns + {\begingroup + \edef\askedlanguage{\reallanguagetag{#1}}% + \ifempty\askedlanguage + \let\askedlanguage\currentlanguage + \fi + \clf_setlanguagepatterns{\askedlanguage}{#2}% + \endgroup} + + \pushoverloadmode -\permanent\protected\def\hyphenation - {\clf_setlanguageexceptions{\currentlanguage}} +\permanent\protected\def\hyphenation{\clf_setlanguageexceptions{\currentlanguage}} +\permanent\protected\def\patterns {\clf_setlanguagepatterns {\currentlanguage}} \popoverloadmode diff --git a/tex/context/base/mkxl/lpdf-pde.lmt b/tex/context/base/mkxl/lpdf-pde.lmt index 7fb14ada2..f8778f602 100644 --- a/tex/context/base/mkxl/lpdf-pde.lmt +++ b/tex/context/base/mkxl/lpdf-pde.lmt @@ -422,7 +422,7 @@ function resolvers.pages(document) __xrefs__[pagedata] = pagereference __cache__[pagereference] = pagedata else - report_epdf("missing pagedata for page %i, case %i",pagenumber,1) + report_epdf("missing pagedata for page %i, case %i",pagenumber,1) end else report_epdf("missing pagedata for page %i, case %i",pagenumber,2) diff --git a/tex/context/base/mkxl/mlib-svg.lmt b/tex/context/base/mkxl/mlib-svg.lmt index e76580862..961176e07 100644 --- a/tex/context/base/mkxl/mlib-svg.lmt +++ b/tex/context/base/mkxl/mlib-svg.lmt @@ -2004,7 +2004,9 @@ local fraction = offset and asnumber_p(offset) local o = at["stroke-opacity"] or (opacity and at["opacity"]) if o == "none" then o = nil - elseif o then + elseif o == "transparent" then + o = f_opacity(0) + else o = asnumber_r(o) if o == ignoredopacity then o = nil @@ -2112,7 +2114,7 @@ local fraction = offset and asnumber_p(offset) end end end - for c in xmlcollected(c,"(symbol|radialGradient|linearGradient") do + for c in xmlcollected(c,"(symbol|radialGradient|linearGradient)") do local id = rawget(c.at,"id") if id then definitions["#" .. id ] = c diff --git a/tex/context/base/mkxl/spac-ver.lmt b/tex/context/base/mkxl/spac-ver.lmt index 2d4aace24..3e606552a 100644 --- a/tex/context/base/mkxl/spac-ver.lmt +++ b/tex/context/base/mkxl/spac-ver.lmt @@ -2233,19 +2233,20 @@ do end end - local trace = false - local last = nil - local vmode_code = tex.modelevels.vertical - local temp_code = nodecodes.temp - local getnest = tex.getnest - local getlist = tex.getlist + local trace = false + local last = nil + local vmode_code = tex.modelevels.vertical + local temp_code = nodecodes.temp + local texgetnest = tex.getnest + local texgetlist = tex.getlist + local getnodetail = nodes.tail trackers.register("vspacing.forcestrutdepth",function(v) trace = v end) -- abs : negative is inner function vspacing.checkstrutdepth(depth) - local nest = getnest() + local nest = texgetnest() if abs(nest.mode) == vmode_code and nest.head then local tail = nest.tail local id = tail.id @@ -2254,10 +2255,10 @@ do tail.depth = depth end nest.prevdepth = depth - elseif id == temp_code and getnest("ptr") == 0 then - local head = getlist("page_head") + elseif id == temp_code and texgetnest("ptr") == 0 then + local head = texgetlist("page_head") if head then - tail = nodes.tail(head) + tail = getnodetail(head) if tail and tail.id == hlist_code then if tail.depth < depth then tail.depth = depth diff --git a/tex/context/base/mkxl/syst-ini.mkxl b/tex/context/base/mkxl/syst-ini.mkxl index 45536d4e4..fb85531b7 100644 --- a/tex/context/base/mkxl/syst-ini.mkxl +++ b/tex/context/base/mkxl/syst-ini.mkxl @@ -1320,4 +1320,8 @@ \tracinglevels\plusthree +%D We just report duplicate patterns being ignored: + +\tracinghyphenation\plusone + \protect \endinput diff --git a/tex/context/base/mkxl/toks-aux.lmt b/tex/context/base/mkxl/toks-aux.lmt index 03f4dc2c1..4f9ed7eb1 100644 --- a/tex/context/base/mkxl/toks-aux.lmt +++ b/tex/context/base/mkxl/toks-aux.lmt @@ -130,30 +130,19 @@ interfaces.implement { local groupcodes = { } local glyphoptioncodes = { } +local discoptioncodes = { } local hyphenationcodes = { } local frozenparcodes = { } local flagcodes = { } local normalizecodes = { } -for k, v in next, tex.getgroupvalues() do - groupcodes[k] = gsub(v,"[_ ]","") -end -for k, v in next, tex.gethyphenationvalues() do - hyphenationcodes[k] = gsub(v,"[_ ]","") -end -for k, v in next, tex.getglyphoptionvalues() do - glyphoptioncodes[k] = gsub(v,"[_ ]","") -end -for k, v in next, tex.getfrozenparvalues() do - frozenparcodes[k] = gsub(v,"[_ ]","") -end -for k, v in next, tex.getflagvalues() do - flagcodes[k] = gsub(v,"[_ ]","") -end -for k, v in next, tex.getnormalizevalues() do - normalizecodes[k] = gsub(v,"[_ ]","") -end - +for k, v in next, tex.getgroupvalues() do groupcodes[k] = gsub(v,"[_ ]","") end +for k, v in next, tex.gethyphenationvalues() do hyphenationcodes[k] = gsub(v,"[_ ]","") end +for k, v in next, tex.getglyphoptionvalues() do glyphoptioncodes[k] = gsub(v,"[_ ]","") end +for k, v in next, tex.getdiscoptionvalues() do discoptioncodes[k] = gsub(v,"[_ ]","") end +for k, v in next, tex.getfrozenparvalues() do frozenparcodes[k] = gsub(v,"[_ ]","") end +for k, v in next, tex.getflagvalues() do flagcodes[k] = gsub(v,"[_ ]","") end +for k, v in next, tex.getnormalizevalues() do normalizecodes[k] = gsub(v,"[_ ]","") end if environment.initex then @@ -161,6 +150,7 @@ if environment.initex then for k, v in next, groupcodes do texintegerdef(v .. "groupcode", k,"immutable") end for k, v in next, glyphoptioncodes do texintegerdef(v .. "code", k,"immutable") end + for k, v in next, discoptioncodes do texintegerdef(v .. "code", k,"immutable") end for k, v in next, hyphenationcodes do texintegerdef(v .. "hyphenationmodecode",k,"immutable") end for k, v in next, frozenparcodes do texintegerdef("frozen" .. v .. "code", k,"immutable") end for k, v in next, flagcodes do texintegerdef(v .. "flagcode", k,"immutable") end @@ -170,6 +160,7 @@ end groupcodes = utilities.storage.allocate(table.swapped(groupcodes, groupcodes)) glyphoptioncodes = utilities.storage.allocate(table.swapped(glyphoptioncodes,glyphoptioncodes)) +discoptioncodes = utilities.storage.allocate(table.swapped(discoptioncodes, discoptioncodes)) hyphenationcodes = utilities.storage.allocate(table.swapped(hyphenationcodes,hyphenationcodes)) frozenparcodes = utilities.storage.allocate(table.swapped(frozenparcodes, frozenparcodes)) flagcodes = utilities.storage.allocate(table.swapped(flagcodes, flagcodes)) @@ -177,6 +168,7 @@ normalizecodes = utilities.storage.allocate(table.swapped(normalizecodes, tex.groupcodes = groupcodes tex.glyphoptioncodes = glyphoptioncodes +tex.discoptioncodes = discoptioncodes tex.hyphenationcodes = hyphenationcodes tex.frozenparcodes = frozenparcodes tex.flagcodes = flagcodes diff --git a/tex/context/modules/mkiv/s-languages-goodies.lmt b/tex/context/modules/mkiv/s-languages-goodies.lmt index cb17680d1..7256fd35d 100644 --- a/tex/context/modules/mkiv/s-languages-goodies.lmt +++ b/tex/context/modules/mkiv/s-languages-goodies.lmt @@ -21,8 +21,20 @@ function moduledata.languages.goodies.show(specification) local l = list[i] local w = l.words if w then + local pre = l.prefixes + local suf = l.suffixes context.startsubject { title = table.concat(table.sortedkeys(l.patterns)," ") } context(languages.strippedgoodiewords(w)) + if pre then + context.blank() + context.bold("prefixes: ") + context(languages.strippedgoodiewords(pre)) + end + if suf then + context.blank() + context.bold("suffixes: ") + context(languages.strippedgoodiewords(suf)) + end context.stopsubject() end end @@ -30,3 +42,58 @@ function moduledata.languages.goodies.show(specification) end end end + +local lpegmatch = lpeg.match + +moduledata.languages.goodies.ligaturehandlers = { } + +function moduledata.languages.goodies.ligatures(specification) + + specification = interfaces.checkedspecification(specification) + local language = specification.language + local filename = specification.file + + if not language then + elseif moduledata.languages.goodies.ligaturehandlers[language] then + else + -- fb ff ffb fff ffh ffi ffj ffk ffl fft fi fk fl ft + local list = specification.list or "ff fi fl ffi fff ffl" + local hash = table.tohash(lpeg.split(" ",list)) -- also strip + local pattern = (1-lpeg.utfchartabletopattern(hash))^1 * lpeg.P(-1) + local checked = { } + + moduledata.languages.goodies.ligaturehandlers[language] = function(original) + if not checked[original] and not lpegmatch(pattern,original) then + checked[original] = true + end + return original + end + + languages.installhandler(language,"moduledata.languages.goodies.ligaturehandlers." .. language .. "") + + statistics.register(string.formatters["'% t' ligatures checked for language %a"](table.sortedkeys(hash), language), function() + return next(checked) and table.concat(table.sortedkeys(checked)," ") or nil + end) + + local applied = languages.appliedoptions[language] + + trackers.enable("languages.applied") + + if applied then + statistics.register(string.formatters["options applied for language %a"](language), function() + return next(applied) and table.concat(table.sortedkeys(applied)," ") or nil + end) + statistics.register(string.formatters["missed ligatures for language %a"](language), function() + for k, v in next, applied do + checked[k] = nil + end + for k, v in next, hash do + checked[k] = nil + end + return next(checked) and table.concat(table.sortedkeys(checked)," ") or nil + end) + end + + end + +end diff --git a/tex/context/modules/mkiv/s-languages-goodies.mkxl b/tex/context/modules/mkiv/s-languages-goodies.mkxl index b98df2c26..669a7b3b8 100644 --- a/tex/context/modules/mkiv/s-languages-goodies.mkxl +++ b/tex/context/modules/mkiv/s-languages-goodies.mkxl @@ -15,7 +15,8 @@ \registerctxluafile{s-languages-goodies}{autosuffix} -\installmodulecommandluasingle \showlanguagegoodies {moduledata.languages.goodies.show} +\installmodulecommandluasingle \showlanguagegoodies {moduledata.languages.goodies.show} +\installmodulecommandluasingle \showlanguageligatures {moduledata.languages.goodies.ligatures} \stopmodule @@ -26,6 +27,7 @@ % \setupbodyfont[libertine] \setuplanguage[de][goodies={lang-de.llg}] +\setuplanguage[en][goodies={lang-en.llg}] \mainlanguage[de] @@ -35,6 +37,21 @@ % Zapf|innovation % \stoplanguageoptions +% \tracinghyphenation3 \tracingonline2 + \starttext - \showlanguagegoodies[file={lang-de.llg}] + \showlanguageligatures[language=de,list=ff fi fl fk ft fb ffi ffl fff ffk fft ffb ffh ffj] + \showlanguageligatures[language=en,list=ff fi fl fk ft fb ffi ffl fff ffk fft ffb ffh ffj] + + \starttitle[title={DE}] + \start \de \showlanguagegoodies [file={lang-de.llg}] \stop + \stoptitle + + \starttitle[title={EN}] + \start \en \showlanguagegoodies[file={lang-en.llg}] \stop + \stoptitle + +% oeps effe +% nonexistentffitestcase +% nonexistentffltestcase \stoptext diff --git a/tex/context/patterns/lmtx/lang-de.llg b/tex/context/patterns/lmtx/lang-de.llg index 9e64eb3b1..db45d004c 100644 --- a/tex/context/patterns/lmtx/lang-de.llg +++ b/tex/context/patterns/lmtx/lang-de.llg @@ -10,6 +10,8 @@ -- users add stuff. The main reason is to deal with compound words (for which we have multiple -- mechanisms) in a more automates way, using modern LMTX features. So this is not for MKIV (at least -- not now). This mechanism can and will also deal with some other language issues. +-- +-- https://typography.guru/journal/whats-a-ligature/ -- Comment : this is a starting point, not de definitve list of words -- Todo : break down this list in more meaningfull categories (like 'matches') @@ -575,7 +577,7 @@ return { -- For Hans (an example of a compound word): -- { -- patterns = { - -- ffl = "ff=l", + -- ffl = "ff+l", -- }, -- words = [[ -- whatever=whatever diff --git a/tex/context/patterns/lmtx/lang-en.llg b/tex/context/patterns/lmtx/lang-en.llg new file mode 100644 index 000000000..5378e0aa9 --- /dev/null +++ b/tex/context/patterns/lmtx/lang-en.llg @@ -0,0 +1,453 @@ +-- The starting point of this list is a post at: +-- +-- https://english.stackexchange.com/a/50957/22099 +-- https://english.stackexchange.com/questions/50660/when-should-i-not-use-a-ligature-in-english-typesetting +-- mentioned on the ConTeXt mailing list after some discussion +-- about this mechanism. + +return { + name = "english", + version = "1.00", + comment = "English ligature suppression", + author = "Mico Loretan, Dave Jarvis, & Hans Hagen", + copyright = "Public domain", + options = { + { + patterns = { + fi = "f|i", + fl = "f|l", + }, + words = [[ + -- f|i + deafish + dwarfish + elfish + oafish + selfish + serfish + unselfish + wolfish + + -- f|l + beefless + briefless + hoofless + leafless + roofless + selfless + turfless + ]], + suffixes = [[ + ness + ly + ]], + }, + { + patterns = { + fi = "f|i", + }, + words = [[ + proofing + ]], + prefixes = [[ + air + child + fire + flame + moth + rust + sound + water + weather + ]], + }, + { + patterns = { + ff = "f|f", + fi = "f|i", + fl = "f|l", + ffi = "f|fi", + ffl = "f|fl", + }, + words = [[ + -- f|f + bookshelfful + mantelshelfful + shelfful + + -- f|i + elfin + + chafing + leafing + loafing + sheafing + strafing + vouchsafing + beefing + reefing + briefing + debriefing + coifing + fifing + jackknifing + knifing + midwifing + waifing + wifing + + goofing + hoofing + roofing + reroofing + spoofing + whoofing + woofing + + gulfing + begulfing + engulfing + ingulfing + golfing + gulfing + rolfing + selfing + wolfing + barfing + bedwarfing + dwarfing + enserfing + kerfing + scarfing + snarfing + surfing + windsurfing + turfing + wharfing + + beefier + comfier + goofier + gulfier + leafier + surfier + turfier + beefiest + comfiest + goofiest + gulfiest + leafiest + surfiest + turfiest + + beefily + goofily + goofiness + + -- f|l + aloofly + briefly + chiefly + deafly + liefly + + calflike + dwarflike + elflike + gulflike + hooflike + leaflike + rooflike + serflike + sheaflike + shelflike + surflike + turflike + waiflike + wolflike + + halflife + shelflife + halfline + roofline + + leaflet + leaflets + leafleted + leafleting + leafletting + leafletted + leafleteer + + pdflatex + + -- f|fi + chaffinch + wolffish + + -- f|fl + safflower + safflowers + ]], + }, + { + patterns = { + ffi = "ff|i", + }, + words = [[ + -- ff|i + cuffing + ]], + prefixes = [[ + hand + un + ]], + }, + { + patterns = { + ffi = "ff|i", + }, + words = [[ + -- ff|i + feoffing + ]], + prefixes = [[ + en + in + ]], + }, + { + patterns = { + ffi = "ff|i", + }, + words = [[ + -- ff|i + staffing + stuffing + ]], + prefixes = [[ + re + over + under + ]], + }, + { + patterns = { + ffi = "ff|i", + }, + words = [[ + -- ff|i + ruffing + ]], + prefixes = [[ + cross + over + under + ]], + }, + { + patterns = { + ffi = "ff|i", + ffl = "ff|l", + }, + words = [[ + -- ff|i + draffish + giraffish + gruffish + offish + raffish + sniffish + standoffish + stiffish + toffish + + -- ff|l + cuffless + stuffless + ]], + suffixes = [[ + ly + ]], + }, + { + patterns = { + ffl = "ff|l", + }, + words = [[ + -- ff|l + scofflaw + cufflink + offline + offload + ]], + suffixes = [[ + s + ed + ing + ]], + }, + { + patterns = { + ffi = "ff|i", + ffl = "ff|l", + }, + words = [[ + -- ff|i + baffing + biffing + boffing + bluffing + outbluffing + buffing + rebuffing + chaffing + cheffing + chuffing + coffing + coiffing + daffing + doffing + fluffing + gaffing + gruffing + huffing + luffing + miffing + muffing + offing + piaffing + puffing + quaffing + reffing + riffing + sclaffing + scoffing + scuffing + shroffing + sluffing + sniffing + snuffing + spiffing + stiffing + stuffing + tariffing + tiffing + waffing + whiffing + yaffing + + buffier + chaffier + chuffier + cliffier + daffier + fluffier + gruffier + huffier + iffier + miffier + puffier + scruffier + sniffier + snuffier + spiffier + stuffier + buffiest + chaffiest + chuffiest + cliffiest + daffiest + fluffiest + gruffiest + huffiest + iffiest + miffiest + puffiest + scruffiest + sniffiest + snuffiest + spiffiest + stuffiest + + daffily + fluffily + gruffily + huffily + puffily + scruffily + sniffily + snuffily + spiffily + stuffily + + fluffiness + huffiness + iffiness + puffiness + scruffiness + sniffiness + spiffiness + stuffiness + + baffies + biffies + jiffies + taffies + toffies + + waffie + + Pfaffian + Wolffian + Wulffian + + -- ff|l + bluffly + gruffly + ruffly + snuffly + stiffly + + rufflike + clifflike + ]], + }, + { + patterns = { + ft = "f|t", + fft = "ff|t", + }, + words = [[ + -- f|t + chieftain + chieftains + chieftaincy + chieftainship + + fifteen + fifteens + fifteenth + fifteenths + fifth + fifthly + fifths + fifties + fiftieth + fiftieths + fifty + fiftyish + + halftime + halftone + + rooftop + rooftops + rooftree + + -- ff|t + offtrack + ]] + } + } +} diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 14c50d6b7..868d224f3 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 : 2021-03-31 18:00 +-- merge date : 2021-04-09 19:54 do -- begin closure to overcome local limits and interference @@ -28572,105 +28572,225 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s goto next end local s=seq.n - local l=ck[5] - local current=start - local last=start if s==1 then - goto next - end - if l>f then - local discfound - local n=f+1 - last=startnext - while n<=l do - if postreplace and not last then - last=getnext(sweepnode) - sweeptype=nil - end - if last then - local char,id=ischar(last,currentfont) - if char then - if skiphash and skiphash[char] then - skipped=true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - last=getnext(last) - elseif seq[n][char] then - if nf then + local discfound + local n=f+1 + last=startnext + while n<=l do + if postreplace and not last then + last=getnext(sweepnode) + sweeptype=nil + end + if last then + local char,id=ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end last=getnext(last) - end - n=n+1 - elseif discfound then - notmatchreplace[discfound]=true - if notmatchpre[discfound] then - goto next + elseif seq[n][char] then + if nl then + elseif id==disc_code then + discseen=true + discfound=last + notmatchpre[last]=nil + notmatchpost[last]=true + notmatchreplace[last]=nil + local pre,post,replace=getdisc(last) + if pre then + local n=n + while pre do + if seq[n][getchar(pre)] then + n=n+1 + if n>l then + break + end + pre=getnext(pre) + else + notmatchpre[last]=true break end - pre=getnext(pre) - else + end + if n<=l then notmatchpre[last]=true - break end - end - if n<=l then + else notmatchpre[last]=true end + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + if n>l then + break + end + replace=getnext(replace) + else + notmatchreplace[last]=true + if notmatchpre[last] then + goto next + else + break + end + end + end + if notmatchpre[last] then + goto next + end + end + last=getnext(last) else - notmatchpre[last]=true + goto next end - if replace then - while replace do - if seq[n][getchar(replace)] then - n=n+1 - if n>l then - break + 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 skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev=getprev(prev) + elseif seq[n][char] then + if n>1 then + prev=getprev(prev) + end + n=n-1 + elseif discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next + else + break + end + else + goto next end - replace=getnext(replace) - else - notmatchreplace[last]=true - if notmatchpre[last] then + 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 + discseen=true + discfound=prev + notmatchpre[prev]=true + notmatchpost[prev]=nil + notmatchreplace[prev]=nil + local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true) + if pre~=start and post~=start and replace~=start then + if post then + local n=n + while posttail do + if seq[n][getchar(posttail)] then + n=n-1 + if posttail==post or n<1 then + break + else + posttail=getprev(posttail) + end + 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 or n<1 then + break + else + replacetail=getprev(replacetail) + end + else + notmatchreplace[prev]=true + if notmatchpost[prev] then + goto next + else + break + end + end + end + else + end + end + prev=getprev(prev) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then + n=n-1 + prev=getprev(prev) else - break + goto next end + elseif seq[n][0xFFFC] then + n=n-1 + prev=getprev(prev) + else + goto next end - end - if notmatchpre[last] then + else goto next end end - last=getnext(last) else goto next end @@ -28678,34 +28798,32 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s goto next end end - end - if f>1 then - if startprev then - local prev=startprev - if prereplace and prev==checkdisc then - prev=getprev(sweepnode) + if s>l then + local current=last and getnext(last) + if not current and postreplace then + current=getnext(sweepnode) end - if prev then + if current then local discfound - local n=f-1 - while n>=1 do - if prev then - local char,id=ischar(prev,currentfont) + local n=l+1 + while n<=s do + if current then + local char,id=ischar(current,currentfont) if char then if skiphash and skiphash[char] then skipped=true if trace_skips then show_skip(dataset,sequence,char,ck,classes[char]) end - prev=getprev(prev) + current=getnext(current) elseif seq[n][char] then - if n>1 then - prev=getprev(prev) + if ns then break + else + pre=getnext(pre) end + else + notmatchpre[current]=true + break 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 or n<1 then - break - else - replacetail=getprev(replacetail) - 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 else - notmatchreplace[prev]=true - if notmatchpost[prev] then - goto next - else - break - end + replace=getnext(replace) + end + else + notmatchreplace[current]=true + if notmatchpre[current] then + goto next + else + break end end - else end + else end - prev=getprev(prev) + current=getnext(current) elseif id==glue_code then local sn=seq[n] - if (sn[32] and spaces[prev]) or sn[0xFFFC] then - n=n-1 - prev=getprev(prev) + if (sn[32] and spaces[current]) or sn[0xFFFC] then + n=n+1 + current=getnext(current) else goto next end elseif seq[n][0xFFFC] 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 skiphash and skiphash[char] then - skipped=true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - current=getnext(current) - elseif seq[n][char] then - if ns then - break - else - pre=getnext(pre) - end - 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 - else - replace=getnext(replace) - end - else - notmatchreplace[current]=true - if notmatchpre[current] then - goto next - else - break - end - end - end - else - end - current=getnext(current) - elseif id==glue_code then - local sn=seq[n] - if (sn[32] and spaces[current]) or sn[0xFFFC] then n=n+1 current=getnext(current) else goto next end - elseif seq[n][0xFFFC] then - n=n+1 - current=getnext(current) else goto next end - else - goto next end + else + goto next end - else - goto next end end if trace_contexts then -- cgit v1.2.3