good-gen.lmt /size: 6623 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['good-gen'] = {
2    version   = 1.000,
3    comment   = "companion to font-lib.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-- depends on ctx
10
11local type, next = type, next
12local lower = string.lower
13local filesuffix, replacesuffix = file.suffix, file.replacesuffix
14local fonts = fonts
15
16----- trace_goodies  = false  trackers.register("fonts.goodies", function(v) trace_goodies = v end)
17----- report_goodies = logs.reporter("fonts","goodies")
18
19local allocate       = utilities.storage.allocate
20local texsp          = tex.sp
21local fontgoodies    = fonts.goodies or { }
22local findfile       = resolvers.findfile
23
24local typefaces      = fonts.typefaces or { }
25fonts.typefaces      = typefaces
26
27-- the following takes care of explicit file specifications
28--
29-- files = {
30--     name = "antykwapoltawskiego",
31--     list = {
32--         ["AntPoltLtCond-Regular.otf"] = {
33--          -- name   = "antykwapoltawskiego",
34--             style  = "regular",
35--             weight = "light",
36--             width  = "condensed",
37--         },
38--     },
39-- }
40
41-- files
42
43local function initialize(goodies)
44    local files = goodies.files
45    if files then
46        fonts.names.register(files)
47    end
48end
49
50fontgoodies.register("files", initialize)
51
52-- some day we will have a define command and then we can also do some
53-- proper tracing
54--
55-- fonts.typefaces["antykwapoltawskiego-condensed"] = {
56--     shortcut     = "rm",
57--     shape        = "serif",
58--     fontname     = "antykwapoltawskiego",
59--     normalweight = "light",
60--     boldweight   = "medium",
61--     width        = "condensed",
62--     size         = "default",
63--     features     = "default",
64-- }
65
66local function initialize(goodies)
67    local typefaces = goodies.typefaces
68    if typefaces then
69        local ft = fonts.typefaces
70        for k, v in next, typefaces do
71            ft[k] = v
72        end
73    end
74end
75
76fontgoodies.register("typefaces", initialize)
77
78local compositions = { }
79
80function fontgoodies.getcompositions(tfmdata)
81    return compositions[file.nameonly(tfmdata.properties.filename or "")]
82end
83
84local function initialize(goodies)
85    local gc = goodies.compositions
86    if gc then
87        for k, v in next, gc do
88            compositions[k] = v
89        end
90    end
91end
92
93fontgoodies.register("compositions", initialize)
94
95-- extra treatments (on top of defaults): \loadfontgoodies[mytreatments]
96
97local treatmentdata = fonts.treatments.data
98
99local function initialize(goodies)
100    local treatments = goodies.treatments
101    if treatments then
102        for name, data in next, treatments do
103            treatmentdata[name] = data -- always wins
104        end
105    end
106end
107
108fontgoodies.register("treatments", initialize)
109
110local filenames       = fontgoodies.filenames or allocate()
111fontgoodies.filenames = filenames
112
113local filedata        = filenames.data or allocate()
114filenames.data        = filedata
115
116local function initialize(goodies) -- design sizes are registered global
117    local fn = goodies.filenames
118    if fn then
119        for usedname, alternativenames in next, fn do
120            filedata[usedname] = alternativenames
121        end
122    end
123end
124
125fontgoodies.register("filenames", initialize)
126
127local getfilename = fonts.names.getfilename
128
129function fontgoodies.filenames.resolve(name)
130    local fd = filedata[name]
131    if fd and findfile(name) == "" then
132        -- maybe not:
133        name = getfilename(name)
134        if findfile(name) ~= "" then
135            return name
136        end
137        -- till here
138        for i=1,#fd do
139            local fn = fd[i]
140            if findfile(fn) ~= "" then
141                return fn
142            end
143            -- new:
144            fn = getfilename(fn)
145            if findfile(fn) ~= "" then
146                return fn
147            end
148        end
149    elseif filesuffix(name) == "any" then
150        -- This is a bit weird place but it's a kind of fallback option in case
151        -- we can't resolve due to a name conflict.
152        local sequence = fonts.readers.sequence
153        for i=1,#sequence do
154            local fn = replacesuffix(name,sequence[i])
155            if findfile(fn) ~= "" then
156                return fn
157            end
158        end
159    else
160        -- no lookup, just use the regular mechanism
161    end
162    return name
163end
164
165local designsizes       = fontgoodies.designsizes or allocate()
166fontgoodies.designsizes = designsizes
167
168local designdata        = designsizes.data or allocate()
169designsizes.data        = designdata
170
171local function initialize(goodies) -- design sizes are registered global
172    local gd = goodies.designsizes
173    if gd then
174        for name, data in next, gd do
175            local ranges = { }
176            for size, file in next, data do
177                if size ~= "default" then
178                    ranges[#ranges+1] =  { texsp(size), file } -- also lower(file)
179                end
180            end
181            table.sort(ranges,function(a,b) return a[1] < b[1] end)
182            designdata[lower(name)] = { -- overloads, doesn't merge!
183                default = data.default,
184                ranges  = ranges,
185            }
186        end
187    end
188end
189
190fontgoodies.register("designsizes", initialize)
191
192function fontgoodies.designsizes.register(name,size,specification)
193    local d = designdata[name]
194    if not d then
195        d = {
196            ranges  = { },
197            default = nil, -- so we have no default set
198        }
199        designdata[name] = d
200    end
201    if size == "default" then
202        d.default = specification
203    else
204        if type(size) == "string" then
205            size = texsp(size) -- hm
206        end
207        local ranges = d.ranges
208        ranges[#ranges+1] = { size, specification }
209    end
210end
211
212function fontgoodies.designsizes.filename(name,spec,size,force) -- returns nil of no match
213    local data = name and designdata[lower(name)]
214    if data then
215        if not spec or spec == "" or spec == "default" then
216            return data.default
217        elseif not force and texconditionals["c_font_compact"] then
218            return data.default
219        elseif spec == "auto" then
220            local ranges = data.ranges
221            if ranges then
222                for i=1,#ranges do
223                    local r = ranges[i]
224                    if r[1] >= size then -- todo: rounding so maybe size - 100
225                        return r[2]
226                    end
227                end
228            end
229            return data.default or (ranges and ranges[#ranges][2])
230        end
231    end
232end
233