diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/typo-cap.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/typo-cap.lua | 372 |
1 files changed, 233 insertions, 139 deletions
diff --git a/Master/texmf-dist/tex/context/base/typo-cap.lua b/Master/texmf-dist/tex/context/base/typo-cap.lua index fdbf2e35338..78ed8700a31 100644 --- a/Master/texmf-dist/tex/context/base/typo-cap.lua +++ b/Master/texmf-dist/tex/context/base/typo-cap.lua @@ -8,7 +8,7 @@ if not modules then modules = { } end modules ['typo-cap'] = { local next, type = next, type local format, insert = string.format, table.insert -local div = math.div +local div, randomnumber = math.div, math.random local trace_casing = false trackers.register("typesetters.casing", function(v) trace_casing = v end) @@ -16,12 +16,23 @@ local report_casing = logs.reporter("typesetting","casing") local nodes, node = nodes, node -local traverse_id = node.traverse_id -local copy_node = node.copy -local end_of_math = node.end_of_math +local nuts = nodes.nuts +local tonode = nuts.tonode +local tonut = nuts.tonut -local texattribute = tex.attribute -local unsetvalue = attributes.unsetvalue +local getfield = nuts.getfield +local setfield = nuts.setfield +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getattr = nuts.getattr +local setattr = nuts.setattr +local getfont = nuts.getfont +local getsubtype = nuts.getsubtype +local getchar = nuts.getchar + +local copy_node = nuts.copy +local end_of_math = nuts.end_of_math local nodecodes = nodes.nodecodes local skipcodes = nodes.skipcodes @@ -29,6 +40,7 @@ local kerncodes = nodes.kerncodes local glyph_code = nodecodes.glyph local kern_code = nodecodes.kern +local disc_code = nodecodes.disc local math_code = nodecodes.math local kerning_code = kerncodes.kerning @@ -44,6 +56,8 @@ local variables = interfaces.variables local v_reset = variables.reset local chardata = characters.data +local texsetattribute = tex.setattribute +local unsetvalue = attributes.unsetvalue typesetters = typesetters or { } local typesetters = typesetters @@ -53,83 +67,125 @@ local cases = typesetters.cases cases.actions = { } local actions = cases.actions -cases.attribute = c_cases -- no longer needed local a_cases = attributes.private("case") -local lastfont = nil +local extract = bit32.extract +local run = 0 -- a trick to make neighbouring ranges work + +local function set(tag,font) + if run == 2^6 then + run = 1 + else + run = run + 1 + end + return font * 0x10000 + tag * 0x100 + run +end --- we use char(0) as placeholder for the larger font, so we need to remove it --- before it can do further harm +local function get(a) + local font = extract(a,16,12) -- 4000 + local tag = extract(a, 8, 8) -- 250 + local run = extract(a, 0, 8) -- 50 + return tag, font, run +end + +-- print(get(set( 1, 0))) +-- print(get(set( 1, 99))) +-- print(get(set( 2, 96))) +-- print(get(set( 30, 922))) +-- print(get(set(250,4000))) + +-- a previous implementation used char(0) as placeholder for the larger font, so we needed +-- to remove it before it can do further harm ... that was too tricky as we use char 0 for +-- other cases too -- --- we could do the whole glyph run here (till no more attributes match) but --- then we end up with more code .. maybe i will clean this up anyway as the --- lastfont hack is somewhat ugly .. on the other hand, we need to deal with --- cases like: +-- we could do the whole glyph run here (till no more attributes match) but then we end up +-- with more code .. maybe i will clean this up anyway as the lastfont hack is somewhat ugly +-- ... on the other hand, we need to deal with cases like: -- -- \WORD {far too \Word{many \WORD{more \word{pushed} in between} useless} words} local uccodes = characters.uccodes local lccodes = characters.lccodes -local function helper(start, codes, special, attribute, once) - local char = start.char - local dc = codes[char] +-- true false true == mixed + +local function helper(start,attr,lastfont,n,codes,special,once,keepother) + local char = getchar(start) + local dc = codes[char] if dc then - local fnt = start.font - if special then - -- will become function - if start.char == 0 then - lastfont = fnt - local prev, next = start.prev, start.next - prev.next = next - if next then - next.prev = prev - end - return prev, true - elseif lastfont and start.prev.id ~= glyph_code then - fnt = lastfont - start.font = lastfont + local fnt = getfont(start) + if keepother and dc == char then + local lfa = lastfont[n] + if lfa then + setfield(start,"font",lfa) + return start, true + else + return start, false end - end - local ifc = fontchar[fnt] - if type(dc) == "table" then - local ok = true - for i=1,#dc do - ok = ok and ifc[dc[i]] + else + if special then + local lfa = lastfont[n] + if lfa then + local previd = getid(getprev(start)) + if previd ~= glyph_code and previd ~= disc_code then + fnt = lfa + setfield(start,"font",lfa) + end + end end - if ok then - -- tood; use generic injector - local prev, original = start, start + local ifc = fontchar[fnt] + if type(dc) == "table" then + local ok = true for i=1,#dc do - local chr = dc[i] - prev = start - if i == 1 then - start.char = chr - else - local g = copy_node(original) - g.char = chr - local next = start.next - g.prev = start - if next then - g.next = next - start.next = g - next.prev = g + -- could be cached in font + if not ifc[dc[i]] then + ok = false + break + end + end + if ok then + -- todo: use generic injector + local prev = start + local original = start + for i=1,#dc do + local chr = dc[i] + prev = start + if i == 1 then + setfield(start,"char",chr) + else + local g = copy_node(original) + setfield(g,"char",chr) + local next = getnext(start) + setfield(g,"prev",start) + if next then + setfield(g,"next",next) + setfield(start,"next",g) + setfield(next,"prev",g) + end + start = g end - start = g end + if once then + lastfont[n] = false + end + return prev, true end - if once then lastfont = nil end - return prev, true + if once then + lastfont[n] = false + end + return start, false + elseif ifc[dc] then + setfield(start,"char",dc) + if once then + lastfont[n] = false + end + return start, true end - if once then lastfont = nil end - return start, false - elseif ifc[dc] then - start.char = dc - if once then lastfont = nil end - return start, true end end - if once then lastfont = nil end + if once then + lastfont[n] = false + end return start, false end @@ -150,88 +206,106 @@ end cases.register = register -local function WORD(start,attribute) - lastfont = nil - return helper(start,uccodes) +local function WORD(start,attr,lastfont,n) + lastfont[n] = false + return helper(start,attr,lastfont,n,uccodes) end -local function word(start,attribute) - lastfont = nil - return helper(start,lccodes) +local function word(start,attr,lastfont,n) + lastfont[n] = false + return helper(start,attr,lastfont,n,lccodes) end -local function Word(start,attribute,attr) - lastfont = nil - local prev = start.prev - if prev and prev.id == kern_code and prev.subtype == kerning_code then - prev = prev.prev - end - if not prev or prev.id ~= glyph_code then - --- only the first character is treated - for n in traverse_id(glyph_code,start.next) do - if n[attribute] == attr then - n[attribute] = unsetvalue - else - -- break -- we can have nested mess - end +local function blockrest(start) + local n = getnext(start) + while n do + local id = getid(n) + if id == glyph_code or id == disc_node and getattr(n,a_cases) == attr then + setattr(n,a_cases,unsetvalue) + else + -- break -- we can have nested mess end + n = getnext(n) + end +end + +local function Word(start,attr,lastfont,n) -- looks quite complex + lastfont[n] = false + local prev = getprev(start) + if prev and getid(prev) == kern_code and getsubtype(prev) == kerning_code then + prev = getprev(prev) + end + if not prev then + blockrest(start) + return helper(start,attr,lastfont,n,uccodes) + end + local previd = getid(prev) + if previd ~= glyph_code and previd ~= disc_code then + -- only the first character is treated + blockrest(start) -- we could return the last in the range and save some scanning -- but why bother - return helper(start,uccodes) + return helper(start,attr,lastfont,n,uccodes) else return start, false end end -local function Words(start,attribute) - lastfont = nil - local prev = start.prev - if prev and prev.id == kern_code and prev.subtype == kerning_code then - prev = prev.prev +local function Words(start,attr,lastfont,n) + lastfont[n] = false + local prev = getprev(start) + if prev and getid(prev) == kern_code and getsubtype(prev) == kerning_code then + prev = getprev(prev) + end + if not prev then + return helper(start,attr,lastfont,n,uccodes) end - if not prev or prev.id ~= glyph_code then - return helper(start,uccodes) + local previd = getid(prev) + if previd ~= glyph_code and previd ~= disc_code then + return helper(start,attr,lastfont,n,uccodes) else return start, false end end -local function capital(start,attribute) -- 3 - return helper(start,uccodes,true,attribute,true) +local function capital(start,attr,lastfont,n) -- 3 + return helper(start,attr,lastfont,n,uccodes,true,true) end -local function Capital(start,attribute) -- 4 - return helper(start,uccodes,true,attribute,false) +local function Capital(start,attr,lastfont,n) -- 4 + return helper(start,attr,lastfont,n,uccodes,true,false) end -local function none(start) +local function mixed(start,attr,lastfont,n) + return helper(start,attr,lastfont,n,uccodes,false,false,true) +end + +local function none(start,attr,lastfont,n) return start, false end -local function random(start) - lastfont = nil - local ch = start.char - local mr = math.random - -- local tfm = fontdata[start.font].characters - local tfm = fontchar[start.font] +local function random(start,attr,lastfont,n) + lastfont[n] = false + local ch = getchar(start) + local tfm = fontchar[getfont(start)] if lccodes[ch] then while true do - local d = chardata[mr(1,0xFFFF)] + local d = chardata[randomnumber(1,0xFFFF)] if d then local uc = uccodes[d] if uc and tfm[uc] then -- this also intercepts tables - start.char = uc + setfield(start,"char",uc) return start, true end end end elseif uccodes[ch] then while true do - local d = chardata[mr(1,0xFFFF)] + local d = chardata[randomnumber(1,0xFFFF)] if d then local lc = lccodes[d] if lc and tfm[lc] then -- this also intercepts tables - start.char = lc + setfield(start,"char",lc) return start, true end end @@ -248,52 +322,83 @@ register(variables.capital, capital) -- 5 register(variables.Capital, Capital) -- 6 register(variables.none, none) -- 7 (dummy) register(variables.random, random) -- 8 +register(variables.mixed, mixed) -- 9 register(variables.cap, variables.capital) -- clone register(variables.Cap, variables.Capital) -- clone --- node.traverse_id_attr - -local function process(namespace,attribute,head) -- not real fast but also not used on much data - lastfont = nil +function cases.handler(head) -- not real fast but also not used on much data + head = tonut(head) + local lastfont = { } local lastattr = nil - local done = false - local start = head + local done = false + local start = head while start do -- while because start can jump ahead - local id = start.id + local id = getid(start) if id == glyph_code then - local attr = start[attribute] + local attr = getattr(start,a_cases) if attr and attr > 0 then if attr ~= lastattr then - lastfont = nil lastattr = attr end - start[attribute] = unsetvalue - local action = actions[attr%100] -- map back to low number + setattr(start,a_cases,unsetvalue) + local n, id, m = get(attr) + if lastfont[n] == nil then + lastfont[n] = id + end + local action = actions[n] -- map back to low number if action then - start, ok = action(start,attribute,attr) - done = done and ok + start, ok = action(start,attr,lastfont,n) + if ok then + done = true + end if trace_casing then - report_casing("case trigger %a, instance %a, result %a",attr%100,div(attr,100),ok) + report_casing("case trigger %a, instance %a, fontid %a, result %a",n,m,id,ok) end elseif trace_casing then - report_casing("unknown case trigger %a",attr) + report_casing("unknown case trigger %a",n) + end + end + elseif id == disc_code then + local attr = getattr(start,a_cases) + if attr and attr > 0 then + if attr ~= lastattr then + lastattr = attr + end + setattr(start,a_cases,unsetvalue) + local n, id, m = get(attr) + if lastfont[n] == nil then + lastfont[n] = id + end + local action = actions[n] -- map back to low number + if action then + local replace = getfield(start,"replace") + if replace then + action(replace,attr,lastfont,n) + end + local pre = getfield(start,"pre") + if pre then + action(pre,attr,lastfont,n) + end + local post = getfield(start,"post") + if post then + action(post,attr,lastfont,n) + end end end elseif id == math_code then start = end_of_math(start) end if start then -- why test - start = start.next + start = getnext(start) end end - lastfont = nil - return head, done + return tonode(head), done end -local m, enabled = 0, false -- a trick to make neighbouring ranges work +local enabled = false -function cases.set(n) +function cases.set(n,id) if n == v_reset then n = unsetvalue else @@ -306,26 +411,15 @@ function cases.set(n) end enabled = true end - if m == 100 then - m = 1 - else - m = m + 1 - end - n = m * 100 + n + n = set(n,id) else n = unsetvalue end end - texattribute[a_cases] = n + texsetattribute(a_cases,n) -- return n -- bonus end -cases.handler = nodes.installattributehandler { - name = "case", - namespace = cases, - processor = process, -} - -- interface commands.setcharactercasing = cases.set |