1if not modules then modules = { } end modules ['font-sel'] = {
2 version = 1.001,
3 comment = "companion to font-sel.mkvi",
4 author = "Wolfgang Schuster",
5 copyright = "Wolfgang Schuster",
6 license = "GNU General Public License"
7}
8
9local next, type = next, type
10
11local context = context
12local cleanname = fonts.names.cleanname
13local gsub, splitup, find, lower = string.gsub, string.splitup, string.find, string.lower
14local concat, sortedkeys = table.concat, table.sortedkeys
15local merge, remove = table.merge, table.remove
16local splitbase, removesuffix = file.splitbase, file.removesuffix
17local splitat, lpegmatch = lpeg.splitat, lpeg.match
18
19local formatters = string.formatters
20local settings_to_array = utilities.parsers.settings_to_array
21local settings_to_hash = utilities.parsers.settings_to_hash
22local allocate = utilities.storage.allocate
23
24local v_default = interfaces.variables.default
25
26local implement = interfaces.implement
27
28local fonts = fonts
29
30local getlookups = fonts.names.getlookups
31local registerdesignsizes = fonts.goodies.designsizes.register
32local bodyfontsizes = storage.shared.bodyfontsizes
33
34fonts.select = fonts.select or { }
35local selectfont = fonts.select
36
37selectfont.data = selectfont.data or allocate()
38selectfont.fallbacks = selectfont.fallbacks or allocate()
39selectfont.methods = selectfont.methods or allocate()
40selectfont.extras = selectfont.extras or allocate()
41selectfont.alternatives = selectfont.alternatives or allocate()
42selectfont.presets = selectfont.presets or allocate()
43selectfont.defaults = selectfont.defaults or allocate()
44
45storage.register("fonts/select/presets", selectfont.presets, "fonts.select.presets")
46
47local data = selectfont.data
48local fallbacks = selectfont.fallbacks
49local methods = selectfont.methods
50local extras = selectfont.extras
51local alternatives = selectfont.alternatives
52local presets = selectfont.presets
53local defaults = selectfont.defaults
54
55local ctx_definefontsynonym = context.definefontsynonym
56local ctx_resetfontfallback = context.resetfontfallback
57local ctx_startfontclass = context.startfontclass
58local ctx_stopfontclass = context.stopfontclass
59local ctx_loadfontgoodies = context.loadfontgoodies
60local ctx_definefontfallback = context.definefontfallback
61local ctx_definetypeface = context.definetypeface
62local ctx_definebodyfont = context.definebodyfont
63
64local trace_register = false trackers.register("selectfont.register", function(v) trace_register = v end)
65local trace_files = false trackers.register("selectfont.files", function(v) trace_files = v end)
66local trace_features = false trackers.register("selectfont.features", function(v) trace_features = v end)
67local trace_goodies = false trackers.register("selectfont.goodies", function(v) trace_goodies = v end)
68local trace_alternatives = false trackers.register("selectfont.alternatives", function(v) trace_alternatives = v end)
69local trace_typescript = false trackers.register("selectfont.typescripts", function(v) trace_typescript = v end)
70
71local report_selectfont = logs.reporter("selectfont")
72local report_files = logs.reporter("selectfont","files")
73local report_features = logs.reporter("selectfont","features")
74local report_goodies = logs.reporter("selectfont","goodies")
75local report_typescript = logs.reporter("selectfont","typescripts")
76
77defaults["rm"] = { features = { ["sc"] = "*,f:smallcaps" } }
78defaults["ss"] = { features = { ["sc"] = "*,f:smallcaps" } }
79
80
81
82defaults["asanamath"] = { options = { extras = "asana-math", features = "math\\mathsizesuffix,mathextra", goodies = "anana-math" } }
83defaults["cambriamath"] = { options = { extras = "cambria-math", features = "math\\mathsizesuffix,mathextra", goodies = "cambria-math" } }
84defaults["euler"] = { options = { extras = "euler-math", features = "math\\mathsizesuffix,mathextra", goodies = "euler-math" } }
85defaults["latinmodernmath"] = { options = { extras = "lm,lm-math", features = "math\\mathsizesuffix,mathextra,lm-math", goodies = "modern" } }
86defaults["lucidabrightmathot"] = { options = { extras = "lucida-opentype-math", features = "math\\mathsizesuffix,mathextra", goodies = "lucida-math" } }
87defaults["minionmath"] = { options = { extras = "minion-math", features = "math\\mathsizesuffix,mathextra", goodies = "minion-math" } }
88defaults["stixtwomath"] = { options = { extras = "stix-two-math", features = "math\\mathsizesuffix,mathextra", goodies = "stixtwo-math" } }
89defaults["texgyredejavumath"] = { options = { extras = "dejavu", features = "math\\mathsizesuffix,mathextra", goodies = "dejavu-math" } }
90defaults["texgyrepagellamath"] = { options = { extras = "texgyre", features = "math\\mathsizesuffix,mathextra", goodies = "pagella-math" } }
91defaults["texgyrebonummath"] = { options = { extras = "texgyre", features = "math\\mathsizesuffix,mathextra", goodies = "bonum-math" } }
92defaults["texgyrescholamath"] = { options = { extras = "texgyre", features = "math\\mathsizesuffix,mathextra", goodies = "schola-math" } }
93defaults["texgyretermesmath"] = { options = { extras = "texgyre", features = "math\\mathsizesuffix,mathextra", goodies = "termes-math" } }
94defaults["xitsmath"] = { options = { extras = "xits-math", features = "math\\mathsizesuffix,mathextra", goodies = "xits-math" } }
95
96defaults["neoeuler"] = defaults["euler"]
97defaults["dejavumath"] = defaults["texgyredejavumath"]
98
99extras["features"] = function(data,alternative,features)
100 local d = data.options.features
101 local e = gsub(gsub(features,"*",d),"{(.*)}","%1")
102 local f = data.features
103 if trace_features then
104 report_features("alternative '%s': saving features '%s'",alternative,e)
105 end
106 if not f then
107 f = { }
108 data.features = f
109 end
110 f[alternative] = e
111end
112
113extras["goodies"] = function(data,alternative,goodies)
114 local e = gsub(goodies,"{(.*)}","%1")
115 local g = data.goodies
116 if trace_goodies then
117 report_goodies("alternative '%s': saving goodies '%s'",alternative,e)
118 end
119 if not g then
120 g = { }
121 data.goodies = g
122 end
123 g[alternative] = e
124end
125
126local function selectfont_savefile(data,alternative,bodyfontsize,size,file)
127 local f = data.files
128 local p, n = splitbase(file["filename"])
129 local t = file["format"]
130 local r = file["rawname"]
131 if t == "ttc" then
132 n = formatters["%s(%s)"](n,r)
133 end
134 if not f then
135 f = { }
136 data.files = f
137 end
138 local a = f[alternative]
139 if not a then
140 a = { }
141 f[alternative] = a
142 end
143 a[bodyfontsize] = { size, n }
144 if trace_files then
145 report_files("alternative '%s': saving file '%s' for size '%s'",alternative,n,size)
146 end
147end
148
149methods["name"] = function(data,alternative,name)
150 local family = data.metadata.family
151 local filename = cleanname(gsub(name,"*",family))
152 if trace_alternatives then
153 report_selectfont("alternative '%s': using method 'name' with argument '%s'",alternative,filename)
154 end
155 local fontname = getlookups{ fontname = filename }
156 local fullname = getlookups{ fullname = filename }
157 if #fontname > 0 then
158 selectfont_savefile(data,alternative,0,"default",fontname[1])
159 elseif #fullname > 0 then
160 selectfont_savefile(data,alternative,0,"default",fullname[1])
161 else
162 if trace_alternatives then
163 report_selectfont("alternative '%s': no font was found for the requested name '%s'",alternative,filename)
164 end
165 end
166end
167
168methods["file"] = function(data,alternative,file)
169 local family = data.metadata.family
170 local filename = cleanname(gsub(removesuffix(file),"*",family))
171 if trace_alternatives then
172 report_selectfont("alternative '%s': using method 'file' with argument '%s'",alternative,filename)
173 end
174 local filename = getlookups{ cleanfilename = cleanname(filename) }
175 if #filename > 0 then
176 selectfont_savefile(data,alternative,0,"default",filename[1])
177 else
178 if trace_alternatives then
179 report_selectfont("alternative '%s': no font was found for the requested file '%s'",alternative,cleanname(gsub(removesuffix(file),"*",family)))
180 end
181 end
182end
183
184local m_weight = {
185 ["thin"] = 100,
186 ["extralight"] = 200,
187 ["light"] = 300,
188 ["regular"] = 400,
189 ["medium"] = 500,
190 ["semibold"] = 600,
191 ["bold"] = 700,
192 ["extrabold"] = 800,
193 ["black"] = 900
194}
195
196local m_width = {
197 ["ultracondensed"] = 1,
198 ["extracondensed"] = 2,
199 ["condensed"] = 3,
200 ["semicondensed"] = 4,
201 ["normal"] = 5,
202 ["semiexpanded"] = 6,
203 ["expanded"] = 7,
204 ["extraexpanded"] = 8,
205 ["ultraexpanded"] = 9,
206}
207
208local m_name = {
209 ["thin"] = { weight = "thin" },
210 ["thinitalic"] = { weight = "thin", style = "italic" },
211 ["extralight"] = { weight = "extralight" },
212 ["extralightitalic"] = { weight = "extralight", style = "italic" },
213 ["light"] = { weight = "light" },
214 ["lightitalic"] = { weight = "light", style = "italic" },
215 ["regular"] = { weight = { "regular", "medium" } },
216 ["italic"] = { weight = { "regular", "medium" }, style = "italic" },
217 ["medium"] = { weight = "medium" },
218 ["mediumitalic"] = { weight = "medium", style = "italic" },
219 ["semibold"] = { weight = "semibold" },
220 ["semibolditalic"] = { weight = "semibold", style = "italic" },
221 ["bold"] = { weight = { "bold", "semibold" } },
222 ["bolditalic"] = { weight = { "bold", "semibold" }, style = "italic" },
223 ["extrabold"] = { weight = "extrabold" },
224 ["extrabolditalic"] = { weight = "extrabold", style = "italic" },
225 ["black"] = { weight = "black" },
226 ["blackitalic"] = { weight = "black", style = "italic" },
227 ["smallcaps"] = { weight = "regular", variant = "smallcaps" },
228}
229
230local m_alternative = {
231 ["tf"] = "regular",
232 ["bf"] = "bold",
233 ["it"] = "italic",
234 ["sl"] = "italic",
235 ["bi"] = "bolditalic",
236 ["bs"] = "bolditalic",
237 ["sc"] = "smallcaps"
238}
239
240local function m_style_family(family)
241 local askedname = cleanname(family)
242 local familyname = getlookups{ familyname = askedname }
243 local family = getlookups{ family = askedname }
244 local fontname = getlookups{ fontname = askedname }
245 if #familyname > 0 then
246 return familyname
247 elseif #family > 0 then
248 return family
249 elseif #fontname > 0 then
250 local fontfamily = fontname[1]["familyname"]
251 report_selectfont("the name '%s' is not a proper family name, use '%s' instead.",askedname,fontfamily)
252 return nil
253 else
254 return nil
255 end
256end
257
258local function m_style_subfamily(entries,style,family)
259 local t = { }
260 local style = cleanname(style)
261 local family = cleanname(family)
262 for index, entry in next, entries do
263 if entry["familyname"] == family and entry["subfamilyname"] == style then
264 t[#t+1] = entry
265 elseif entry["family"] == family and entry["subfamily"] == style then
266 t[#t+1] = entry
267 end
268 end
269 return #t ~= 0 and t or nil
270end
271
272local function m_style_weight(entries,style)
273 local t = { }
274 local weight = m_name[style] and m_name[style]["weight"] or "regular"
275 if type(weight) == "table" then
276 for _, w in next, weight do
277 local found = false
278 local pfmweight = m_weight[w]
279 for index, entry in next, entries do
280 if entry["pfmweight"] == pfmweight then
281 found = true
282 t[#t+1] = entry
283 elseif entry["weight"] == w then
284 found = true
285 t[#t+1] = entry
286 end
287 end
288 if found then break end
289 end
290 else
291 local pfmweight = m_weight[weight]
292 for index, entry in next, entries do
293 if entry["pfmweight"] == pfmweight then
294 t[#t+1] = entry
295 elseif entry["weight"] == weight then
296 t[#t+1] = entry
297 end
298 end
299 end
300 return #t ~= 0 and t or nil
301end
302
303local function m_style_style(entries,style)
304 local t = { }
305 local style = m_name[style] and m_name[style]["style"] or "normal"
306 for index, entry in next, entries do
307 if style == "italic" and entry["angle"] and entry["angle"] ~= 0 then
308 t[#t+1] = entry
309 elseif style == "normal" and entry["angle"] and entry["angle"] ~= 0 then
310
311 elseif entry["style"] == style then
312 t[#t+1] = entry
313 end
314 end
315 return #t ~= 0 and t or nil
316end
317
318local function m_style_variant(entries,style)
319 local t = { }
320 local variant = m_name[style] and m_name[style]["variant"] or "normal"
321 for index, entry in next, entries do
322 if entry["variant"] == variant then
323 t[#t+1] = entry
324 end
325 end
326 return #t ~= 0 and t or nil
327end
328
329local function m_style_width(entries,style)
330 local t = { }
331 local width = m_name[style] and m_name[style]["width"] or "normal"
332 local pfmwidth = m_width[width]
333 for index, entry in next, entries do
334 if entry["pfmwidth"] == pfmwidth then
335 t[#t+1] = entry
336 end
337 end
338 return #t ~= 0 and t or nil
339end
340
341local function m_style_size(data,alternative,entries)
342 if #entries == 1 then
343 selectfont_savefile(data,alternative,0,"default",entries[1])
344 else
345 for index, entry in next, entries do
346 local minsize = entry["minsize"]
347 local maxsize = entry["maxsize"]
348 if minsize and maxsize then
349 for size, state in next, bodyfontsizes do
350 local bodyfontsize, _ = number.splitdimen(size)
351 bodyfontsize = bodyfontsize * 10
352 if minsize < bodyfontsize and bodyfontsize < maxsize then
353 if bodyfontsize == 100 then
354 selectfont_savefile(data,alternative,0,"default",entry)
355 end
356 selectfont_savefile(data,alternative,bodyfontsize,size,entry)
357 end
358 end
359 else
360 if trace_alternatives then
361 report_selectfont("alternative '%s': multiple files are available for the requested style '%s' from '%s'",alternative,style,family)
362 end
363 end
364 end
365 end
366end
367
368methods["style"] = function(data,alternative,style)
369 local fontfamily = data.metadata.family
370 local designsize = data.options.designsize
371 local fontstyle = m_alternative[style] or style
372 local entries = m_style_family(fontfamily)
373 if entries then
374 local subfamily = m_style_subfamily(entries,fontstyle,fontfamily)
375 if subfamily then
376 entries = subfamily
377 else
378 entries = m_style_weight(entries,fontstyle)
379 if entries then
380 entries = m_style_style(entries,fontstyle)
381 if entries then
382 entries = m_style_variant(entries,fontstyle)
383 if entries and #entries > 1 and designsize == "default" then
384 entries = m_style_width(entries,fontstyle)
385 end
386 end
387 end
388 end
389 end
390 if entries then
391 m_style_size(data,alternative,entries)
392 else
393 if trace_alternatives then
394 report_selectfont("alternative '%s': no font was found for the requested style '%s' from '%s'",alternative,style,family)
395 end
396 end
397end
398
399methods[v_default] = function(data,alternative)
400 local family = data.metadata.family
401 if trace_alternatives then
402 report_selectfont("alternative '%s': using method 'default'",alternative)
403 end
404 local result = getlookups{ familyname = cleanname(family) }
405 if #result == 1 and alternative == "tf" then
406 if trace_alternatives then
407 report_selectfont("alternative '%s': the family '%s' contains only one font",alternative,family)
408 end
409 selectfont_savefile(data,alternative,0,"default",result[1])
410
411
412
413
414 else
415 if trace_alternatives then
416 report_selectfont("alternative '%s': changing method 'default' to method 'style'",alternative)
417 end
418 methods["style"](data,alternative,alternative)
419 end
420end
421
422local function selectfont_savealternative(data,alternative,userdata)
423 local a = data.alternatives
424 local e = userdata[alternative]
425 if not a then
426 a = { }
427 data.alternatives = a
428 end
429 a[alternative] = e
430end
431
432function selectfont.fontdata(index)
433 local data = data[index]
434 local style = data.metadata.style
435 local defaults = defaults[style]
436 if defaults then
437 for category, argument in next, defaults do
438 local extra = extras[category]
439 if extra then
440 for alternative, entry in next, argument do
441 extra(data,alternative,entry)
442 end
443 end
444 end
445 end
446end
447
448function selectfont.userdata(index)
449 local data = data[index]
450 local preset = data.options.preset
451 local presets = presets[preset]
452 local userdata = settings_to_hash(data.userdata)
453 if presets then
454 merge(userdata,presets)
455 end
456 for alternative, _ in next, alternatives do
457 selectfont_savealternative(data,alternative,userdata)
458 end
459end
460
461function selectfont.registerfiles(index)
462 local data = data[index]
463 local colon = splitat(":",true)
464 for alternative, _ in next, alternatives do
465 local arguments = data.alternatives[alternative]
466 if arguments and arguments ~= "" then
467 local entries = settings_to_array(arguments)
468 for index, entry in next, entries do
469 method, argument = lpegmatch(colon,entry)
470 if not argument then
471 argument = method
472 method = "name"
473 end
474 if #entries == 1 and method == "features" then
475 extras["features"](data,alternative,argument)
476 methods[v_default](data,alternative)
477 else
478 (extras[method] or methods[method] or methods[v_default])(data,alternative,argument)
479 end
480 end
481 else
482 methods[v_default](data,alternative)
483 end
484 end
485end
486
487function selectfont.registerfontalternative(alternative)
488 local a = alternatives[alternative]
489 if not a then
490 if trace_register then
491 report_selectfont("register alternative '%s'",alternative)
492 end
493 a = true
494 alternatives[alternative] = a
495 end
496end
497
498function selectfont.registerfallback(index)
499 local data = data[index]
500 local fontclass = data.metadata.typeface
501 local fontstyle = data.metadata.style
502 local fallback = fallbacks[fontclass]
503 if not fallback then
504 fallback = { }
505 fallbacks[fontclass] = fallback
506 end
507 local entries = fallback[fontstyle]
508 if not entries then
509 entries = { }
510 fallback[fontstyle] = entries
511 end
512 entries[#entries+1] = index
513end
514
515function selectfont.registerfontfamily(settings)
516 local index = #data + 1
517 data[index] = settings
518 selectfont.fontdata (index)
519 selectfont.userdata (index)
520 selectfont.registerfiles(index)
521 return index
522end
523
524local m_synonym = {
525 ["rm"] = {
526 ["tf"] = "Serif",
527 ["bf"] = "SerifBold",
528 ["it"] = "SerifItalic",
529 ["sl"] = "SerifSlanted",
530 ["bi"] = "SerifBoldItalic",
531 ["bs"] = "SerifBoldSlanted",
532 ["sc"] = "SerifCaps",
533 },
534 ["ss"] = {
535 ["tf"] = "Sans",
536 ["bf"] = "SansBold",
537 ["it"] = "SansItalic",
538 ["sl"] = "SansSlanted",
539 ["bi"] = "SansBoldItalic",
540 ["bs"] = "SansBoldSlanted",
541 ["sc"] = "SansCaps",
542 },
543 ["tt"] = {
544 ["tf"] = "Mono",
545 ["bf"] = "MonoBold",
546 ["it"] = "MonoItalic",
547 ["sl"] = "MonoSlanted",
548 ["bi"] = "MonoBoldItalic",
549 ["bs"] = "MonoBoldSlanted",
550 ["sc"] = "MonoCaps",
551 },
552 ["mm"] = {
553 ["tf"] = "MathRoman",
554 ["bf"] = "MathBold",
555 },
556 ["hw"] = {
557 ["tf"] = "Handwriting",
558 },
559 ["cg"] = {
560 ["tf"] = "Calligraphy",
561 },
562}
563
564function selectfont.features(data,style,alternative)
565 local family = data.metadata.family
566 local features = data.features
567 local options = data.options
568 local defaults = defaults[cleanname(family)]
569 if features and features[alternative] then
570 return features[alternative]
571 elseif defaults and defaults.options and defaults.options.features then
572 return defaults.options.features
573 else
574 return options.features
575 end
576end
577
578function selectfont.goodies(data,style,alternative)
579 local family = data.metadata.family
580 local goodies = data.goodies
581 local options = data.options
582 local defaults = defaults[cleanname(family)]
583 if goodies and goodies[alternative] then
584 return goodies[alternative]
585 elseif defaults and defaults.options and defaults.options.goodies then
586 return defaults.options.goodies
587 else
588 return options.goodies
589 end
590end
591
592function selectfont.fontsynonym(data,class,style,alternative,index)
593 local fontfiles = data.files[alternative] or data.files["tf"]
594 local fontsizes = sortedkeys(fontfiles)
595 local fallback = index ~= 0
596 local fontclass = lower(class)
597
598
599 local fontfeature = selectfont.features(data,style,alternative)
600 local fontgoodie = selectfont.goodies (data,style,alternative)
601 local synonym = m_synonym[style] and m_synonym[style][alternative]
602 local fontfile = formatters ["file-%s-%s-%s"](fontclass,style,alternative)
603 local fontsynonym = formatters ["synonym-%s-%s-%s"](fontclass,style,alternative)
604 if fallback then
605 fontfile = formatters ["file-%s-%s-%s-%s"](fontclass,style,alternative,index)
606 fontsynonym = formatters ["synonym-%s-%s-%s-%s"](fontclass,style,alternative,index)
607 end
608 local fontfallback = formatters["fallback-%s-%s-%s"](fontclass,style,alternative)
609 for _, fontsize in next, fontsizes do
610
611
612
613 registerdesignsizes(fontfile,fontfiles[fontsize][1],fontfiles[fontsize][2])
614 end
615 if fallback then
616
617
618
619 ctx_definefontsynonym( { fontsynonym }, { fontfile }, { features = fontfeature } )
620 else
621
622
623
624 ctx_definefontsynonym( { fontsynonym }, { fontfile }, { features = fontfeature, goodies = fontgoodie, fallbacks = fontfallback } )
625 if synonym then
626
627
628
629 ctx_definefontsynonym( { synonym }, { fontsynonym } )
630 end
631 end
632end
633
634function selectfont.fontfallback(data,class,style,alternative,index)
635 local range = data.options.range
636 local scale = data.options.rscale ~= "" and data.options.rscale or 1
637 local check = data.options.check ~= "" and data.options.check or ""
638 local force = data.options.force ~= "" and data.options.force or ""
639 local fontfeature = data.features and data.features[alternative] or data.options.features
640 local fontclass = lower(class)
641 local fontsynonym = formatters["synonym-%s-%s-%s-%s"](fontclass,style,alternative,index)
642 local fontfallback = formatters["fallback-%s-%s-%s"] (fontclass,style,alternative)
643 if index == 1 then
644 ctx_resetfontfallback( { fontfallback } )
645 end
646
647
648
649 ctx_definefontfallback( { fontfallback }, { fontsynonym }, { range }, { rscale = scale, check = check, force = force } )
650end
651
652function selectfont.filefallback(data,class,style,alternative,index)
653 local range = data.options.range
654 local offset = data.options.offset
655 local scale = data.options.rscale ~= "" and data.options.rscale or 1
656 local check = data.options.check ~= "" and data.options.check or "yes"
657 local force = data.options.force ~= "" and data.options.force or "yes"
658 local fontfile = data.files[alternative] and data.files[alternative][0] or data.files["tf"][0]
659 local fontfeature = data.features and data.features[alternative] or data.options.features
660 local fontclass = lower(class)
661 local fontfallback = formatters["fallback-%s-%s-%s"](fontclass,style,alternative)
662 if index == 1 then
663 ctx_resetfontfallback( { fontfallback } )
664 end
665
666
667
668 ctx_definefontfallback( { fontfallback }, { formatters["file:%s*%s"](fontfile[2],fontfeature) }, { range }, { rscale = scale, check = check, force = force, offset = offset } )
669end
670
671function selectfont.mathfallback(index,entry,class,style)
672 local data = data[entry]
673 ctx_startfontclass( { class } )
674 for alternative, _ in next, alternatives do
675 if alternative == "tf" or alternative == "bf" then
676 selectfont.filefallback(data,class,style,alternative,index)
677 end
678 end
679 ctx_stopfontclass()
680end
681
682function selectfont.textfallback(index,entry,class,style)
683 local data = data[entry]
684 ctx_startfontclass( { class } )
685 for alternative, _ in next, alternatives do
686 selectfont.fontsynonym (data,class,style,alternative,index)
687 selectfont.fontfallback(data,class,style,alternative,index)
688 end
689 ctx_stopfontclass()
690end
691
692function selectfont.fallback(data)
693 local fontclass = data.metadata.typeface
694 local fontstyle = data.metadata.style
695 local fallbacks = fallbacks[fontclass] and fallbacks[fontclass][fontstyle]
696 if fallbacks then
697 for index, entry in next, fallbacks do
698
699
700
701 if fontstyle == "mm" then
702 selectfont.mathfallback(index,entry,fontclass,fontstyle)
703 else
704 selectfont.textfallback(index,entry,fontclass,fontstyle)
705 end
706 end
707 end
708end
709
710function selectfont.typescript(data)
711 local class = data.metadata.typeface
712 local family = data.metadata.family
713 local style = data.metadata.style
714 local extras = data.options.extras
715 local defaults = defaults[cleanname(family)]
716 if extras == "" then
717 extras = defaults and defaults.options and defaults.options.extras or ""
718 end
719 ctx_startfontclass( { class } )
720 if extras ~= "" then
721 extras = settings_to_array(extras)
722 for _, extra in next, extras do
723 ctx_loadfontgoodies( { extra } )
724 end
725 end
726 for alternative, _ in next, alternatives do
727 if style == "mm" then
728
729 if alternative == "tf" or alternative == "bf" then
730 selectfont.fontsynonym (data,class,style,alternative,0)
731 end
732 else
733 selectfont.fontsynonym (data,class,style,alternative,0)
734 end
735 end
736 ctx_stopfontclass()
737end
738
739function selectfont.bodyfont(data)
740 local class = data.metadata.typeface
741 local fontstyle = data.metadata.style
742 local fontclass = lower(class)
743 local fontsizes = concat(sortedkeys(bodyfontsizes),",")
744 local fontsynonym = nil
745 local fontlist = { }
746 for alternative, _ in next, alternatives do
747 fontsynonym = formatters["synonym-%s-%s-%s"](fontclass,fontstyle,alternative)
748 fontlist[#fontlist+1] = formatters["%s=%s sa 1"] (alternative,fontsynonym)
749
750
751
752 end
753 fontlist = concat(fontlist,",")
754 ctx_definebodyfont( { class }, { fontsizes }, { fontstyle }, { fontlist } )
755end
756
757local m_style = {
758 ["rm"] = "serif",
759 ["ss"] = "sans",
760 ["tt"] = "mono",
761 ["mm"] = "math",
762 ["hw"] = "handwriting",
763 ["cg"] = "calligraphy",
764}
765
766function selectfont.typeface(data)
767 local fontclass = data.metadata.typeface
768 local fontstyle = data.metadata.style
769 local style = m_style[fontstyle]
770 local size = data.options.designsize ~= "" and data.options.designsize or "default"
771 local scale = data.options.rscale ~= "" and data.options.rscale or 1
772
773
774
775 ctx_definetypeface( { fontclass }, { fontstyle }, { style }, { "" }, { "default" }, { designsize = size, rscale = scale } )
776end
777
778function selectfont.default(data)
779 local family = data.metadata.family
780 local fontclass = data.metadata.typeface
781 local fontstyle = data.metadata.style
782 local style = m_style[fontstyle]
783 report_selectfont("the requested font '%s' has no files for the 'tf' alternative, Latin Modern is used instead.",family)
784 ctx_definetypeface( { fontclass }, { fontstyle }, { style }, { "modern" }, { "default" } )
785end
786
787function selectfont.definefontfamily(index)
788 local data = data[index]
789 local fontstyle = data.metadata.style
790 local fontfiles = data.files and data.files["tf"]
791 if fontfiles then
792 selectfont.fallback (data)
793 selectfont.typescript(data)
794 if fontstyle ~= "mm" then
795 selectfont.bodyfont(data)
796 end
797 selectfont.typeface(data)
798 else
799 selectfont.default(data)
800 end
801end
802
803function selectfont.definefallbackfamily(index)
804 local data = data[index]
805 local family = data.metadata.family
806 local fontclass = data.metadata.typeface
807 local fontstyle = data.metadata.style
808 local fontfiles = data.files
809 if fontfiles then
810 selectfont.registerfallback(index)
811 else
812 report_selectfont("the requested fallback font '%s' for typeface '%s' style '%s' was ignored because no files where found.",family,fontclass,fontstyle)
813 end
814end
815
816function selectfont.definefontfamilypreset(name,data)
817 local p = presets[name]
818 local d = settings_to_hash(data)
819 if not p then
820 p = d
821 presets[name] = p
822 end
823end
824
825implement {
826 name = "registerfontfamily",
827 actions = { selectfont.registerfontfamily, context },
828 arguments = {
829 {
830 {
831 "metadata", {
832 { "typeface" },
833 { "style" },
834 { "family" }
835 }
836 },
837 {
838 "options", {
839 { "designsize" },
840 { "rscale" },
841 { "goodies" },
842 { "preset" },
843 { "extras" },
844 { "features" },
845 { "range" },
846 { "offset" },
847 { "check" },
848 { "force" }
849 }
850 },
851 {
852 "userdata"
853 }
854 }
855 }
856}
857
858implement {
859 name = "registerfontalternative",
860 actions = selectfont.registerfontalternative,
861 arguments = "string"
862}
863
864implement {
865 name = "definefontfamily",
866 actions = selectfont.definefontfamily,
867 arguments = "integer"
868}
869
870implement {
871 name = "definefallbackfamily",
872 actions = selectfont.definefallbackfamily,
873 arguments = "integer"
874}
875
876implement {
877 name = "definefontfamilypreset",
878 actions = selectfont.definefontfamilypreset,
879 arguments = "2 strings",
880}
881 |