summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/font-col.lmt')
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-col.lmt525
1 files changed, 525 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt
new file mode 100644
index 00000000000..41d3274008c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt
@@ -0,0 +1,525 @@
+if not modules then modules = { } end modules ['font-col'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- possible optimization: delayed initialization of vectors
+-- we should also share equal vectors (math)
+
+local context, commands, trackers, logs = context, commands, trackers, logs
+local node, nodes, fonts, characters = node, nodes, fonts, characters
+local file, lpeg, table, string = file, lpeg, table, string
+
+local type, next, tonumber, toboolean = type, next, tonumber, toboolean
+local gmatch = string.gmatch
+local fastcopy = table.fastcopy
+local formatters = string.formatters
+
+local nuts = nodes.nuts
+
+local setfont = nuts.setfont
+
+----- traverse_char = nuts.traverse_char
+local nextchar = nuts.traversers.char
+local getscales = nuts.getscales
+local setscales = nuts.setscales
+local setprop = nuts.setprop
+
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local trace_collecting = false trackers.register("fonts.collecting", function(v) trace_collecting = v end)
+
+local report_fonts = logs.reporter("fonts","collections")
+
+local texconditionals = tex.conditionals
+
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+
+local collections = fonts.collections or { }
+fonts.collections = collections
+
+local definitions = collections.definitions or { }
+collections.definitions = definitions
+
+local vectors = collections.vectors or { }
+collections.vectors = vectors
+
+local helpers = fonts.helpers
+local charcommand = helpers.commands.char
+local rightcommand = helpers.commands.right
+local addprivate = helpers.addprivate
+local hasprivate = helpers.hasprivate
+local fontpatternhassize = helpers.fontpatternhassize
+
+local hashes = fonts.hashes
+local fontdata = hashes.identifiers
+local fontquads = hashes.quads
+local chardata = hashes.characters
+local propdata = hashes.properties
+local mathparameters = hashes.mathparameters
+
+local currentfont = font.current
+local addcharacters = font.addcharacters
+
+local implement = interfaces.implement
+
+local list = { }
+local current = 0
+local enabled = false
+
+local validvectors = table.setmetatableindex(function(t,k)
+ local v = false
+ if not mathparameters[k] then
+ v = vectors[k]
+ end
+ t[k] = v
+ return v
+end)
+
+local function checkenabled()
+ -- a bit ugly but nicer than a fuzzy state while defining math
+ if next(vectors) then
+ if not enabled then
+ enableaction("processors","fonts.collections.process")
+ enabled = true
+ end
+ else
+ if enabled then
+ disableaction("processors","fonts.collections.process")
+ enabled = false
+ end
+ end
+end
+
+collections.checkenabled = checkenabled
+
+function collections.reset(name,font)
+ if font and font ~= "" then
+ local d = definitions[name]
+ if d then
+ d[font] = nil
+ if not next(d) then
+ definitions[name] = nil
+ end
+ end
+ else
+ definitions[name] = nil
+ end
+end
+
+function collections.define(name,font,ranges,details)
+ -- todo: details -> method=force|conditional rscale=
+ -- todo: remap=name
+ local d = definitions[name]
+ if not d then
+ d = { }
+ definitions[name] = d
+ end
+ if name and trace_collecting then
+ report_fonts("extending collection %a using %a",name,font)
+ end
+ details = settings_to_hash(details)
+ -- todo, combine per font start/stop as arrays
+ local offset = details.offset
+ if type(offset) == "string" then
+ offset = characters.getrange(offset,true) or false
+ else
+ offset = tonumber(offset) or false
+ end
+ local target = details.target
+ if type(target) == "string" then
+ target = characters.getrange(target,true) or false
+ else
+ target = tonumber(target) or false
+ end
+ local rscale = tonumber (details.rscale) or 1
+ local force = toboolean(details.force,true)
+ local check = toboolean(details.check,true)
+ local factor = tonumber(details.factor)
+ local features = details.features
+ for s in gmatch(ranges,"[^, ]+") do
+ local start, stop, description, gaps = characters.getrange(s,true)
+ if start and stop then
+ if trace_collecting then
+ if description then
+ report_fonts("using range %a, slots %U - %U, description %a)",s,start,stop,description)
+ end
+ for i=1,#d do
+ local di = d[i]
+ if (start >= di.start and start <= di.stop) or (stop >= di.start and stop <= di.stop) then
+ report_fonts("overlapping ranges %U - %U and %U - %U",start,stop,di.start,di.stop)
+ end
+ end
+ end
+ d[#d+1] = {
+ font = font,
+ start = start,
+ stop = stop,
+ gaps = gaps,
+ offset = offset,
+ target = target,
+ rscale = rscale,
+ force = force,
+ check = check,
+ method = details.method,
+ factor = factor,
+ features = features,
+ }
+ end
+ end
+end
+
+-- todo: provide a lua variant (like with definefont)
+
+function collections.registermain(name)
+ local last = currentfont()
+ if trace_collecting then
+ report_fonts("registering font %a with name %a",last,name)
+ end
+ list[#list+1] = last
+end
+
+-- check: when true, only set when present in font
+-- force: when false, then not set when already set
+
+local uccodes = characters.uccodes
+local lccodes = characters.lccodes
+
+local methods = {
+ lowercase = function(oldchars,newchars,vector,start,stop,cloneid)
+ for k, v in next, oldchars do
+ if k >= start and k <= stop then
+ local lccode = lccodes[k]
+ if k ~= lccode and newchars[lccode] then
+ vector[k] = { cloneid, lccode }
+ end
+ end
+ end
+ end,
+ uppercase = function(oldchars,newchars,vector,start,stop,cloneid)
+ for k, v in next, oldchars do
+ if k >= start and k <= stop then
+ local uccode = uccodes[k]
+ if k ~= uccode and newchars[uccode] then
+ vector[k] = { cloneid, uccode }
+ end
+ end
+ end
+ end,
+}
+
+function collections.clonevector(name)
+ statistics.starttiming(fonts)
+ if trace_collecting then
+ report_fonts("processing collection %a",name)
+ end
+ local definitions = definitions[name]
+ local vector = { }
+ vectors[current] = vector
+ for i=1,#definitions do
+ local definition = definitions[i]
+ local name = definition.font
+ local start = definition.start
+ local stop = definition.stop
+ local check = definition.check
+ local force = definition.force
+ local offset = definition.offset or start
+ local remap = definition.remap -- not used
+ local target = definition.target
+ local method = definition.method
+ local cloneid = list[i]
+ local oldchars = fontdata[current].characters
+ local newchars = fontdata[cloneid].characters
+ local factor = definition.factor
+ local rscale = definition.rscale
+ if factor then
+ vector.factor = factor
+ end
+ if texconditionals["c_font_compact"] and rscale then
+ vector.rscale = rscale
+ end
+ if trace_collecting then
+ if target then
+ report_fonts("remapping font %a to %a for range %U - %U, offset %X, target %U",current,cloneid,start,stop,offset,target)
+ else
+ report_fonts("remapping font %a to %a for range %U - %U, offset %X",current,cloneid,start,stop,offset)
+ end
+ end
+ if method then
+ method = methods[method]
+ end
+ if method then
+ method(oldchars,newchars,vector,start,stop,cloneid)
+ elseif check then
+ if target then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if not newchars[target] then
+ -- not in font
+ elseif force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, target }
+ end
+ target = target + 1
+ end
+ elseif remap then
+ -- not used
+ else
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if not newchars[unicode] then
+ -- not in font
+ elseif force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = cloneid
+ end
+ end
+ end
+ else
+ if target then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, target }
+ end
+ target = target + 1
+ end
+ elseif remap then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, remap[unicode] }
+ end
+ end
+ else
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = cloneid
+ end
+ end
+ end
+ end
+ end
+ if trace_collecting then
+ report_fonts("activating collection %a for font %a",name,current)
+ end
+ statistics.stoptiming(fonts)
+ -- for WS: needs checking
+ if validvectors[current] then
+ checkenabled()
+ end
+end
+
+-- we already have this parser
+--
+-- local spec = (P("sa") + P("at") + P("scaled") + P("at") + P("mo")) * P(" ")^1 * (1-P(" "))^1 * P(" ")^0 * -1
+-- local okay = ((1-spec)^1 * spec * Cc(true)) + Cc(false)
+--
+-- if lpegmatch(okay,name) then
+
+function collections.prepare(name) -- we can do this in lua now .. todo
+ current = currentfont()
+ if vectors[current] then
+ return
+ end
+ local properties = propdata[current]
+ local mathsize = properties.mathsize
+ if mathsize == 1 or mathsize == 2 or mathsize == 3 then
+ return
+ end
+ local d = definitions[name]
+ if d then
+ if trace_collecting then
+ local filename = file.basename(properties.filename or "?")
+ report_fonts("applying collection %a to %a, file %a",name,current,filename)
+ end
+ list = { }
+ context.pushcatcodes("prt") -- context.unprotect()
+ context.font_fallbacks_start_cloning()
+ for i=1,#d do
+ local f = d[i]
+ local name = f.font
+ local scale = f.rscale or 1
+ if texconditionals["c_font_compact"] then
+ scale = 1
+ end
+ if fontpatternhassize(name) then
+ context.font_fallbacks_clone_unique(name,scale)
+ else
+ context.font_fallbacks_clone_inherited(name,scale)
+ end
+ context.font_fallbacks_register_main(name)
+ end
+ context.font_fallbacks_prepare_clone_vectors(name)
+ context.font_fallbacks_stop_cloning()
+ context.popcatcodes() -- context.protect()
+ end
+end
+
+function collections.report(message)
+ if trace_collecting then
+ report_fonts("tex: %s",message)
+ end
+end
+
+local function monoslot(font,char,parent,factor)
+ local tfmdata = fontdata[font]
+ local privatename = formatters["faked mono %s"](char)
+ local privateslot = hasprivate(tfmdata,privatename)
+ if privateslot then
+ return privateslot
+ else
+ local characters = tfmdata.characters
+ local properties = tfmdata.properties
+ local width = factor * fontquads[parent]
+ local character = characters[char]
+ if character then
+ -- runtime patching of the font (can only be new characters)
+ -- instead of messing with existing dimensions
+ local data = {
+ -- no features so a simple copy
+ width = width,
+ height = character.height,
+ depth = character.depth,
+ commands = {
+ rightcommand[(width - character.width or 0)/2],
+ charcommand[char],
+ }
+ }
+ local u = addprivate(tfmdata, privatename, data)
+ addcharacters(properties.id, { characters = { [u] = data } } )
+ return u
+ else
+ return char
+ end
+ end
+end
+
+function collections.register(font,char,handler)
+ if font and char and type(handler) == "function" then
+ local vector = vectors[font]
+ if not vector then
+ vector = { }
+ vectors[font] = vector
+ end
+ vector[char] = handler
+ end
+end
+
+-- todo: also general one for missing
+
+function collections.process(head) -- this way we keep feature processing
+ for n, char, font in nextchar, head do
+ local vector = validvectors[font]
+ if vector then
+ local vect = vector[char]
+ if vect then
+ local kind = type(vect)
+ local newfont, newchar
+ if kind == "table" then
+ newfont = vect[1]
+ newchar = vect[2]
+ if trace_collecting then
+ report_fonts("remapping character %C in font %a to character %C in font %a%s",
+ char,font,newchar,newfont,not chardata[newfont][newchar] and " (missing)" or ""
+ )
+ end
+ elseif kind == "function" then
+ newfont, newchar = vect(font,char,vector)
+ if not newfont then
+ newfont = font
+ end
+ if not newchar then
+ newchar = char
+ end
+ if trace_collecting then
+ report_fonts("remapping character %C in font %a to character %C in font %a%s",
+ char,font,newchar,newfont,not chardata[newfont][newchar] and " (missing)" or ""
+ )
+ end
+ vector[char] = { newfont, newchar }
+ else
+ local fakemono = vector.factor
+ if trace_collecting then
+ report_fonts("remapping font %a to %a for character %C%s",
+ font,vect,char,not chardata[vect][char] and " (missing)" or ""
+ )
+ end
+ newfont = vect
+ if fakemono then
+ newchar = monoslot(vect,char,font,fakemono)
+ else
+ newchar = char
+ end
+ end
+ local rscale = vector.rscale
+ if rscale and rscale ~= 1 then
+ local s, x, y = getscales(n)
+ setscales(n,s*rscale,x*rscale,y*rscale)
+ end
+ setfont(n,newfont,newchar)
+ setprop(n, "original", { font = font, char = char })
+ end
+ end
+ end
+ return head
+end
+
+function collections.found(font,char) -- this way we keep feature processing
+ if not char then
+ font, char = currentfont(), font
+ end
+ if chardata[font][char] then
+ return true -- in normal font
+ else
+ local v = vectors[font]
+ return v and v[char] and true or false
+ end
+end
+
+-- interface
+
+implement {
+ name = "fontcollectiondefine",
+ actions = collections.define,
+ arguments = "4 strings",
+}
+
+implement {
+ name = "fontcollectionreset",
+ actions = collections.reset,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "fontcollectionprepare",
+ actions = collections.prepare,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionreport",
+ actions = collections.report,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionregister",
+ actions = collections.registermain,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionclone",
+ actions = collections.clonevector,
+ arguments = "string"
+}
+
+implement {
+ name = "doifelsecharinfont",
+ actions = { collections.found, commands.doifelse },
+ arguments = "integer"
+}