1if not modules then modules = { } end modules [ ' font-syn ' ] = {
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
10
11
12
13
14
15
16
17
18
19local next , tonumber , type , tostring = next , tonumber , type , tostring
20local sub , gsub , match , find , lower , upper = string . sub , string . gsub , string . match , string . find , string . lower , string . upper
21local concat , sort , fastcopy , tohash = table . concat , table . sort , table . fastcopy , table . tohash
22local serialize , sortedhash = table . serialize , table . sortedhash
23local lpegmatch = lpeg . match
24local unpack = unpack or table . unpack
25local formatters , topattern = string . formatters , string . topattern
26local round = math . round
27local P , R , S , C , Cc , Ct , Cs = lpeg . P , lpeg . R , lpeg . S , lpeg . C , lpeg . Cc , lpeg . Ct , lpeg . Cs
28local lpegmatch , lpegpatterns = lpeg . match , lpeg . patterns
29local isfile , modificationtime = lfs . isfile , lfs . modification
30
31local allocate = utilities . storage . allocate
32local sparse = utilities . storage . sparse
33local setmetatableindex = table . setmetatableindex
34
35local removesuffix = file . removesuffix
36local splitbase = file . splitbase
37local splitname = file . splitname
38local basename = file . basename
39local nameonly = file . nameonly
40local pathpart = file . pathpart
41local suffixonly = file . suffix
42local filejoin = file . join
43local is_qualified_path = file . is_qualified_path
44local exists = io . exists
45
46local findfile = resolvers . findfile
47local cleanpath = resolvers . cleanpath
48local resolveprefix = resolvers . resolve
49
50local settings_to_hash = utilities . parsers . settings_to_hash_tolerant
51
52local trace_names = false trackers . register ( " fonts.names " , function ( v ) trace_names = v end )
53local trace_warnings = false trackers . register ( " fonts.warnings " , function ( v ) trace_warnings = v end )
54local trace_specifications = false trackers . register ( " fonts.specifications " , function ( v ) trace_specifications = v end )
55local trace_rejections = false trackers . register ( " fonts.rejections " , function ( v ) trace_rejections = v end )
56
57local report_names = logs . reporter ( " fonts " , " names " )
58
59
63
64fonts = fonts or { }
65
66local names = fonts . names or allocate { }
67fonts . names = names
68
69local filters = names . filters or { }
70names . filters = filters
71
72local treatments = fonts . treatments or { }
73fonts . treatments = treatments
74
75names . data = names . data or allocate { }
76
77names . version = 1 . 131
78names . basename = " names "
79names . saved = false
80names . loaded = false
81names . be_clever = true
82names . enabled = true
83names . cache = containers . define ( " fonts " , " data " , names . version , true )
84
85local usesystemfonts = true
86local autoreload = true
87
88directives . register ( " fonts.autoreload " , function ( v ) autoreload = toboolean ( v ) end )
89directives . register ( " fonts.usesystemfonts " , function ( v ) usesystemfonts = toboolean ( v ) end )
90
91
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123local weights = Cs (
124 P ( " demibold " )
125 + P ( " semibold " )
126 + P ( " mediumbold " )
127 + P ( " ultrabold " )
128 + P ( " extrabold " )
129 + P ( " ultralight " )
130 + P ( " extralight " )
131 + P ( " bold " )
132 + P ( " demi " )
133 + P ( " semi " )
134 + P ( " light " )
135 + P ( " medium " )
136 + P ( " heavy " )
137 + P ( " ultra " )
138 + P ( " black " )
139
140 + P ( " bol " )
141 + P ( " regular " ) / " normal "
142)
143
144
145
146
147
148
149
150
151
152
153
154
155
156local normalized_weights = sparse {
157 regular = " normal " ,
158}
159
160local styles = Cs (
161 P ( " reverseoblique " ) / " reverseitalic "
162 + P ( " regular " ) / " normal "
163 + P ( " italic " )
164 + P ( " oblique " ) / " italic "
165 + P ( " slanted " )
166 + P ( " roman " ) / " normal "
167 + P ( " ital " ) / " italic "
168 + P ( " ita " ) / " italic "
169
170)
171
172local normalized_styles = sparse {
173 reverseoblique = " reverseitalic " ,
174 regular = " normal " ,
175 oblique = " italic " ,
176}
177
178local widths = Cs (
179 P ( " condensed " )
180 + P ( " thin " )
181 + P ( " expanded " )
182 + P ( " cond " ) / " condensed "
183
184 + P ( " normal " )
185 + P ( " book " ) / " normal "
186)
187
188local normalized_widths = sparse ( )
189
190local variants = Cs (
191 P ( " smallcaps " )
192 + P ( " oldstyle " )
193 + P ( " caps " ) / " smallcaps "
194)
195
196local normalized_variants = sparse ( )
197
198names . knownweights = {
199 " black " ,
200 " bold " ,
201 " demi " ,
202 " demibold " ,
203 " extrabold " ,
204 " heavy " ,
205 " light " ,
206 " medium " ,
207 " mediumbold " ,
208 " normal " ,
209 " regular " ,
210 " semi " ,
211 " semibold " ,
212 " ultra " ,
213 " ultrabold " ,
214 " ultralight " ,
215}
216
217names . knownstyles = {
218 " italic " ,
219 " normal " ,
220 " oblique " ,
221 " regular " ,
222 " reverseitalic " ,
223 " reverseoblique " ,
224 " roman " ,
225 " slanted " ,
226}
227
228names . knownwidths = {
229 " book " ,
230 " condensed " ,
231 " expanded " ,
232 " normal " ,
233 " thin " ,
234}
235
236names . knownvariants = {
237 " normal " ,
238 " oldstyle " ,
239 " smallcaps " ,
240}
241
242local remappedweights = {
243 [ " " ] = " normal " ,
244 [ " bol " ] = " bold " ,
245}
246
247local remappedstyles = {
248 [ " " ] = " normal " ,
249}
250
251local remappedwidths = {
252 [ " " ] = " normal " ,
253}
254
255local remappedvariants = {
256 [ " " ] = " normal " ,
257}
258
259names . remappedweights = remappedweights setmetatableindex ( remappedweights , " self " )
260names . remappedstyles = remappedstyles setmetatableindex ( remappedstyles , " self " )
261names . remappedwidths = remappedwidths setmetatableindex ( remappedwidths , " self " )
262names . remappedvariants = remappedvariants setmetatableindex ( remappedvariants , " self " )
263
264local any = P ( 1 )
265
266local analyzed_table
267
268local analyzer = Cs (
269 (
270 weights / function ( s ) analyzed_table [ 1 ] = s return " " end
271 + styles / function ( s ) analyzed_table [ 2 ] = s return " " end
272 + widths / function ( s ) analyzed_table [ 3 ] = s return " " end
273 + variants / function ( s ) analyzed_table [ 4 ] = s return " " end
274 + any
275 ) ^ 0
276)
277
278local splitter = lpeg . splitat ( " - " )
279
280function names . splitspec ( askedname )
281 local name , weight , style , width , variant = lpegmatch ( splitter , askedname )
282 weight = weight and lpegmatch ( weights , weight ) or weight
283 style = style and lpegmatch ( styles , style ) or style
284 width = width and lpegmatch ( widths , width ) or width
285 variant = variant and lpegmatch ( variants , variant ) or variant
286 if trace_names then
287 report_names ( " requested name %a split in name %a, weight %a, style %a, width %a and variant %a " ,
288 askedname , name , weight , style , width , variant )
289 end
290 if not weight or not weight or not width or not variant then
291 weight , style , width , variant = weight or " normal " , style or " normal " , width or " normal " , variant or " normal "
292 if trace_names then
293 report_names ( " request %a normalized to '%s-%s-%s-%s-%s' " ,
294 askedname , name , weight , style , width , variant )
295 end
296 end
297 return name or askedname , weight , style , width , variant
298end
299
300local function analyzespec ( somename )
301 if somename then
302 analyzed_table = { }
303 local name = lpegmatch ( analyzer , somename )
304 return name , analyzed_table [ 1 ] , analyzed_table [ 2 ] , analyzed_table [ 3 ] , analyzed_table [ 4 ]
305 end
306end
307
308
312
313filters . afm = fonts . handlers . afm . readers . getinfo
314filters . otf = fonts . handlers . otf . readers . getinfo
315filters . ttf = filters . otf
316filters . ttc = filters . otf
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
420
421filters . list = {
422 " otf " , " ttf " , " ttc " , " afm " ,
423}
424
425
426
427names . fontconfigfile = " fonts.conf "
428names . osfontdirvariable = " OSFONTDIR "
429names . extrafontsvariable = " EXTRAFONTS "
430names . runtimefontsvariable = " RUNTIMEFONTS "
431
432filters . paths = { }
433filters . names = { }
434
435function names . getpaths ( trace )
436 local hash , result , r = { } , { } , 0
437 local function collect ( t , where )
438 for i = 1 , # t do
439 local v = cleanpath ( t [ i ] )
440 v = gsub ( v , " /+$ " , " " )
441 local key = lower ( v )
442 report_names ( " variable %a specifies path %a " , where , v )
443 if not hash [ key ] then
444 r = r + 1
445 result [ r ] = v
446 hash [ key ] = true
447 end
448 end
449 end
450 local path = names . osfontdirvariable or " "
451 if path ~ = " " then
452 collect ( resolvers . expandedpathlist ( path ) , path )
453 end
454 local path = names . extrafontsvariable or " "
455 if path ~ = " " then
456 collect ( resolvers . expandedpathlist ( path ) , path )
457 end
458 if xml then
459 local confname = resolvers . expansion ( " FONTCONFIG_FILE " ) or " "
460 if confname = = " " then
461 confname = names . fontconfigfile or " "
462 end
463 if confname ~ = " " then
464
465 local name = findfile ( confname , " fontconfig files " ) or " "
466 if name = = " " then
467
468 name = filejoin ( " /etc " , confname )
469 if not isfile ( name ) then
470 name = " "
471 end
472 end
473 if name ~ = " " and isfile ( name ) then
474 if trace_names then
475 report_names ( " %s fontconfig file %a " , " loading " , name )
476 end
477 local xmldata = xml . load ( name )
478
479 xml . include ( xmldata , " include " , " " , true , function ( incname )
480 if not is_qualified_path ( incname ) then
481 local path = pathpart ( name )
482 if path ~ = " " then
483 incname = filejoin ( path , incname )
484 end
485 end
486 if isfile ( incname ) then
487 if trace_names then
488 report_names ( " %s fontconfig file %a " , " merging included " , incname )
489 end
490 return io . loaddata ( incname )
491 elseif trace_names then
492 report_names ( " %s fontconfig file: %a " , " ignoring included " , incname )
493 end
494 end )
495
496 local fontdirs = xml . collect_texts ( xmldata , " dir " , true )
497 if trace_names then
498 report_names ( " %s dirs found in fontconfig " , # fontdirs )
499 end
500 collect ( fontdirs , " fontconfig file " )
501 end
502 end
503 end
504 sort ( result )
505 function names . getpaths ( )
506 return result
507 end
508 return result
509end
510
511local function cleanname ( name )
512 return ( gsub ( lower ( name ) , " [^%a%d] " , " " ) )
513end
514
515local function cleanfilename ( fullname , defaultsuffix )
516 if fullname then
517 local path , name , suffix = splitname ( fullname )
518 if name then
519 name = gsub ( lower ( name ) , " [^%a%d] " , " " )
520 if suffix and suffix ~ = " " then
521 return name . . " . " . . suffix
522 elseif defaultsuffix and defaultsuffix ~ = " " then
523 return name . . " . " . . defaultsuffix
524 else
525 return name
526 end
527 end
528 end
529 return " badfontname "
530end
531
532local sorter = function ( a , b )
533 return a > b
534end
535
536
537
538names . cleanname = cleanname
539names . cleanfilename = cleanfilename
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555local function check_name ( data , result , filename , modification , suffix , subfont )
556
557 local specifications = data . specifications
558
559 local fullname = result . fullname
560 local fontname = result . fontname
561 local family = result . family
562 local subfamily = result . subfamily
563 local familyname = result . familyname
564 local subfamilyname = result . subfamilyname
565
566
567 local weight = result . weight
568 local width = result . width
569 local italicangle = tonumber ( result . italicangle )
570 local subfont = subfont
571 local rawname = fullname or fontname or familyname
572 local filebase = removesuffix ( basename ( filename ) )
573 local cleanfilename = cleanname ( filebase )
574
575 fullname = fullname and cleanname ( fullname )
576 fontname = fontname and cleanname ( fontname )
577 family = family and cleanname ( family )
578 subfamily = subfamily and cleanname ( subfamily )
579 familyname = familyname and cleanname ( familyname )
580 subfamilyname = subfamilyname and cleanname ( subfamilyname )
581
582
583 weight = weight and cleanname ( weight )
584 width = width and cleanname ( width )
585 italicangle = italicangle = = 0 and nil
586
587 local a_name , a_weight , a_style , a_width , a_variant = analyzespec ( fullname or fontname or familyname )
588
589 local width = width or a_width
590 local variant = a_variant
591 local style = subfamilyname or subfamily
592 if style then
593 style = gsub ( style , " [^%a] " , " " )
594 elseif italicangle then
595 style = " italic "
596 end
597 if not variant or variant = = " " then
598 variant = " normal "
599 end
600 if not weight or weight = = " " then
601 weight = a_weight
602 end
603 if not style or style = = " " then
604 style = a_style
605 end
606 if not familyname then
607 familyname = a_name
608 end
609 fontname = fontname or fullname or familyname or filebase
610 fullname = fullname or fontname
611 familyname = familyname or fontname
612
613 local units = result . units or 1000
614 local designsize = result . designsize or 0
615 local minsize = result . minsize or 0
616 local maxsize = result . maxsize or 0
617 local angle = result . italicangle or 0
618 local pfmwidth = result . pfmwidth or 0
619 local pfmweight = result . pfmweight or 0
620
621 local instancenames = result . instancenames
622
623 specifications [ # specifications + 1 ] = {
624 filename = filename ,
625 cleanfilename = cleanfilename ,
626
627 format = lower ( suffix ) ,
628 subfont = subfont ,
629 rawname = rawname ,
630 fullname = fullname ,
631 fontname = fontname ,
632 family = family ,
633 subfamily = subfamily ,
634 familyname = familyname ,
635 subfamilyname = subfamilyname ,
636
637
638 weight = weight ,
639 style = style ,
640 width = width ,
641 variant = variant ,
642 units = units ~ = 1000 and units or nil ,
643 pfmwidth = pfmwidth ~ = 0 and pfmwidth or nil ,
644 pfmweight = pfmweight ~ = 0 and pfmweight or nil ,
645 angle = angle ~ = 0 and angle or nil ,
646 minsize = minsize ~ = 0 and minsize or nil ,
647 maxsize = maxsize ~ = 0 and maxsize or nil ,
648 designsize = designsize ~ = 0 and designsize or nil ,
649 modification = modification ~ = 0 and modification or nil ,
650 instancenames = instancenames or nil ,
651 }
652end
653
654local function cleanupkeywords ( )
655 local data = names . data
656 local specifications = names . data . specifications
657 if specifications then
658 local weights = { }
659 local styles = { }
660 local widths = { }
661 local variants = { }
662 for i = 1 , # specifications do
663 local s = specifications [ i ]
664
665 local _ , b_weight , b_style , b_width , b_variant = analyzespec ( s . weight )
666 local _ , c_weight , c_style , c_width , c_variant = analyzespec ( s . style )
667 local _ , d_weight , d_style , d_width , d_variant = analyzespec ( s . width )
668 local _ , e_weight , e_style , e_width , e_variant = analyzespec ( s . variant )
669 local _ , f_weight , f_style , f_width , f_variant = analyzespec ( s . fullname or " " )
670 local weight = b_weight or c_weight or d_weight or e_weight or f_weight or " normal "
671 local style = b_style or c_style or d_style or e_style or f_style or " normal "
672 local width = b_width or c_width or d_width or e_width or f_width or " normal "
673 local variant = b_variant or c_variant or d_variant or e_variant or f_variant or " normal "
674 weight = remappedweights [ weight or " " ]
675 style = remappedstyles [ style or " " ]
676 width = remappedwidths [ width or " " ]
677 variant = remappedvariants [ variant or " " ]
678 weights [ weight ] = ( weights [ weight ] or 0 ) + 1
679 styles [ style ] = ( styles [ style ] or 0 ) + 1
680 widths [ width ] = ( widths [ width ] or 0 ) + 1
681 variants [ variant ] = ( variants [ variant ] or 0 ) + 1
682 if weight ~ = s . weight then
683 s . fontweight = s . weight
684 end
685 s . weight , s . style , s . width , s . variant = weight , style , width , variant
686 end
687 local statistics = data . statistics
688 statistics . used_weights = weights
689 statistics . used_styles = styles
690 statistics . used_widths = widths
691 statistics . used_variants = variants
692 end
693end
694
695local function collectstatistics ( runtime )
696 local data = names . data
697 local specifications = data . specifications
698 local statistics = data . statistics
699 if specifications then
700 local f_w = formatters [ " %i " ]
701 local f_a = formatters [ " %0.2f " ]
702
703 local weights = { }
704 local styles = { }
705 local widths = { }
706 local variants = { }
707
708 local angles = { }
709
710 local pfmweights = { } setmetatableindex ( pfmweights , " table " )
711 local pfmwidths = { } setmetatableindex ( pfmwidths , " table " )
712
713 for i = 1 , # specifications do
714 local s = specifications [ i ]
715
716 local weight = s . weight
717 local style = s . style
718 local width = s . width
719 local variant = s . variant
720 if weight then weights [ weight ] = ( weights [ weight ] or 0 ) + 1 end
721 if style then styles [ style ] = ( styles [ style ] or 0 ) + 1 end
722 if width then widths [ width ] = ( widths [ width ] or 0 ) + 1 end
723 if variant then variants [ variant ] = ( variants [ variant ] or 0 ) + 1 end
724
725 local angle = f_a ( tonumber ( s . angle ) or 0 )
726 angles [ angle ] = ( angles [ angles ] or 0 ) + 1
727
728 local pfmweight = f_w ( s . pfmweight or 0 )
729 local pfmwidth = f_w ( s . pfmwidth or 0 )
730 local tweights = pfmweights [ pfmweight ]
731 local twidths = pfmwidths [ pfmwidth ]
732 tweights [ pfmweight ] = ( tweights [ pfmweight ] or 0 ) + 1
733 twidths [ pfmwidth ] = ( twidths [ pfmwidth ] or 0 ) + 1
734 end
735
736 statistics . weights = weights
737 statistics . styles = styles
738 statistics . widths = widths
739 statistics . variants = variants
740 statistics . angles = angles
741 statistics . pfmweights = pfmweights
742 statistics . pfmwidths = pfmwidths
743 statistics . fonts = # specifications
744
745 setmetatableindex ( pfmweights , nil )
746 setmetatableindex ( pfmwidths , nil )
747
748 report_names ( " " )
749 report_names ( " statistics: " )
750 report_names ( " " )
751 report_names ( " weights " )
752 report_names ( " " )
753 report_names ( formatters [ " %T " ] ( weights ) )
754 report_names ( " " )
755 report_names ( " styles " )
756 report_names ( " " )
757 report_names ( formatters [ " %T " ] ( styles ) )
758 report_names ( " " )
759 report_names ( " widths " )
760 report_names ( " " )
761 report_names ( formatters [ " %T " ] ( widths ) )
762 report_names ( " " )
763 report_names ( " variants " )
764 report_names ( " " )
765 report_names ( formatters [ " %T " ] ( variants ) )
766 report_names ( " " )
767 report_names ( " angles " )
768 report_names ( " " )
769 report_names ( formatters [ " %T " ] ( angles ) )
770 report_names ( " " )
771 report_names ( " pfmweights " )
772 report_names ( " " )
773 for k , v in sortedhash ( pfmweights ) do
774 report_names ( formatters [ " %-10s: %T " ] ( k , v ) )
775 end
776 report_names ( " " )
777 report_names ( " pfmwidths " )
778 report_names ( " " )
779 for k , v in sortedhash ( pfmwidths ) do
780 report_names ( formatters [ " %-10s: %T " ] ( k , v ) )
781 end
782 report_names ( " " )
783 report_names ( " registered fonts : %i " , statistics . fonts )
784 report_names ( " read files : %i " , statistics . readfiles )
785 report_names ( " skipped files : %i " , statistics . skippedfiles )
786 report_names ( " duplicate files : %i " , statistics . duplicatefiles )
787 if runtime then
788 report_names ( " total scan time : %0.3f seconds " , runtime )
789 end
790 end
791end
792
793local function collecthashes ( )
794 local data = names . data
795 local mappings = data . mappings
796 local fallbacks = data . fallbacks
797 local specifications = data . specifications
798 local nofmappings = 0
799 local noffallbacks = 0
800 if specifications then
801
802 local conflicts = setmetatableindex ( " table " )
803 for index = 1 , # specifications do
804 local specification = specifications [ index ]
805 local format = specification . format
806 local fullname = specification . fullname
807 local fontname = specification . fontname
808
809
810
811 local familyname = specification . familyname or specification . family
812 local subfamilyname = specification . subfamilyname
813 local subfamily = specification . subfamily
814 local weight = specification . weight
815 local mapping = mappings [ format ]
816 local fallback = fallbacks [ format ]
817 local instancenames = specification . instancenames
818 if fullname and not mapping [ fullname ] then
819 mapping [ fullname ] = index
820 nofmappings = nofmappings + 1
821 end
822 if fontname and not mapping [ fontname ] then
823 mapping [ fontname ] = index
824 nofmappings = nofmappings + 1
825 end
826 if instancenames then
827 for i = 1 , # instancenames do
828 local instance = fullname . . instancenames [ i ]
829 mapping [ instance ] = index
830 nofmappings = nofmappings + 1
831 end
832 end
833
834
835
836
837
838
839
840
841 if familyname then
842 if weight and weight ~ = sub ( familyname , # familyname - # weight + 1 , # familyname ) then
843 local madename = familyname . . weight
844 if not mapping [ madename ] and not fallback [ madename ] then
845 fallback [ madename ] = index
846 noffallbacks = noffallbacks + 1
847 end
848 end
849 if subfamily and subfamily ~ = sub ( familyname , # familyname - # subfamily + 1 , # familyname ) then
850 local extraname = familyname . . subfamily
851 if not mapping [ extraname ] and not fallback [ extraname ] then
852 fallback [ extraname ] = index
853 noffallbacks = noffallbacks + 1
854 end
855 end
856 if subfamilyname and subfamilyname ~ = sub ( familyname , # familyname - # subfamilyname + 1 , # familyname ) then
857 local extraname = familyname . . subfamilyname
858 if not mapping [ extraname ] and not fallback [ extraname ] then
859 fallback [ extraname ] = index
860 noffallbacks = noffallbacks + 1
861 end
862 end
863
864 if not mapping [ familyname ] and not fallback [ familyname ] then
865 fallback [ familyname ] = index
866 noffallbacks = noffallbacks + 1
867 end
868 local conflict = conflicts [ format ]
869 conflict [ familyname ] = ( conflict [ familyname ] or 0 ) + 1
870 end
871 end
872 for format , conflict in next , conflicts do
873 local fallback = fallbacks [ format ]
874 for familyname , n in next , conflict do
875 if n > 1 then
876 fallback [ familyname ] = nil
877 noffallbacks = noffallbacks - n
878 end
879 end
880 end
881 end
882 return nofmappings , noffallbacks
883end
884
885local function collectfamilies ( )
886 local data = names . data
887 local specifications = data . specifications
888 local families = data . families
889 for index = 1 , # specifications do
890 local familyname = specifications [ index ] . familyname
891 local family = families [ familyname ]
892 if not family then
893 families [ familyname ] = { index }
894 else
895 family [ # family + 1 ] = index
896 end
897 end
898end
899
900local function checkduplicate ( where )
901 local data = names . data
902 local mapping = data [ where ]
903 local specifications = data . specifications
904 local loaded = { }
905 if specifications and mapping then
906
907 local order = filters . list
908 for i = 1 , # order do
909 local m = mapping [ order [ i ] ]
910 for k , v in sortedhash ( m ) do
911 local s = specifications [ v ]
912 local hash = formatters [ " %s-%s-%s-%s-%s " ] ( s . familyname , s . weight or " * " , s . style or " * " , s . width or " * " , s . variant or " * " )
913 local h = loaded [ hash ]
914 if h then
915 local ok = true
916 local fn = s . filename
917 for i = 1 , # h do
918 if h [ i ] = = fn then
919 ok = false
920 break
921 end
922 end
923 if ok then
924 h [ # h + 1 ] = fn
925 end
926 else
927 loaded [ hash ] = { s . filename }
928 end
929 end
930 end
931 end
932 local n = 0
933 for k , v in sortedhash ( loaded ) do
934 local nv = # v
935 if nv > 1 then
936 if trace_warnings then
937 report_names ( " lookup %a clashes with %a " , k , v )
938 end
939 n = n + nv
940 end
941 end
942 report_names ( " %a double lookups in %a " , n , where )
943end
944
945local function checkduplicates ( )
946 checkduplicate ( " mappings " )
947 checkduplicate ( " fallbacks " )
948end
949
950local function sorthashes ( )
951 local data = names . data
952 local list = filters . list
953 local mappings = data . mappings
954 local fallbacks = data . fallbacks
955 local sorted_mappings = { }
956 local sorted_fallbacks = { }
957 data . sorted_mappings = sorted_mappings
958 data . sorted_fallbacks = sorted_fallbacks
959 for i = 1 , # list do
960 local l = list [ i ]
961 sorted_mappings [ l ] = table . keys ( mappings [ l ] )
962 sorted_fallbacks [ l ] = table . keys ( fallbacks [ l ] )
963 sort ( sorted_mappings [ l ] , sorter )
964 sort ( sorted_fallbacks [ l ] , sorter )
965 end
966 local sorted_families = table . keys ( data . families )
967 data . sorted_families = sorted_families
968 sort ( sorted_families , sorter )
969end
970
971local function unpackreferences ( )
972 local data = names . data
973 local specifications = data . specifications
974 if specifications then
975 for k , v in sortedhash ( data . families ) do
976 for i = 1 , # v do
977 v [ i ] = specifications [ v [ i ] ]
978 end
979 end
980 local mappings = data . mappings
981 if mappings then
982 for _ , m in sortedhash ( mappings ) do
983 for k , v in sortedhash ( m ) do
984 m [ k ] = specifications [ v ]
985 end
986 end
987 end
988 local fallbacks = data . fallbacks
989 if fallbacks then
990 for _ , f in sortedhash ( fallbacks ) do
991 for k , v in sortedhash ( f ) do
992 f [ k ] = specifications [ v ]
993 end
994 end
995 end
996 end
997end
998
999local function analyzefiles ( olddata )
1000
1001 if not trace_warnings then
1002 report_names ( " warnings are disabled (tracker 'fonts.warnings') " )
1003 end
1004
1005 local data = names . data
1006 local done = { }
1007 local totalnofread = 0
1008 local totalnofskipped = 0
1009 local totalnofduplicates = 0
1010 local nofread = 0
1011 local nofskipped = 0
1012 local nofduplicates = 0
1013 local skip_paths = filters . paths
1014 local skip_names = filters . names
1015 local specifications = data . specifications
1016 local oldindices = olddata and olddata . indices or { }
1017 local oldspecifications = olddata and olddata . specifications or { }
1018 local oldrejected = olddata and olddata . rejected or { }
1019 local treatmentdata = treatments . data or { }
1020
1021
1022 local function walk_tree ( pathlist , suffix , identify )
1023 if pathlist then
1024 for i = 1 , # pathlist do
1025 local path = pathlist [ i ]
1026 path = cleanpath ( path . . " / " )
1027 path = gsub ( path , " /+ " , " / " )
1028 local pattern = path . . " **. " . . suffix
1029 report_names ( " globbing path %a " , pattern )
1030 local t = dir . glob ( pattern )
1031 sort ( t , sorter )
1032 for j = 1 , # t do
1033 local completename = t [ j ]
1034 identify ( completename , basename ( completename ) , suffix , completename )
1035 end
1036
1037 end
1038 end
1039 end
1040
1041 local function identify ( completename , name , suffix , storedname )
1042 local pathpart , basepart = splitbase ( completename )
1043 nofread = nofread + 1
1044 local treatment = treatmentdata [ completename ] or treatmentdata [ basepart ]
1045 if treatment and treatment . ignored then
1046 if trace_names or trace_rejections then
1047 report_names ( " %s font %a is ignored, reason %a " , suffix , completename , treatment . comment or " unknown " )
1048 end
1049 nofskipped = nofskipped + 1
1050 elseif done [ name ] then
1051 if lower ( completename ) ~ = lower ( done [ name ] ) then
1052
1053 if trace_names or trace_rejections then
1054 report_names ( " %s font %a already done as %a " , suffix , completename , done [ name ] )
1055 end
1056 nofduplicates = nofduplicates + 1
1057 nofskipped = nofskipped + 1
1058 end
1059 elseif not exists ( completename ) then
1060
1061 if trace_names or trace_rejections then
1062 report_names ( " %s font %a does not really exist " , suffix , completename )
1063 end
1064 nofskipped = nofskipped + 1
1065 elseif not is_qualified_path ( completename ) and findfile ( completename , suffix ) = = " " then
1066
1067 if trace_names or trace_rejections then
1068 report_names ( " %s font %a cannot be found by backend " , suffix , completename )
1069 end
1070 nofskipped = nofskipped + 1
1071 else
1072 if # skip_paths > 0 then
1073 for i = 1 , # skip_paths do
1074 if find ( pathpart , skip_paths [ i ] ) then
1075 if trace_names or trace_rejections then
1076 report_names ( " rejecting path of %s font %a " , suffix , completename )
1077 end
1078 nofskipped = nofskipped + 1
1079 return
1080 end
1081 end
1082 end
1083 if # skip_names > 0 then
1084 for i = 1 , # skip_paths do
1085 if find ( basepart , skip_names [ i ] ) then
1086 done [ name ] = true
1087 if trace_names or trace_rejections then
1088 report_names ( " rejecting name of %s font %a " , suffix , completename )
1089 end
1090 nofskipped = nofskipped + 1
1091 return
1092 end
1093 end
1094 end
1095 if trace_names then
1096 report_names ( " identifying %s font %a " , suffix , completename )
1097 end
1098
1099 local result = nil
1100 local modification = modificationtime ( completename )
1101 if olddata and modification and modification > 0 then
1102 local oldindex = oldindices [ storedname ]
1103 if oldindex then
1104 local oldspecification = oldspecifications [ oldindex ]
1105 if oldspecification and oldspecification . filename = = storedname then
1106 local oldmodification = oldspecification . modification
1107 if oldmodification = = modification then
1108 result = oldspecification
1109 specifications [ # specifications + 1 ] = result
1110 else
1111
1112 end
1113 else
1114
1115 end
1116 elseif oldrejected [ storedname ] = = modification then
1117 result = false
1118 end
1119 end
1120 if result = = nil then
1121 local lsuffix = lower ( suffix )
1122 local result , message = filters [ lsuffix ] ( completename )
1123 if result then
1124 if # result > 0 then
1125 for r = 1 , # result do
1126 check_name ( data , result [ r ] , storedname , modification , suffix , r )
1127 end
1128 else
1129 check_name ( data , result , storedname , modification , suffix )
1130 end
1131 if trace_warnings and message and message ~ = " " then
1132 report_names ( " warning when identifying %s font %a, %s " , suffix , completename , message )
1133 end
1134 elseif trace_warnings then
1135 nofskipped = nofskipped + 1
1136 report_names ( " error when identifying %s font %a, %s " , suffix , completename , message or " unknown " )
1137 end
1138 end
1139 done [ name ] = completename
1140 end
1141 logs . flush ( )
1142 end
1143
1144 local function traverse ( what , method )
1145 local list = filters . list
1146 for n = 1 , # list do
1147 local suffix = list [ n ]
1148 local t = os . gettimeofday ( )
1149 nofread , nofskipped , nofduplicates = 0 , 0 , 0
1150 suffix = lower ( suffix )
1151 report_names ( " identifying %s font files with suffix %a " , what , suffix )
1152 method ( suffix )
1153 suffix = upper ( suffix )
1154 report_names ( " identifying %s font files with suffix %a " , what , suffix )
1155 method ( suffix )
1156 totalnofread , totalnofskipped , totalnofduplicates = totalnofread + nofread , totalnofskipped + nofskipped , totalnofduplicates + nofduplicates
1157 local elapsed = os . gettimeofday ( ) - t
1158 report_names ( " %s %s files identified, %s skipped, %s duplicates, %s hash entries added, runtime %0.3f seconds " , nofread , what , nofskipped , nofduplicates , nofread - nofskipped , elapsed )
1159 end
1160 logs . flush ( )
1161 end
1162
1163
1164
1165 local function withtree ( suffix )
1166 resolvers . dowithfilesintree ( " .*%. " . . suffix . . " $ " , function ( method , root , path , name )
1167 if method = = " file " or method = = " tree " then
1168 local completename = root . . " / " . . path . . " / " . . name
1169 completename = resolveprefix ( completename )
1170 identify ( completename , name , suffix , name )
1171 return true
1172 end
1173 end , function ( blobtype , blobpath , pattern )
1174 blobpath = resolveprefix ( blobpath )
1175 report_names ( " scanning path %a for %s files " , blobpath , suffix )
1176 end , function ( blobtype , blobpath , pattern , total , checked , done )
1177 blobpath = resolveprefix ( blobpath )
1178 report_names ( " %s %s files checked, %s okay " , checked , suffix , done )
1179 end )
1180 end
1181
1182 local function withlsr ( suffix )
1183
1184
1185 local pathlist = resolvers . splitpath ( resolvers . showpath ( " ls-R " ) or " " )
1186 walk_tree ( pathlist , suffix , identify )
1187 end
1188
1189 local function withsystem ( suffix )
1190 walk_tree ( names . getpaths ( trace ) , suffix , identify )
1191 end
1192
1193 traverse ( " tree " , withtree )
1194
1195 if not usesystemfonts then
1196 report_names ( " ignoring system fonts " )
1197 elseif texconfig . kpse_init then
1198 traverse ( " lsr " , withlsr )
1199 else
1200 traverse ( " system " , withsystem )
1201 end
1202
1203 data . statistics . readfiles = totalnofread
1204 data . statistics . skippedfiles = totalnofskipped
1205 data . statistics . duplicatefiles = totalnofduplicates
1206
1207
1208
1209
1210
1211end
1212
1213local function addfilenames ( )
1214 local data = names . data
1215 local specifications = data . specifications
1216 local indices = { }
1217 local files = { }
1218 for i = 1 , # specifications do
1219 local fullname = specifications [ i ] . filename
1220 files [ cleanfilename ( fullname ) ] = fullname
1221 indices [ fullname ] = i
1222 end
1223 data . files = files
1224 data . indices = indices
1225end
1226
1227local function rejectclashes ( )
1228 local specifications = names . data . specifications
1229 local used = { }
1230 local okay = { }
1231 local rejected = { }
1232 local o = 0
1233 for i = 1 , # specifications do
1234 local s = specifications [ i ]
1235 local f = s . fontname
1236 if f then
1237 local fnd = used [ f ]
1238 local fnm = s . filename
1239 if fnd then
1240 if trace_warnings then
1241 report_names ( " fontname %a clashes, %a rejected in favor of %a " , f , fnm , fnd )
1242 end
1243 rejected [ f ] = s . modification
1244 else
1245 used [ f ] = fnm
1246 o = o + 1
1247 okay [ o ] = s
1248 end
1249 else
1250 o = o + 1
1251 okay [ o ] = s
1252 end
1253 end
1254 local d = # specifications - # okay
1255 if d > 0 then
1256 report_names ( " %s files rejected due to clashes " , d )
1257 end
1258 names . data . specifications = okay
1259 names . data . rejected = rejected
1260end
1261
1262local function resetdata ( )
1263 local mappings = { }
1264 local fallbacks = { }
1265 for _ , k in next , filters . list do
1266 mappings [ k ] = { }
1267 fallbacks [ k ] = { }
1268 end
1269 names . data = {
1270 version = names . version ,
1271 mappings = mappings ,
1272 fallbacks = fallbacks ,
1273 specifications = { } ,
1274 families = { } ,
1275 statistics = { } ,
1276 names = { } ,
1277 indices = { } ,
1278 rejected = { } ,
1279 datastate = resolvers . datastate ( ) ,
1280 }
1281end
1282
1283function names . identify ( force )
1284 local starttime = os . gettimeofday ( )
1285 resetdata ( )
1286 analyzefiles ( not force and names . readdata ( names . basename ) )
1287 rejectclashes ( )
1288 collectfamilies ( )
1289 cleanupkeywords ( )
1290 collecthashes ( )
1291 checkduplicates ( )
1292 addfilenames ( )
1293
1294 collectstatistics ( os . gettimeofday ( ) - starttime )
1295end
1296
1297function names . is_permitted ( name )
1298 return containers . is_usable ( names . cache , name )
1299end
1300function names . writedata ( name , data )
1301 containers . write ( names . cache , name , data )
1302end
1303function names . readdata ( name )
1304 return containers . read ( names . cache , name )
1305end
1306
1307function names . load ( reload , force )
1308 if not names . loaded then
1309 if reload then
1310 if names . is_permitted ( names . basename ) then
1311 names . identify ( force )
1312 names . writedata ( names . basename , names . data )
1313 else
1314 report_names ( " unable to access database cache " )
1315 end
1316 names . saved = true
1317 end
1318 local data = names . readdata ( names . basename )
1319 names . data = data
1320 if not names . saved then
1321 if not data or not next ( data ) or not data . specifications or not next ( data . specifications ) then
1322 names . load ( true )
1323 end
1324 names . saved = true
1325 end
1326 if not data then
1327 report_names ( " accessing the data table failed " )
1328 else
1329 unpackreferences ( )
1330 sorthashes ( )
1331 end
1332 names . loaded = true
1333 end
1334end
1335
1336local function list_them ( mapping , sorted , pattern , t , all )
1337 if mapping [ pattern ] then
1338 t [ pattern ] = mapping [ pattern ]
1339 else
1340 for k = 1 , # sorted do
1341 local v = sorted [ k ]
1342 if not t [ v ] and find ( v , pattern ) then
1343 t [ v ] = mapping [ v ]
1344 if not all then
1345 return
1346 end
1347 end
1348 end
1349 end
1350end
1351
1352function names . list ( pattern , reload , all )
1353 names . load ( )
1354 if names . loaded then
1355 local t = { }
1356 local data = names . data
1357 if data then
1358 local list = filters . list
1359 local mappings = data . mappings
1360 local sorted_mappings = data . sorted_mappings
1361 local fallbacks = data . fallbacks
1362 local sorted_fallbacks = data . sorted_fallbacks
1363 for i = 1 , # list do
1364 local format = list [ i ]
1365 list_them ( mappings [ format ] , sorted_mappings [ format ] , pattern , t , all )
1366 if next ( t ) and not all then
1367 return t
1368 end
1369 list_them ( fallbacks [ format ] , sorted_fallbacks [ format ] , pattern , t , all )
1370 if next ( t ) and not all then
1371 return t
1372 end
1373 end
1374 end
1375 return t
1376 end
1377end
1378
1379local reloaded = false
1380
1381local function is_reloaded ( )
1382 if not reloaded then
1383 local data = names . data
1384 if autoreload then
1385 local c_status = serialize ( resolvers . datastate ( ) )
1386 local f_status = serialize ( data . datastate )
1387 if c_status = = f_status then
1388 if trace_names then
1389 report_names ( " font database has matching configuration and file hashes " )
1390 end
1391 return
1392 else
1393 report_names ( " font database has mismatching configuration and file hashes " )
1394 end
1395 else
1396 report_names ( " font database is regenerated (controlled by directive 'fonts.autoreload') " )
1397 end
1398 names . loaded = false
1399 reloaded = true
1400 logs . flush ( )
1401 names . load ( true )
1402 end
1403end
1404
1405
1410
1411local function fuzzy ( mapping , sorted , name , sub )
1412 local condensed = gsub ( name , " [^%a%d] " , " " )
1413 for k = 1 , # sorted do
1414 local v = sorted [ k ]
1415 if find ( v , condensed ) then
1416 return mapping [ v ] , v
1417 end
1418 end
1419end
1420
1421
1422
1423local function checkinstance ( found , askedname )
1424 local instancenames = found . instancenames
1425 if instancenames then
1426 local fullname = found . fullname
1427 for i = 1 , # instancenames do
1428 local instancename = instancenames [ i ]
1429 if fullname . . instancename = = askedname then
1430 local f = fastcopy ( found )
1431 f . instances = nil
1432 f . instance = instancename
1433 return f
1434 end
1435 end
1436 end
1437 return found
1438end
1439
1440local function foundname ( name , sub )
1441 local data = names . data
1442 local mappings = data . mappings
1443 local sorted_mappings = data . sorted_mappings
1444 local fallbacks = data . fallbacks
1445 local sorted_fallbacks = data . sorted_fallbacks
1446 local list = filters . list
1447
1448
1449
1450 for i = 1 , # list do
1451 local l = list [ i ]
1452 local found = mappings [ l ] [ name ]
1453 if found then
1454 if trace_names then
1455 report_names ( " resolved via direct name match: %a " , name )
1456 end
1457 return checkinstance ( found , name )
1458 end
1459 end
1460 for i = 1 , # list do
1461 local l = list [ i ]
1462 local found , fname = fuzzy ( mappings [ l ] , sorted_mappings [ l ] , name , sub )
1463 if found then
1464 if trace_names then
1465 report_names ( " resolved via fuzzy name match: %a onto %a " , name , fname )
1466 end
1467 return checkinstance ( found , name )
1468 end
1469 end
1470 for i = 1 , # list do
1471 local l = list [ i ]
1472 local found = fallbacks [ l ] [ name ]
1473 if found then
1474 if trace_names then
1475 report_names ( " resolved via direct fallback match: %a " , name )
1476 end
1477 return checkinstance ( found , name )
1478 end
1479 end
1480 for i = 1 , # list do
1481 local l = list [ i ]
1482 local found , fname = fuzzy ( sorted_mappings [ l ] , sorted_fallbacks [ l ] , name , sub )
1483 if found then
1484 if trace_names then
1485 report_names ( " resolved via fuzzy fallback match: %a onto %a " , name , fname )
1486 end
1487 return checkinstance ( found , name )
1488 end
1489 end
1490 if trace_names then
1491 report_names ( " font with name %a cannot be found " , name )
1492 end
1493end
1494
1495function names . resolvedspecification ( askedname , sub )
1496 if askedname and askedname ~ = " " and names . enabled then
1497 askedname = cleanname ( askedname )
1498 names . load ( )
1499 local found = foundname ( askedname , sub )
1500 if not found and is_reloaded ( ) then
1501 found = foundname ( askedname , sub )
1502 end
1503 return found
1504 end
1505end
1506
1507function names . resolve ( askedname , sub )
1508 local found = names . resolvedspecification ( askedname , sub )
1509 if found then
1510 return found . filename , found . subfont and found . rawname , found . subfont , found . instance
1511 end
1512end
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525local runtimefiles = { }
1526local runtimedone = false
1527
1528local function addruntimepath ( path )
1529 names . load ( )
1530 local paths = type ( path ) = = " table " and path or { path }
1531 local suffixes = tohash ( filters . list )
1532 for i = 1 , # paths do
1533 local path = resolveprefix ( paths [ i ] )
1534 if path ~ = " " then
1535 local list = dir . glob ( path . . " /* " )
1536 for i = 1 , # list do
1537 local fullname = list [ i ]
1538 local suffix = lower ( suffixonly ( fullname ) )
1539 if suffixes [ suffix ] then
1540 local c = cleanfilename ( fullname )
1541 runtimefiles [ c ] = fullname
1542 if trace_names then
1543 report_names ( " adding runtime filename %a for %a " , c , fullname )
1544 end
1545 end
1546 end
1547 end
1548 end
1549end
1550
1551local function addruntimefiles ( variable )
1552 local paths = variable and resolvers . expandedpathlistfromvariable ( variable )
1553 if paths and # paths > 0 then
1554 addruntimepath ( paths )
1555 end
1556end
1557
1558names . addruntimepath = addruntimepath
1559names . addruntimefiles = addruntimefiles
1560
1561function names . getfilename ( askedname , suffix )
1562 if not runtimedone then
1563 addruntimefiles ( names . runtimefontsvariable )
1564 runtimedone = true
1565 end
1566 local cleanname = cleanfilename ( askedname , suffix )
1567 local found = runtimefiles [ cleanname ]
1568 if found then
1569 return found
1570 end
1571 names . load ( )
1572 local files = names . data . files
1573 local found = files and files [ cleanname ] or " "
1574 if found = = " " and is_reloaded ( ) then
1575 files = names . data . files
1576 found = files and files [ cleanname ] or " "
1577 end
1578 if found and found ~ = " " then
1579 return resolvers . findbinfile ( found , suffix ) or " "
1580 end
1581end
1582
1583
1584
1585local function s_collect_weight_style_width_variant ( found , done , all , weight , style , width , variant , family )
1586 if family then
1587 for i = 1 , # family do
1588 local f = family [ i ]
1589 if f and weight = = f . weight and style = = f . style and width = = f . width and variant = = f . variant then
1590 found [ # found + 1 ] , done [ f ] = f , true
1591 if not all then return end
1592 end
1593 end
1594 end
1595end
1596local function m_collect_weight_style_width_variant ( found , done , all , weight , style , width , variant , families , sorted , strictname )
1597 for i = 1 , # sorted do
1598 local k = sorted [ i ]
1599 local family = families [ k ]
1600 for i = 1 , # family do
1601 local f = family [ i ]
1602 if not done [ f ] and weight = = f . weight and style = = f . style and width = = f . width and variant = = f . variant and find ( f . fontname , strictname ) then
1603 found [ # found + 1 ] , done [ f ] = f , true
1604 if not all then return end
1605 end
1606 end
1607 end
1608end
1609
1610local function s_collect_weight_style_width ( found , done , all , weight , style , width , family )
1611 if family then
1612 for i = 1 , # family do
1613 local f = family [ i ]
1614 if f and weight = = f . weight and style = = f . style and width = = f . width then
1615 found [ # found + 1 ] , done [ f ] = f , true
1616 if not all then return end
1617 end
1618 end
1619 end
1620end
1621local function m_collect_weight_style_width ( found , done , all , weight , style , width , families , sorted , strictname )
1622 for i = 1 , # sorted do
1623 local k = sorted [ i ]
1624 local family = families [ k ]
1625 for i = 1 , # family do
1626 local f = family [ i ]
1627 if not done [ f ] and weight = = f . weight and style = = f . style and width = = f . width and find ( f . fontname , strictname ) then
1628 found [ # found + 1 ] , done [ f ] = f , true
1629 if not all then return end
1630 end
1631 end
1632 end
1633end
1634
1635local function s_collect_weight_style ( found , done , all , weight , style , family )
1636 if family then
1637 for i = 1 , # family do local f = family [ i ]
1638 if f and weight = = f . weight and style = = f . style then
1639 found [ # found + 1 ] , done [ f ] = f , true
1640 if not all then return end
1641 end
1642 end
1643 end
1644end
1645local function m_collect_weight_style ( found , done , all , weight , style , families , sorted , strictname )
1646 for i = 1 , # sorted do
1647 local k = sorted [ i ]
1648 local family = families [ k ]
1649 for i = 1 , # family do
1650 local f = family [ i ]
1651 if not done [ f ] and weight = = f . weight and style = = f . style and find ( f . fontname , strictname ) then
1652 found [ # found + 1 ] , done [ f ] = f , true
1653 if not all then return end
1654 end
1655 end
1656 end
1657end
1658
1659local function s_collect_style_width ( found , done , all , style , width , family )
1660 if family then
1661 for i = 1 , # family do local f = family [ i ]
1662 if f and style = = f . style and width = = f . width then
1663 found [ # found + 1 ] , done [ f ] = f , true
1664 if not all then return end
1665 end
1666 end
1667 end
1668end
1669local function m_collect_style_width ( found , done , all , style , width , families , sorted , strictname )
1670 for i = 1 , # sorted do
1671 local k = sorted [ i ]
1672 local family = families [ k ]
1673 for i = 1 , # family do
1674 local f = family [ i ]
1675 if not done [ f ] and style = = f . style and width = = f . width and find ( f . fontname , strictname ) then
1676 found [ # found + 1 ] , done [ f ] = f , true
1677 if not all then return end
1678 end
1679 end
1680 end
1681end
1682
1683local function s_collect_weight ( found , done , all , weight , family )
1684 if family then
1685 for i = 1 , # family do local f = family [ i ]
1686 if f and weight = = f . weight then
1687 found [ # found + 1 ] , done [ f ] = f , true
1688 if not all then return end
1689 end
1690 end
1691 end
1692end
1693local function m_collect_weight ( found , done , all , weight , families , sorted , strictname )
1694 for i = 1 , # sorted do
1695 local k = sorted [ i ]
1696 local family = families [ k ]
1697 for i = 1 , # family do
1698 local f = family [ i ]
1699 if not done [ f ] and weight = = f . weight and find ( f . fontname , strictname ) then
1700 found [ # found + 1 ] , done [ f ] = f , true
1701 if not all then return end
1702 end
1703 end
1704 end
1705end
1706
1707local function s_collect_style ( found , done , all , style , family )
1708 if family then
1709 for i = 1 , # family do local f = family [ i ]
1710 if f and style = = f . style then
1711 found [ # found + 1 ] , done [ f ] = f , true
1712 if not all then return end
1713 end
1714 end
1715 end
1716end
1717local function m_collect_style ( found , done , all , style , families , sorted , strictname )
1718 for i = 1 , # sorted do
1719 local k = sorted [ i ]
1720 local family = families [ k ]
1721 for i = 1 , # family do
1722 local f = family [ i ]
1723 if not done [ f ] and style = = f . style and find ( f . fontname , strictname ) then
1724 found [ # found + 1 ] , done [ f ] = f , true
1725 if not all then return end
1726 end
1727 end
1728 end
1729end
1730
1731local function s_collect_width ( found , done , all , width , family )
1732 if family then
1733 for i = 1 , # family do local f = family [ i ]
1734 if f and width = = f . width then
1735 found [ # found + 1 ] , done [ f ] = f , true
1736 if not all then return end
1737 end
1738 end
1739 end
1740end
1741local function m_collect_width ( found , done , all , width , families , sorted , strictname )
1742 for i = 1 , # sorted do
1743 local k = sorted [ i ]
1744 local family = families [ k ]
1745 for i = 1 , # family do
1746 local f = family [ i ]
1747 if not done [ f ] and width = = f . width and find ( f . fontname , strictname ) then
1748 found [ # found + 1 ] , done [ f ] = f , true
1749 if not all then return end
1750 end
1751 end
1752 end
1753end
1754
1755local function s_collect ( found , done , all , family )
1756 if family then
1757 for i = 1 , # family do local f = family [ i ]
1758 if f then
1759 found [ # found + 1 ] , done [ f ] = f , true
1760 if not all then return end
1761 end
1762 end
1763 end
1764end
1765local function m_collect ( found , done , all , families , sorted , strictname )
1766 for i = 1 , # sorted do
1767 local k = sorted [ i ]
1768 local family = families [ k ]
1769 for i = 1 , # family do
1770 local f = family [ i ]
1771 if not done [ f ] and find ( f . fontname , strictname ) then
1772 found [ # found + 1 ] , done [ f ] = f , true
1773 if not all then return end
1774 end
1775 end
1776 end
1777end
1778
1779local function collect ( stage , found , done , name , weight , style , width , variant , all )
1780 local data = names . data
1781 local families = data . families
1782 local sorted = data . sorted_families
1783 local strictname = " ^ " . . name
1784 local family = families [ name ]
1785 if trace_names then
1786 report_names ( " resolving name %a, weight %a, style %a, width %a, variant %a " , name , weight , style , width , variant )
1787 end
1788 if weight and weight ~ = " " then
1789 if style and style ~ = " " then
1790 if width and width ~ = " " then
1791 if variant and variant ~ = " " then
1792 if trace_names then
1793 report_names ( " resolving stage %s, name %a, weight %a, style %a, width %a, variant %a " , stage , name , weight , style , width , variant )
1794 end
1795 s_collect_weight_style_width_variant ( found , done , all , weight , style , width , variant , family )
1796 m_collect_weight_style_width_variant ( found , done , all , weight , style , width , variant , families , sorted , strictname )
1797 else
1798 if trace_names then
1799 report_names ( " resolving stage %s, name %a, weight %a, style %a, width %a " , stage , name , weight , style , width )
1800 end
1801 s_collect_weight_style_width ( found , done , all , weight , style , width , family )
1802 m_collect_weight_style_width ( found , done , all , weight , style , width , families , sorted , strictname )
1803 end
1804 else
1805 if trace_names then
1806 report_names ( " resolving stage %s, name %a, weight %a, style %a " , stage , name , weight , style )
1807 end
1808 s_collect_weight_style ( found , done , all , weight , style , family )
1809 m_collect_weight_style ( found , done , all , weight , style , families , sorted , strictname )
1810 end
1811 else
1812 if trace_names then
1813 report_names ( " resolving stage %s, name %a, weight %a " , stage , name , weight )
1814 end
1815 s_collect_weight ( found , done , all , weight , family )
1816 m_collect_weight ( found , done , all , weight , families , sorted , strictname )
1817 end
1818 elseif style and style ~ = " " then
1819 if width and width ~ = " " then
1820 if trace_names then
1821 report_names ( " resolving stage %s, name %a, style %a, width %a " , stage , name , style , width )
1822 end
1823 s_collect_style_width ( found , done , all , style , width , family )
1824 m_collect_style_width ( found , done , all , style , width , families , sorted , strictname )
1825 else
1826 if trace_names then
1827 report_names ( " resolving stage %s, name %a, style %a " , stage , name , style )
1828 end
1829 s_collect_style ( found , done , all , style , family )
1830 m_collect_style ( found , done , all , style , families , sorted , strictname )
1831 end
1832 elseif width and width ~ = " " then
1833 if trace_names then
1834 report_names ( " resolving stage %s, name %a, width %a " , stage , name , width )
1835 end
1836 s_collect_width ( found , done , all , width , family )
1837 m_collect_width ( found , done , all , width , families , sorted , strictname )
1838 else
1839 if trace_names then
1840 report_names ( " resolving stage %s, name %a " , stage , name )
1841 end
1842 s_collect ( found , done , all , family )
1843 m_collect ( found , done , all , families , sorted , strictname )
1844 end
1845end
1846
1847local function heuristic ( name , weight , style , width , variant , all )
1848 local found , done = { } , { }
1849
1850 weight , style , width , variant = weight or " normal " , style or " normal " , width or " normal " , variant or " normal "
1851 name = cleanname ( name )
1852 collect ( 1 , found , done , name , weight , style , width , variant , all )
1853
1854 if # found = = 0 and variant ~ = " normal " then
1855 variant = " normal "
1856 collect ( 4 , found , done , name , weight , style , width , variant , all )
1857 end
1858 if # found = = 0 and width ~ = " normal " then
1859 width = " normal "
1860 collect ( 2 , found , done , name , weight , style , width , variant , all )
1861 end
1862 if # found = = 0 and weight ~ = " normal " then
1863 weight = " normal "
1864 collect ( 3 , found , done , name , weight , style , width , variant , all )
1865 end
1866 if # found = = 0 and style ~ = " normal " then
1867 style = " normal "
1868 collect ( 4 , found , done , name , weight , style , width , variant , all )
1869 end
1870
1871 local nf = # found
1872 if trace_names then
1873 if nf then
1874 local t = { }
1875 for i = 1 , nf do
1876 t [ i ] = formatters [ " %a " ] ( found [ i ] . fontname )
1877 end
1878 report_names ( " name %a resolved to %s instances: % t " , name , nf , t )
1879 else
1880 report_names ( " name %a unresolved " , name )
1881 end
1882 end
1883 if all then
1884 return nf > 0 and found
1885 else
1886 return found [ 1 ]
1887 end
1888end
1889
1890function names . specification ( askedname , weight , style , width , variant , reload , all )
1891 if askedname and askedname ~ = " " and names . enabled then
1892 askedname = cleanname ( askedname )
1893 names . load ( reload )
1894 local found = heuristic ( askedname , weight , style , width , variant , all )
1895 if not found and is_reloaded ( ) then
1896 found = heuristic ( askedname , weight , style , width , variant , all )
1897 if not filename then
1898 found = foundname ( askedname )
1899 end
1900 end
1901 return found
1902 end
1903end
1904
1905function names . collect ( askedname , weight , style , width , variant , reload , all )
1906 if askedname and askedname ~ = " " and names . enabled then
1907 askedname = cleanname ( askedname )
1908 names . load ( reload )
1909 local list = heuristic ( askedname , weight , style , width , variant , true )
1910 if not list or # list = = 0 and is_reloaded ( ) then
1911 list = heuristic ( askedname , weight , style , width , variant , true )
1912 end
1913 return list
1914 end
1915end
1916
1917function names . collectspec ( askedname , reload , all )
1918 local name , weight , style , width , variant = names . splitspec ( askedname )
1919 return names . collect ( name , weight , style , width , variant , reload , all )
1920end
1921
1922function names . resolvespec ( askedname , sub )
1923 local found = names . specification ( names . splitspec ( askedname ) )
1924 if found then
1925 return found . filename , found . subfont and found . rawname
1926 end
1927end
1928
1929function names . collectfiles ( askedname , reload )
1930 if askedname and askedname ~ = " " and names . enabled then
1931 askedname = cleanname ( askedname )
1932 names . load ( reload )
1933 local list = { }
1934 local specifications = names . data . specifications
1935 for i = 1 , # specifications do
1936 local s = specifications [ i ]
1937 if find ( cleanname ( basename ( s . filename ) ) , askedname ) then
1938 list [ # list + 1 ] = s
1939 end
1940 end
1941 return list
1942 end
1943end
1944
1945
1946
1947
1948
1949
1950
1951function names . exists ( name )
1952 local found = false
1953 local list = filters . list
1954 for k = 1 , # list do
1955 local v = list [ k ]
1956 found = ( findfile ( name , v ) or " " ) ~ = " "
1957 if found then
1958 return found
1959 end
1960 end
1961 return ( findfile ( name , " tfm " ) or " " ) ~ = " " or ( names . resolve ( name ) or " " ) ~ = " "
1962end
1963
1964local lastlookups , lastpattern = { } , " "
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013local function look_them_up ( lookups , specification )
2014 for key , value in sortedhash ( specification ) do
2015 local t = { }
2016 local n = 0
2017 if find ( value , " * " , 1 , true ) then
2018 value = topattern ( value )
2019 for i = 1 , # lookups do
2020 local s = lookups [ i ]
2021 if find ( s [ key ] , value ) then
2022 n = n + 1
2023 t [ n ] = lookups [ i ]
2024 end
2025 end
2026 else
2027 for i = 1 , # lookups do
2028 local s = lookups [ i ]
2029 if s [ key ] = = value then
2030 n = n + 1
2031 t [ n ] = lookups [ i ]
2032 end
2033 end
2034 end
2035 if trace_names then
2036 report_names ( " %s matches for key %a with value %a " , # t , key , value )
2037 end
2038 lookups = t
2039 end
2040 return lookups
2041end
2042
2043local function first_look ( name , reload )
2044 names . load ( reload )
2045 local data = names . data
2046 local specifications = data . specifications
2047 local families = data . families
2048 if name then
2049 return families [ name ]
2050 else
2051 return specifications
2052 end
2053end
2054
2055function names . lookup ( pattern , name , reload )
2056 names . load ( reload )
2057 local data = names . data
2058 local specifications = data . specifications
2059 local families = data . families
2060 local lookups = specifications
2061 if name then
2062 name = cleanname ( name )
2063 end
2064 if type ( pattern ) = = " table " then
2065 local familyname = pattern . familyname
2066 if familyname then
2067 familyname = cleanname ( familyname )
2068 pattern . familyname = familyname
2069 end
2070 local lookups = first_look ( name or familyname , reload )
2071 if lookups then
2072 if trace_names then
2073 report_names ( " starting with %s lookups for '%T' " , # lookups , pattern )
2074 end
2075 lookups = look_them_up ( lookups , pattern )
2076 end
2077 lastpattern = false
2078 lastlookups = lookups or { }
2079 elseif lastpattern ~ = pattern then
2080 local lookups = first_look ( name or ( not find ( pattern , " = " , 1 , true ) and pattern ) , reload )
2081 if lookups then
2082 if trace_names then
2083 report_names ( " starting with %s lookups for %a " , # lookups , pattern )
2084 end
2085 local specification = settings_to_hash ( pattern )
2086 local familyname = specification . familyname
2087 if familyname then
2088 familyname = cleanname ( familyname )
2089 specification . familyname = familyname
2090 end
2091 lookups = look_them_up ( lookups , specification )
2092 end
2093 lastpattern = pattern
2094 lastlookups = lookups or { }
2095 end
2096 return # lastlookups
2097end
2098
2099function names . getlookupkey ( key , n )
2100 local l = lastlookups [ n or 1 ]
2101 return ( l and l [ key ] ) or " "
2102end
2103
2104function names . noflookups ( )
2105 return # lastlookups
2106end
2107
2108function names . getlookups ( pattern , name , reload )
2109 if pattern then
2110 names . lookup ( pattern , name , reload )
2111 end
2112 return lastlookups
2113end
2114
2115
2116
2117local specifications = allocate ( )
2118names . specifications = specifications
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132function names . register ( files )
2133 if files then
2134 local list , commonname = files . list , files . name
2135 if list then
2136 local n , m = 0 , 0
2137 for filename , filespec in sortedhash ( list ) do
2138 local name = lower ( filespec . name or commonname )
2139 if name and name ~ = " " then
2140 local style = normalized_styles [ lower ( filespec . style or " normal " ) ]
2141 local width = normalized_widths [ lower ( filespec . width or " normal " ) ]
2142 local weight = normalized_weights [ lower ( filespec . weight or " normal " ) ]
2143 local variant = normalized_variants [ lower ( filespec . variant or " normal " ) ]
2144 local weights = specifications [ name ] if not weights then weights = { } specifications [ name ] = weights end
2145 local styles = weights [ weight ] if not styles then styles = { } weights [ weight ] = styles end
2146 local widths = styles [ style ] if not widths then widths = { } styles [ style ] = widths end
2147 local variants = widths [ width ] if not variants then variants = { } widths [ width ] = variants end
2148 variants [ variant ] = filename
2149 n = n + 1
2150 else
2151 m = m + 1
2152 end
2153 end
2154 if trace_specifications then
2155 report_names ( " %s filenames registered, %s filenames rejected " , n , m )
2156 end
2157 end
2158 end
2159end
2160
2161function names . registered ( name , weight , style , width , variant )
2162 local ok = specifications [ name ]
2163 ok = ok and ( ok [ ( weight and weight ~ = " " and weight ) or " normal " ] or ok . normal )
2164 ok = ok and ( ok [ ( style and style ~ = " " and style ) or " normal " ] or ok . normal )
2165 ok = ok and ( ok [ ( width and width ~ = " " and width ) or " normal " ] or ok . normal )
2166 ok = ok and ( ok [ ( variant and variant ~ = " " and variant ) or " normal " ] or ok . normal )
2167
2168
2169
2170 if ok then
2171 return {
2172 filename = ok ,
2173 subname = " " ,
2174
2175 }
2176 end
2177end
2178
2179function names . resolvespec ( askedname , sub )
2180 local name , weight , style , width , variant = names . splitspec ( askedname )
2181 if trace_specifications then
2182 report_names ( " resolving specification: %a to name=%s, weight=%s, style=%s, width=%s, variant=%s " , askedname , name , weight , style , width , variant )
2183 end
2184 local found = names . registered ( name , weight , style , width , variant )
2185 if found and found . filename then
2186 if trace_specifications then
2187 report_names ( " resolved by registered names: %a to %s " , askedname , found . filename )
2188 end
2189 return found . filename , found . subname , found . rawname
2190 else
2191 found = names . specification ( name , weight , style , width , variant )
2192 if found and found . filename then
2193 if trace_specifications then
2194 report_names ( " resolved by font database: %a to %s " , askedname , found . filename )
2195 end
2196 return found . filename , found . subfont and found . rawname
2197 end
2198 end
2199 if trace_specifications then
2200 report_names ( " unresolved: %s " , askedname )
2201 end
2202end
2203
2204function fonts . names . ignoredfile ( filename )
2205 return false
2206end
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221 |