font-vir.lmt /size: 5601 b    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['font-vir'] = {
2    version   = 1.001,
3    comment   = "companion to font-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- This is actually very experimental code but it has been so since we began with
10-- \LUATEX\ so \unknown. We keep it around because it's not that much code and
11-- because we have some demo's (in the test suite) that use it.
12
13local next, setmetatable, getmetatable = next, setmetatable, getmetatable
14
15local allocate          = utilities.storage.allocate
16local setmetatableindex = table.setmetatableindex
17local fastcopy          = table.fastcopy
18
19local fonts             = fonts
20local constructors      = fonts.constructors
21local vf                = constructors.handlers.vf
22vf.version              = 1.000 -- same as tfm
23
24local definers          = fonts.definers
25local methods           = definers.methods
26
27local variants          = allocate()
28local combinations      = { }
29local combiner          = { }
30local whatever          = allocate()
31local helpers           = allocate()
32local predefined        = fonts.helpers.commands
33
34methods.variants        = variants -- todo .. wrong namespace
35vf.combinations         = combinations
36vf.combiner             = combiner
37vf.whatever             = whatever
38vf.helpers              = helpers
39vf.predefined           = predefined
40
41local slotcommand       = predefined.slot
42
43setmetatableindex(whatever, function(t,k) local v = { } t[k] = v return v end)
44
45local function checkparameters(g,f)
46    if f and g and not g.parameters and #g.fonts > 0 then
47        local p = { }
48        for k,v in next, f.parameters do
49            p[k] = v
50        end
51        g.parameters = p
52        setmetatable(p, getmetatable(f.parameters))
53    end
54end
55
56function methods.install(tag, rules)
57    vf.combinations[tag] = rules
58    variants[tag] = function(specification)
59        return vf.combine(specification,tag)
60    end
61end
62
63local function combine_load(g,name)
64    return constructors.readanddefine(name or g.specification.name,g.specification.size)
65end
66
67local function combine_assign(g, name, from, to, start, force)
68    local f, id = combine_load(g,name)
69    if f and id then
70        -- optimize for whole range, then just g = f
71        if not from  then from, to = 0, 0xFF00 end
72        if not to    then to       = from      end
73        if not start then start    = from      end
74        local fc = f.characters
75        local gc = g.characters
76        local fd = f.descriptions
77        local gd = g.descriptions
78        local hn = #g.fonts+1
79        g.fonts[hn] = { id = id } -- no need to be sparse
80        for i=from,to do
81            if fc[i] and (force or not gc[i]) then
82                gc[i] = fastcopy(fc[i],true) -- can be optimized
83                gc[i].commands = { slotcommand[hn][start] }
84                gd[i] = fd[i]
85            end
86            start = start + 1
87        end
88        checkparameters(g,f)
89    end
90end
91
92local function combine_process(g,list)
93    if list then
94        for _,v in next, list do
95            (combiner.commands[v[1]] or nop)(g,v)
96        end
97    end
98end
99
100local function combine_names(g,name,force)
101    local f, id = constructors.readanddefine(name,g.specification.size)
102    if f and id then
103        local fc = f.characters
104        local gc = g.characters
105        local fd = f.descriptions
106        local gd = g.descriptions
107        g.fonts[#g.fonts+1] = { id = id } -- no need to be sparse
108        local hn = #g.fonts
109        for k, v in next, fc do
110            if force or not gc[k] then
111                gc[k] = fastcopy(v,true)
112                gc[k].commands = { slotcommand[hn][k] }
113                gd[i] = fd[i]
114            end
115        end
116        checkparameters(g,f)
117    end
118end
119
120local combine_feature = function(g,v)
121    local key   = v[2]
122    local value = v[3]
123    if key then
124        if value == nil then
125            value = true
126        end
127        local specification = g.specification
128        if specification then
129            local normalfeatures = specification.features.normal
130            if normalfeatures then
131                normalfeatures[key] = value -- otf?
132            end
133        end
134    end
135end
136
137combiner.commands = allocate {
138    ["initialize"]      = function(g,v) combine_assign    (g,g.properties.name) end,
139    ["include-method"]  = function(g,v) combine_process   (g,combinations[v[2]]) end, -- name
140 -- ["copy-parameters"] = function(g,v) combine_parameters(g,v[2]) end, -- name
141    ["copy-range"]      = function(g,v) combine_assign    (g,v[2],v[3],v[4],v[5],true) end, -- name, from-start, from-end, to-start
142    ["copy-char"]       = function(g,v) combine_assign    (g,v[2],v[3],v[3],v[4],true) end, -- name, from, to
143    ["fallback-range"]  = function(g,v) combine_assign    (g,v[2],v[3],v[4],v[5],false) end, -- name, from-start, from-end, to-start
144    ["fallback-char"]   = function(g,v) combine_assign    (g,v[2],v[3],v[3],v[4],false) end, -- name, from, to
145    ["copy-names"]      = function(g,v) combine_names     (g,v[2],true) end,
146    ["fallback-names"]  = function(g,v) combine_names     (g,v[2],false) end,
147    ["feature"]         =               combine_feature,
148}
149
150function vf.combine(specification,tag)
151    local g = {
152        name          = specification.name,
153        properties    = { },
154        fonts         = { },
155        characters    = { },
156        descriptions  = { },
157        specification = fastcopy(specification),
158    }
159    combine_process(g,combinations[tag])
160    return g
161end
162