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