diff options
Diffstat (limited to 'tex/context/base/typo-cap.lua')
-rw-r--r-- | tex/context/base/typo-cap.lua | 662 |
1 files changed, 331 insertions, 331 deletions
diff --git a/tex/context/base/typo-cap.lua b/tex/context/base/typo-cap.lua index 304d133c9..fdbf2e353 100644 --- a/tex/context/base/typo-cap.lua +++ b/tex/context/base/typo-cap.lua @@ -1,331 +1,331 @@ -if not modules then modules = { } end modules ['typo-cap'] = {
- version = 1.001,
- comment = "companion to typo-cap.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
- }
-
-local next, type = next, type
-local format, insert = string.format, table.insert
-local div = math.div
-
-local trace_casing = false trackers.register("typesetters.casing", function(v) trace_casing = v end)
-
-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 texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local kerncodes = nodes.kerncodes
-
-local glyph_code = nodecodes.glyph
-local kern_code = nodecodes.kern
-local math_code = nodecodes.math
-
-local kerning_code = kerncodes.kerning
-local userskip_code = skipcodes.userskip
-
-local tasks = nodes.tasks
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local fontchar = fonthashes.characters
-
-local variables = interfaces.variables
-local v_reset = variables.reset
-
-local chardata = characters.data
-
-typesetters = typesetters or { }
-local typesetters = typesetters
-
-typesetters.cases = typesetters.cases or { }
-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
-
--- we use char(0) as placeholder for the larger font, so we need to remove it
--- before it can do further harm
---
--- 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]
- 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
- 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]]
- end
- if ok then
- -- tood; use generic injector
- local prev, original = start, start
- 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
- end
- start = g
- end
- end
- if once then lastfont = nil end
- return prev, 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
- return start, false
-end
-
-local registered, n = { }, 0
-
-local function register(name,f)
- if type(f) == "function" then
- n = n + 1
- actions[n] = f
- registered[name] = n
- return n
- else
- local n = registered[f]
- registered[name] = n
- return n
- end
-end
-
-cases.register = register
-
-local function WORD(start,attribute)
- lastfont = nil
- return helper(start,uccodes)
-end
-
-local function word(start,attribute)
- lastfont = nil
- return helper(start,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
- end
- -- we could return the last in the range and save some scanning
- -- but why bother
- return helper(start,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
- end
- if not prev or prev.id ~= glyph_code then
- return helper(start,uccodes)
- else
- return start, false
- end
-end
-
-local function capital(start,attribute) -- 3
- return helper(start,uccodes,true,attribute,true)
-end
-
-local function Capital(start,attribute) -- 4
- return helper(start,uccodes,true,attribute,false)
-end
-
-local function none(start)
- 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]
- if lccodes[ch] then
- while true do
- local d = chardata[mr(1,0xFFFF)]
- if d then
- local uc = uccodes[d]
- if uc and tfm[uc] then -- this also intercepts tables
- start.char = uc
- return start, true
- end
- end
- end
- elseif uccodes[ch] then
- while true do
- local d = chardata[mr(1,0xFFFF)]
- if d then
- local lc = lccodes[d]
- if lc and tfm[lc] then -- this also intercepts tables
- start.char = lc
- return start, true
- end
- end
- end
- end
- return start, false
-end
-
-register(variables.WORD, WORD) -- 1
-register(variables.word, word) -- 2
-register(variables.Word, Word) -- 3
-register(variables.Words, Words) -- 4
-register(variables.capital, capital) -- 5
-register(variables.Capital, Capital) -- 6
-register(variables.none, none) -- 7 (dummy)
-register(variables.random, random) -- 8
-
-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
- local lastattr = nil
- local done = false
- local start = head
- while start do -- while because start can jump ahead
- local id = start.id
- if id == glyph_code then
- local attr = start[attribute]
- 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
- if action then
- start, ok = action(start,attribute,attr)
- done = done and ok
- if trace_casing then
- report_casing("case trigger %a, instance %a, result %a",attr%100,div(attr,100),ok)
- end
- elseif trace_casing then
- report_casing("unknown case trigger %a",attr)
- end
- end
- elseif id == math_code then
- start = end_of_math(start)
- end
- if start then -- why test
- start = start.next
- end
- end
- lastfont = nil
- return head, done
-end
-
-local m, enabled = 0, false -- a trick to make neighbouring ranges work
-
-function cases.set(n)
- if n == v_reset then
- n = unsetvalue
- else
- n = registered[n] or tonumber(n)
- if n then
- if not enabled then
- tasks.enableaction("processors","typesetters.cases.handler")
- if trace_casing then
- report_casing("enabling case handler")
- end
- enabled = true
- end
- if m == 100 then
- m = 1
- else
- m = m + 1
- end
- n = m * 100 + n
- else
- n = unsetvalue
- end
- end
- texattribute[a_cases] = n
- -- return n -- bonus
-end
-
-cases.handler = nodes.installattributehandler {
- name = "case",
- namespace = cases,
- processor = process,
-}
-
--- interface
-
-commands.setcharactercasing = cases.set
+if not modules then modules = { } end modules ['typo-cap'] = { + version = 1.001, + comment = "companion to typo-cap.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" + } + +local next, type = next, type +local format, insert = string.format, table.insert +local div = math.div + +local trace_casing = false trackers.register("typesetters.casing", function(v) trace_casing = v end) + +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 texattribute = tex.attribute +local unsetvalue = attributes.unsetvalue + +local nodecodes = nodes.nodecodes +local skipcodes = nodes.skipcodes +local kerncodes = nodes.kerncodes + +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local math_code = nodecodes.math + +local kerning_code = kerncodes.kerning +local userskip_code = skipcodes.userskip + +local tasks = nodes.tasks + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontchar = fonthashes.characters + +local variables = interfaces.variables +local v_reset = variables.reset + +local chardata = characters.data + +typesetters = typesetters or { } +local typesetters = typesetters + +typesetters.cases = typesetters.cases or { } +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 + +-- we use char(0) as placeholder for the larger font, so we need to remove it +-- before it can do further harm +-- +-- 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] + 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 + 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]] + end + if ok then + -- tood; use generic injector + local prev, original = start, start + 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 + end + start = g + end + end + if once then lastfont = nil end + return prev, 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 + return start, false +end + +local registered, n = { }, 0 + +local function register(name,f) + if type(f) == "function" then + n = n + 1 + actions[n] = f + registered[name] = n + return n + else + local n = registered[f] + registered[name] = n + return n + end +end + +cases.register = register + +local function WORD(start,attribute) + lastfont = nil + return helper(start,uccodes) +end + +local function word(start,attribute) + lastfont = nil + return helper(start,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 + end + -- we could return the last in the range and save some scanning + -- but why bother + return helper(start,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 + end + if not prev or prev.id ~= glyph_code then + return helper(start,uccodes) + else + return start, false + end +end + +local function capital(start,attribute) -- 3 + return helper(start,uccodes,true,attribute,true) +end + +local function Capital(start,attribute) -- 4 + return helper(start,uccodes,true,attribute,false) +end + +local function none(start) + 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] + if lccodes[ch] then + while true do + local d = chardata[mr(1,0xFFFF)] + if d then + local uc = uccodes[d] + if uc and tfm[uc] then -- this also intercepts tables + start.char = uc + return start, true + end + end + end + elseif uccodes[ch] then + while true do + local d = chardata[mr(1,0xFFFF)] + if d then + local lc = lccodes[d] + if lc and tfm[lc] then -- this also intercepts tables + start.char = lc + return start, true + end + end + end + end + return start, false +end + +register(variables.WORD, WORD) -- 1 +register(variables.word, word) -- 2 +register(variables.Word, Word) -- 3 +register(variables.Words, Words) -- 4 +register(variables.capital, capital) -- 5 +register(variables.Capital, Capital) -- 6 +register(variables.none, none) -- 7 (dummy) +register(variables.random, random) -- 8 + +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 + local lastattr = nil + local done = false + local start = head + while start do -- while because start can jump ahead + local id = start.id + if id == glyph_code then + local attr = start[attribute] + 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 + if action then + start, ok = action(start,attribute,attr) + done = done and ok + if trace_casing then + report_casing("case trigger %a, instance %a, result %a",attr%100,div(attr,100),ok) + end + elseif trace_casing then + report_casing("unknown case trigger %a",attr) + end + end + elseif id == math_code then + start = end_of_math(start) + end + if start then -- why test + start = start.next + end + end + lastfont = nil + return head, done +end + +local m, enabled = 0, false -- a trick to make neighbouring ranges work + +function cases.set(n) + if n == v_reset then + n = unsetvalue + else + n = registered[n] or tonumber(n) + if n then + if not enabled then + tasks.enableaction("processors","typesetters.cases.handler") + if trace_casing then + report_casing("enabling case handler") + end + enabled = true + end + if m == 100 then + m = 1 + else + m = m + 1 + end + n = m * 100 + n + else + n = unsetvalue + end + end + texattribute[a_cases] = n + -- return n -- bonus +end + +cases.handler = nodes.installattributehandler { + name = "case", + namespace = cases, + processor = process, +} + +-- interface + +commands.setcharactercasing = cases.set |