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