1if not modules then modules = { } end modules [ ' font-ctx ' ] = {
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
15local tostring , next , type , rawget , tonumber = tostring , next , type , rawget , tonumber
16
17local format , gmatch , match , find , lower , upper , gsub , byte , topattern = string . format , string . gmatch , string . match , string . find , string . lower , string . upper , string . gsub , string . byte , string . topattern
18local concat , serialize , sort , fastcopy , mergedtable = table . concat , table . serialize , table . sort , table . fastcopy , table . merged
19local sortedhash , sortedkeys , sequenced = table . sortedhash , table . sortedkeys , table . sequenced
20local parsers = utilities . parsers
21local settings_to_hash , hash_to_string , settings_to_array = parsers . settings_to_hash , parsers . hash_to_string , parsers . settings_to_array
22local formatcolumns = utilities . formatters . formatcolumns
23local mergehashes = utilities . parsers . mergehashes
24local formatters = string . formatters
25local basename = file . basename
26
27local utfchar , utfbyte = utf . char , utf . byte
28local round = math . round
29
30local context , commands = context , commands
31
32local P , S , C , Cc , Cf , Cg , Ct , lpegmatch = lpeg . P , lpeg . S , lpeg . C , lpeg . Cc , lpeg . Cf , lpeg . Cg , lpeg . Ct , lpeg . match
33
34local trace_features = false trackers . register ( " fonts.features " , function ( v ) trace_features = v end )
35local trace_defining = false trackers . register ( " fonts.defining " , function ( v ) trace_defining = v end )
36local trace_designsize = false trackers . register ( " fonts.designsize " , function ( v ) trace_designsize = v end )
37local trace_usage = false trackers . register ( " fonts.usage " , function ( v ) trace_usage = v end )
38local trace_mapfiles = false trackers . register ( " fonts.mapfiles " , function ( v ) trace_mapfiles = v end )
39local trace_automode = false trackers . register ( " fonts.automode " , function ( v ) trace_automode = v end )
40local trace_merge = false trackers . register ( " fonts.merge " , function ( v ) trace_merge = v end )
41
42local report = logs . reporter ( " fonts " )
43local report_features = logs . reporter ( " fonts " , " features " )
44local report_cummulative = logs . reporter ( " fonts " , " cummulative " )
45local report_defining = logs . reporter ( " fonts " , " defining " )
46local report_status = logs . reporter ( " fonts " , " status " )
47local report_mapfiles = logs . reporter ( " fonts " , " mapfiles " )
48
49local setmetatableindex = table . setmetatableindex
50
51local implement = interfaces . implement
52
53local chardata = characters . data
54
55local fonts = fonts
56local handlers = fonts . handlers
57local otf = handlers . otf
58local afm = handlers . afm
59local tfm = handlers . tfm
60local names = fonts . names
61local definers = fonts . definers
62local specifiers = fonts . specifiers
63local constructors = fonts . constructors
64local loggers = fonts . loggers
65local fontgoodies = fonts . goodies
66local helpers = fonts . helpers
67local hashes = fonts . hashes
68local currentfont = font . current
69local definefont = font . define
70
71local getprivateslot = helpers . getprivateslot
72
73local cleanname = names . cleanname
74
75local encodings = fonts . encodings
76
77local aglunicodes = nil
78
79local nuts = nodes . nuts
80local tonut = nuts . tonut
81
82local nextchar = nuts . traversers . char
83
84local getattr = nuts . getattr
85local setattr = nuts . setattr
86local getstate = nuts . getstate
87local setsubtype = nuts . setsubtype
88
89local texgetdimen = tex . getdimen
90local texsetcount = tex . setcount
91local texget = tex . get
92
93local texdefinefont = tex . definefont
94local texsp = tex . sp
95
96local fontdata = hashes . identifiers
97local characters = hashes . characters
98local descriptions = hashes . descriptions
99local properties = hashes . properties
100local resources = hashes . resources
101local unicodes = hashes . unicodes
102local csnames = hashes . csnames
103local lastmathids = hashes . lastmathids
104local exheights = hashes . exheights
105local emwidths = hashes . emwidths
106local parameters = hashes . parameters
107
108local designsizefilename = fontgoodies . designsizes . filename
109
110local ctx_char = context . char
111local ctx_safechar = context . safechar
112local ctx_getvalue = context . getvalue
113
114local otffeatures = otf . features
115local otftables = otf . tables
116
117local registerotffeature = otffeatures . register
118
119local sequencers = utilities . sequencers
120local appendgroup = sequencers . appendgroup
121local appendaction = sequencers . appendaction
122
123specifiers . contextsetups = specifiers . contextsetups or { }
124specifiers . contextnumbers = specifiers . contextnumbers or { }
125specifiers . contextmerged = specifiers . contextmerged or { }
126specifiers . synonyms = specifiers . synonyms or { }
127
128local setups = specifiers . contextsetups
129local numbers = specifiers . contextnumbers
130local merged = specifiers . contextmerged
131local synonyms = specifiers . synonyms
132
133storage . register ( " fonts/setups " , setups , " fonts.specifiers.contextsetups " )
134storage . register ( " fonts/numbers " , numbers , " fonts.specifiers.contextnumbers " )
135storage . register ( " fonts/merged " , merged , " fonts.specifiers.contextmerged " )
136storage . register ( " fonts/synonyms " , synonyms , " fonts.specifiers.synonyms " )
137
138
139
140if environment . initex then
141 setmetatableindex ( setups , function ( t , k )
142 return type ( k ) = = " number " and rawget ( t , numbers [ k ] ) or nil
143 end )
144else
145 setmetatableindex ( setups , function ( t , k )
146 local v = type ( k ) = = " number " and rawget ( t , numbers [ k ] )
147 if v then
148 t [ k ] = v
149 return v
150 end
151 end )
152end
153
154
155
156local function getfontname ( tfmdata )
157 return basename ( type ( tfmdata ) = = " number " and properties [ tfmdata ] . name or tfmdata . properties . name )
158end
159
160helpers . name = getfontname
161
162local addformatter = utilities . strings . formatters . add
163
164addformatter ( formatters , " font:name " , [[ "'"..fontname(%s).."'" ]] , { fontname = helpers . name } )
165addformatter ( formatters , " font:features " , [[ "'"..sequenced(%s," ",true).."'" ]] , { sequenced = table . sequenced } )
166
167
168
169constructors . resolvevirtualtoo = true
170
171if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then
172 constructors . fixprotrusion = false
173end
174
175constructors . sharefonts = true
176constructors . nofsharedfonts = 0
177constructors . nofsharedhashes = 0
178constructors . nofsharedvectors = 0
179constructors . noffontsloaded = 0
180constructors . autocleanup = true
181
182
183
184
185
186
187
188
189
190
191
192function constructors . cleanuptable ( tfmdata )
193 if constructors . autocleanup and tfmdata . properties . virtualized then
194 for k , v in next , tfmdata . characters do
195 if v . commands then v . commands = nil end
196
197 end
198 end
199end
200
201do
202
203 local shares = { }
204 local hashes = { }
205
206
207 local nofinstances = 0
208 local instances = setmetatableindex ( function ( t , k )
209 nofinstances = nofinstances + 1
210 t [ k ] = nofinstances
211 return nofinstances
212 end )
213
214 function constructors . trytosharefont ( target , tfmdata )
215 constructors . noffontsloaded = constructors . noffontsloaded + 1
216 if constructors . sharefonts then
217 local fonthash = target . specification . hash
218 if fonthash then
219 local properties = target . properties
220 local fullname = target . fullname
221 local fontname = target . fontname
222 local psname = target . psname
223
224 local instance = properties . instance
225 if instance then
226 local format = tfmdata . properties . format
227 if format = = " opentype " then
228 target . streamprovider = 1
229 elseif format = = " truetype " then
230 target . streamprovider = 2
231 else
232 target . streamprovider = 0
233 end
234 if target . streamprovider > 0 then
235 if fullname then
236 fullname = fullname . . " : " . . instances [ instance ]
237 target . fullname = fullname
238 end
239 if fontname then
240 fontname = fontname . . " : " . . instances [ instance ]
241 target . fontname = fontname
242 end
243 if psname then
244
245
246
247 psname = psname . . " : " . . instances [ instance ]
248 target . psname = psname
249 end
250 end
251 end
252
253 local sharedname = hashes [ fonthash ]
254 if sharedname then
255
256
257 if trace_defining then
258 report_defining ( " font %a uses backend resources of font %a (%s) " , target . fullname , sharedname , " common hash " )
259 end
260 target . fullname = sharedname
261 properties . sharedwith = sharedname
262 constructors . nofsharedfonts = constructors . nofsharedfonts + 1
263 constructors . nofsharedhashes = constructors . nofsharedhashes + 1
264 else
265
266
267
268
269 local characters = target . characters
270 local n = 1
271 local t = { target . psname }
272
273 if instance then
274 n = n + 1
275 t [ n ] = instance
276 end
277
278 local u = sortedkeys ( characters )
279 for i = 1 , # u do
280 local k = u [ i ]
281 n = n + 1 ; t [ n ] = k
282 n = n + 1 ; t [ n ] = characters [ k ] . index or k
283 end
284 local checksum = md5 . HEX ( concat ( t , " " ) )
285 local sharedname = shares [ checksum ]
286 local fullname = target . fullname
287 if sharedname then
288 if trace_defining then
289 report_defining ( " font %a uses backend resources of font %a (%s) " , fullname , sharedname , " common vector " )
290 end
291 fullname = sharedname
292 properties . sharedwith = sharedname
293 constructors . nofsharedfonts = constructors . nofsharedfonts + 1
294 constructors . nofsharedvectors = constructors . nofsharedvectors + 1
295 else
296 shares [ checksum ] = fullname
297 end
298 target . fullname = fullname
299 hashes [ fonthash ] = fullname
300 end
301 end
302 end
303 end
304
305end
306
307directives . register ( " fonts.checksharing " , function ( v )
308 if not v then
309 report_defining ( " font sharing in backend is disabled " )
310 end
311 constructors . sharefonts = v
312end )
313
314function definers . resetnullfont ( )
315
316 local parameters = fonts . nulldata . parameters
317
318 parameters . slant = 0
319 parameters . space = 0
320 parameters . space_stretch = 0
321 parameters . space_shrink = 0
322 parameters . x_height = 0
323 parameters . quad = 0
324 parameters . extra_space = 0
325 parameters . designsize = 655360
326
327 constructors . enhanceparameters ( parameters )
328
329 definers . resetnullfont = function ( ) end
330end
331
332implement {
333 name = " resetnullfont " ,
334 onlyonce = true ,
335 actions = function ( )
336 for i = 1 , 7 do
337
338 context ( [[ \fontdimen%s\nullfont\zeropoint ]] , i )
339 end
340 definers . resetnullfont ( )
341 end
342}
343
344
345
346
347
348local needsnodemode = {
349
350 gsub_multiple = true ,
351
352
353 gsub_context = true ,
354 gsub_contextchain = true ,
355 gsub_reversecontextchain = true ,
356
357
358 gpos_mark2base = true ,
359 gpos_mark2ligature = true ,
360 gpos_mark2mark = true ,
361 gpos_cursive = true ,
362
363
364 gpos_context = true ,
365 gpos_contextchain = true ,
366}
367
368otftables . scripts . auto = " automatic fallback to latn when no dflt present "
369
370
371
372local function checkedscript ( tfmdata , resources , features )
373 local latn = false
374 local script = false
375 if resources . features then
376 for g , list in next , resources . features do
377 for f , scripts in next , list do
378 if scripts . dflt then
379 script = " dflt "
380 break
381 elseif scripts . latn then
382 latn = true
383 end
384 end
385 end
386 end
387 if not script then
388 script = latn and " latn " or " dflt "
389 end
390 if trace_automode then
391 report_defining ( " auto script mode, using script %a in font %!font:name! " , script , tfmdata )
392 end
393 features . script = script
394 return script
395end
396
397
398
399local function checkedmode ( tfmdata , resources , features )
400 local sequences = resources . sequences
401 if sequences and # sequences > 0 then
402 local script = features . script or " dflt "
403 local language = features . language or " dflt "
404 for feature , value in next , features do
405 if value then
406 local found = false
407 for i = 1 , # sequences do
408 local sequence = sequences [ i ]
409 local features = sequence . features
410 if features then
411 local scripts = features [ feature ]
412 if scripts then
413 local languages = scripts [ script ]
414 if languages and languages [ language ] then
415 if found then
416
417 if trace_automode then
418 report_defining ( " forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s " ,
419 " node " , tfmdata , feature , script , language , " multiple lookups " )
420 end
421 features . mode = " node "
422 return " node "
423 elseif needsnodemode [ sequence . type ] then
424 if trace_automode then
425 report_defining ( " forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s " ,
426 " node " , tfmdata , feature , script , language , " no base support " )
427 end
428 features . mode = " node "
429 return " node "
430 else
431
432 found = true
433 end
434 end
435 end
436 end
437 end
438 end
439 end
440 end
441 if trace_automode then
442 report_defining ( " forcing mode base, font %!font:name! " , tfmdata )
443 end
444 features . mode = " base "
445 return " base "
446end
447
448definers . checkedscript = checkedscript
449definers . checkedmode = checkedmode
450
451local function modechecker ( tfmdata , features , mode )
452 if trace_features then
453 report_features ( " fontname %!font:name!, features %!font:features! " , tfmdata , features )
454 end
455 local rawdata = tfmdata . shared . rawdata
456 local resources = rawdata and rawdata . resources
457 local script = features . script
458 if resources then
459 if script = = " auto " then
460 script = checkedscript ( tfmdata , resources , features )
461 end
462 if mode = = " auto " then
463 mode = checkedmode ( tfmdata , resources , features )
464 end
465 else
466 report_features ( " missing resources for font %!font:name! " , tfmdata )
467 end
468 return mode
469end
470
471registerotffeature {
472
473
474 name = " mode " ,
475 modechecker = modechecker ,
476}
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496do
497
498 local beforecopyingcharacters = sequencers . new {
499 name = " beforecopyingcharacters " ,
500 arguments = " target,original " ,
501 }
502
503 appendgroup ( beforecopyingcharacters , " before " )
504 appendgroup ( beforecopyingcharacters , " system " )
505 appendgroup ( beforecopyingcharacters , " after " )
506
507 function constructors . beforecopyingcharacters ( original , target )
508 local runner = beforecopyingcharacters . runner
509 if runner then
510 runner ( original , target )
511 end
512 end
513
514 local aftercopyingcharacters = sequencers . new {
515 name = " aftercopyingcharacters " ,
516 arguments = " target,original " ,
517 }
518
519 appendgroup ( aftercopyingcharacters , " before " )
520 appendgroup ( aftercopyingcharacters , " system " )
521 appendgroup ( aftercopyingcharacters , " after " )
522
523 function constructors . aftercopyingcharacters ( original , target )
524 local runner = aftercopyingcharacters . runner
525 if runner then
526 runner ( original , target )
527 end
528 end
529
530end
531
532
544
545
546
547
548
549
550
551
552
553local loadfont = definers . loadfont
554
555function definers . loadfont ( specification , size , id )
556 local variants = definers . methods . variants
557 local virtualfeatures = specification . features . virtual
558 if virtualfeatures and virtualfeatures . preset then
559 local variant = variants [ virtualfeatures . preset ]
560 if variant then
561 return variant ( specification , size , id )
562 end
563 else
564 local tfmdata = loadfont ( specification , size , id )
565
566 return tfmdata
567 end
568end
569
570local function predefined ( specification )
571 local variants = definers . methods . variants
572 local detail = specification . detail
573 if detail ~ = " " and variants [ detail ] then
574 specification . features . virtual = { preset = detail }
575 end
576 return specification
577end
578
579definers . registersplit ( " @ " , predefined , " virtual " )
580
581local normalize_features = otffeatures . normalize
582
583local function definecontext ( name , t )
584 local number = setups [ name ] and setups [ name ] . number or 0
585 if number = = 0 then
586 number = # numbers + 1
587 numbers [ number ] = name
588 end
589 t . number = number
590 setups [ name ] = t
591 return number , t
592end
593
594
595
596
597
598
599
600local h = setmetatableindex ( function ( t , k )
601 local v = " , " . . k . . " , "
602 t [ k ] = v
603 return v
604end )
605
606
607
608
609
610
611
612
613
614
615local function presetcontext ( name , parent , features )
616 if features = = " " and find ( parent , " = " , 1 , true ) then
617 features = parent
618 parent = " "
619 end
620 if not features or features = = " " then
621 features = { }
622 elseif type ( features ) = = " string " then
623 features = normalize_features ( settings_to_hash ( features ) )
624
625
626 for key , value in next , features do
627 if type ( value ) = = " string " and find ( value , " [=] " ) then
628 local t = settings_to_hash ( value )
629 if next ( t ) then
630 features [ key ] = sequenced ( normalize_features ( t , true ) , " , " )
631 end
632 end
633 end
634 else
635 features = normalize_features ( features )
636 end
637
638 if parent ~ = " " then
639 for p in gmatch ( parent , " [^, ]+ " ) do
640 local s = setups [ p ]
641 if s then
642 for k , v in next , s do
643
644
645 if features [ k ] = = nil then
646 features [ k ] = v
647 end
648 end
649 else
650
651 end
652 end
653 end
654
655
656
657
658
659
660
661
662
663
664
665 for feature , value in next , features do
666 if value = = nil then
667 local default = default_features [ feature ]
668 if default ~ = nil then
669 features [ feature ] = default
670 end
671 end
672 end
673
674
675 local t = { }
676 for k , v in next , features do
677
678 t [ k ] = v
679 end
680
681
682 local number = setups [ name ] and setups [ name ] . number or 0
683 if number = = 0 then
684 number = # numbers + 1
685 numbers [ number ] = name
686 end
687
688 t . number = number
689
690
691
692
693
694
695
696
697 setups [ name ] = t
698 return number , t
699end
700
701local function adaptcontext ( pattern , features )
702 local pattern = topattern ( pattern , false , true )
703 for name in next , setups do
704 if find ( name , pattern ) then
705 presetcontext ( name , name , features )
706 end
707 end
708end
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741local function contextnumber ( name )
742 local t = setups [ name ]
743 return t and t . number or 0
744end
745
746local function mergecontext ( currentnumber , extraname , option )
747 local extra = setups [ extraname ]
748 if extra then
749 local current = setups [ numbers [ currentnumber ] ]
750 local mergedfeatures = { }
751 local mergedname = nil
752 if option < 0 then
753 if current then
754 for k , v in next , current do
755 if not extra [ k ] then
756 mergedfeatures [ k ] = v
757 end
758 end
759 end
760 mergedname = currentnumber . . " - " . . extraname
761 else
762 if current then
763 for k , v in next , current do
764 mergedfeatures [ k ] = v
765 end
766 end
767 for k , v in next , extra do
768 mergedfeatures [ k ] = v
769 end
770 mergedname = currentnumber . . " + " . . extraname
771 end
772 local number = # numbers + 1
773 mergedfeatures . number = number
774 numbers [ number ] = mergedname
775 merged [ number ] = option
776 setups [ mergedname ] = mergedfeatures
777 return number
778 else
779 return currentnumber
780 end
781end
782
783local extrasets = { }
784
785setmetatableindex ( extrasets , function ( t , k )
786 local v = mergehashes ( setups , k )
787 t [ k ] = v
788 return v
789end )
790
791local function mergecontextfeatures ( currentname , extraname , how , mergedname )
792 local extra = setups [ extraname ] or extrasets [ extraname ]
793 if extra then
794 local current = setups [ currentname ]
795 local mergedfeatures = { }
796 if how = = " + " then
797 if current then
798 for k , v in next , current do
799 mergedfeatures [ k ] = v
800 end
801 end
802 for k , v in next , extra do
803 mergedfeatures [ k ] = v
804 end
805 if trace_merge then
806 report_features ( " merge %a, method %a, current %|T, extra %|T, result %|T " , mergedname , " add " , current or { } , extra , mergedfeatures )
807 end
808 elseif how = = " - " then
809 if current then
810 for k , v in next , current do
811 mergedfeatures [ k ] = v
812 end
813 end
814 for k , v in next , extra do
815
816 if v = = true then
817 mergedfeatures [ k ] = false
818 end
819 end
820 if trace_merge then
821 report_features ( " merge %a, method %a, current %|T, extra %|T, result %|T " , mergedname , " subtract " , current or { } , extra , mergedfeatures )
822 end
823 else
824 for k , v in next , extra do
825 mergedfeatures [ k ] = v
826 end
827 if trace_merge then
828 report_features ( " merge %a, method %a, result %|T " , mergedname , " replace " , mergedfeatures )
829 end
830 end
831 local number = # numbers + 1
832 mergedfeatures . number = number
833 numbers [ number ] = mergedname
834 merged [ number ] = option
835 setups [ mergedname ] = mergedfeatures
836 return number
837 else
838 return numbers [ currentname ] or 0
839 end
840end
841
842local function registercontext ( fontnumber , extraname , option )
843 local extra = setups [ extraname ]
844 if extra then
845 local mergedfeatures = { }
846 local mergedname = nil
847 if option < 0 then
848 mergedname = fontnumber . . " - " . . extraname
849 else
850 mergedname = fontnumber . . " + " . . extraname
851 end
852 for k , v in next , extra do
853 mergedfeatures [ k ] = v
854 end
855 local number = # numbers + 1
856 mergedfeatures . number = number
857 numbers [ number ] = mergedname
858 merged [ number ] = option
859 setups [ mergedname ] = mergedfeatures
860 return number
861 else
862 return 0
863 end
864end
865
866local function registercontextfeature ( mergedname , extraname , how )
867 local extra = setups [ extraname ]
868 if extra then
869 local mergedfeatures = { }
870 for k , v in next , extra do
871 mergedfeatures [ k ] = v
872 end
873 local number = # numbers + 1
874 mergedfeatures . number = number
875 numbers [ number ] = mergedname
876 merged [ number ] = how = = " = " and 1 or 2
877 setups [ mergedname ] = mergedfeatures
878 return number
879 else
880 report_features ( " unknown feature %a cannot be merged into %a using method %a " , extraname , mergedname , how )
881 return 0
882 end
883end
884
885specifiers . presetcontext = presetcontext
886specifiers . contextnumber = contextnumber
887specifiers . mergecontext = mergecontext
888specifiers . registercontext = registercontext
889specifiers . definecontext = definecontext
890
891
892
893constructors . hashmethods . virtual = function ( list )
894 local s = { }
895 local n = 0
896 for k , v in next , list do
897 n = n + 1
898
899
900
901
902
903
904
905 s [ n ] = k . . " = " . . tostring ( v )
906 end
907 if n > 0 then
908 sort ( s )
909 return concat ( s , " + " )
910 end
911end
912
913if not JITSUPPORTED then
914
915 constructors . hashmethods . normal = function ( list )
916 local s = { }
917 local n = 0
918 for k , v in next , list do
919 if not k then
920
921 elseif k = = " number " or k = = " features " then
922
923 else
924 n = n + 1
925 if type ( v ) = = " table " then
926
927 local t = { }
928 local m = 0
929 for k , v in next , v do
930 m = m + 1
931 t [ m ] = format ( " %q=%q " , k , v )
932 end
933 sort ( t )
934 s [ n ] = format ( " %q={%s} " , k , concat ( t , " , " ) )
935 else
936 s [ n ] = format ( " %q=%q " , k , v )
937 end
938 end
939 end
940 if n > 0 then
941 sort ( s )
942 return concat ( s , " + " )
943 end
944 end
945
946 constructors . hashmethods . virtual = function ( list )
947 local s = { }
948 local n = 0
949 for k , v in next , list do
950 n = n + 1
951 s [ n ] = format ( " %q=%q " , k , v )
952 end
953 if n > 0 then
954 sort ( s )
955 return concat ( s , " + " )
956 end
957 end
958
959end
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987function specifiers . showcontext ( name )
988 return setups [ name ] or setups [ numbers [ name ] ] or setups [ numbers [ tonumber ( name ) ] ] or { }
989end
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013local function splitcontext ( features )
1014 local n , sf
1015 if find ( features , " , " ) then
1016
1017
1018
1019
1020
1021
1022 setups [ features ] = nil
1023
1024
1025 n , sf = presetcontext ( features , features , " " )
1026 else
1027 sf = setups [ features ]
1028 if not sf then
1029
1030 n , sf = presetcontext ( features , " " , " " )
1031 end
1032 end
1033 return fastcopy ( sf )
1034end
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077specifiers . splitcontext = splitcontext
1078
1079function specifiers . contexttostring ( name , kind , separator , yes , no , strict , omit )
1080 return hash_to_string (
1081 mergedtable ( handlers [ kind ] . features . defaults or { } , setups [ name ] or { } ) ,
1082 separator , yes , no , strict , omit or { " number " }
1083 )
1084end
1085
1086local function starred ( features )
1087 local detail = features . detail
1088 if detail and detail ~ = " " then
1089 features . features . normal = splitcontext ( detail )
1090 else
1091 features . features . normal = { }
1092 end
1093 return features
1094end
1095
1096definers . registersplit ( ' * ' , starred , " featureset " )
1097
1098
1099
1100local space = P ( " " )
1101local spaces = space ^ 0
1102local separator = S ( " ;, " )
1103local equal = P ( " = " )
1104local sometext = C ( ( 1 - equal - space - separator ) ^ 1 )
1105local truevalue = P ( " + " ) * spaces * sometext * Cc ( true )
1106local falsevalue = P ( " - " ) * spaces * sometext * Cc ( false )
1107local somevalue = sometext * spaces * Cc ( true )
1108local keyvalue = sometext * spaces * equal * spaces * sometext
1109local pattern = Cf ( Ct ( " " ) * ( space + separator + Cg ( falsevalue + truevalue + keyvalue + somevalue ) ) ^ 0 , rawset )
1110
1111local function colonized ( specification )
1112 specification . features . normal = normalize_features ( lpegmatch ( pattern , specification . detail ) )
1113 return specification
1114end
1115
1116definers . registersplit ( " : " , colonized , " direct " )
1117
1118
1119
1120local sizepattern , splitpattern , specialscale do
1121
1122
1123
1124 local leftparent = ( P " ( " )
1125 local rightparent = ( P " ) " )
1126 local leftbrace = ( P " { " )
1127 local rightbrace = ( P " } " )
1128 local withinparents = leftparent * ( 1 - rightparent ) ^ 0 * rightparent
1129 local withinbraces = leftbrace * ( 1 - rightbrace ) ^ 0 * rightbrace
1130 local value = C ( ( withinparents + withinbraces + ( 1 - space ) ) ^ 1 )
1131 local dimension = C ( ( space / " " + P ( 1 ) ) ^ 1 )
1132 local rest = C ( P ( 1 ) ^ 0 )
1133 local scale_none = Cc ( 0 )
1134 local scale_at = ( P ( " at " ) + P ( " @ " ) ) * Cc ( 1 ) * spaces * dimension
1135 local scale_sa = P ( " sa " ) * Cc ( 2 ) * spaces * dimension
1136 local scale_mo = P ( " mo " ) * Cc ( 3 ) * spaces * dimension
1137 local scale_scaled = P ( " scaled " ) * Cc ( 4 ) * spaces * dimension
1138 local scale_ht = P ( " ht " ) * Cc ( 5 ) * spaces * dimension
1139 local scale_cp = P ( " cp " ) * Cc ( 6 ) * spaces * dimension
1140
1141 specialscale = { [ 5 ] = " ht " , [ 6 ] = " cp " }
1142
1143 sizepattern = spaces * ( scale_at + scale_sa + scale_mo + scale_ht + scale_cp + scale_scaled + scale_none )
1144 splitpattern = spaces * value * spaces * rest
1145
1146end
1147
1148function helpers . splitfontpattern ( str )
1149 local name , size = lpegmatch ( splitpattern , str )
1150 local kind , size = lpegmatch ( sizepattern , size )
1151 return name , kind , size
1152end
1153
1154function helpers . fontpatternhassize ( str )
1155 local name , size = lpegmatch ( splitpattern , str )
1156 local kind , size = lpegmatch ( sizepattern , size )
1157 return size or false
1158end
1159
1160local specification
1161
1162local getspecification = definers . getspecification
1163
1164
1165
1166
1167local specifiers = { }
1168
1169do
1170
1171 local starttiming = statistics . starttiming
1172 local stoptiming = statistics . stoptiming
1173
1174 local setmacro = tokens . setters . macro
1175
1176 implement {
1177 name = " definefont_one " ,
1178 arguments = " string " ,
1179 actions = function ( str )
1180 starttiming ( fonts )
1181 if trace_defining then
1182 report_defining ( " memory usage before: %s " , statistics . memused ( ) )
1183 report_defining ( " start stage one: %s " , str )
1184 end
1185 local fullname , size = lpegmatch ( splitpattern , str )
1186 local lookup , name , sub , method , detail = getspecification ( fullname )
1187 if not name then
1188 report_defining ( " strange definition %a " , str )
1189
1190 elseif name = = " unknown " then
1191
1192 else
1193
1194 setmacro ( " somefontname " , name , " global " )
1195 end
1196
1197 if size and size ~ = " " then
1198 local mode , size = lpegmatch ( sizepattern , size )
1199 if size and mode then
1200 texsetcount ( " scaledfontmode " , mode )
1201
1202 setmacro ( " somefontsize " , size )
1203 else
1204 texsetcount ( " scaledfontmode " , 0 )
1205
1206 end
1207 elseif true then
1208
1209 texsetcount ( " scaledfontmode " , 2 )
1210
1211 else
1212 texsetcount ( " scaledfontmode " , 0 )
1213
1214 end
1215 specification = definers . makespecification ( str , lookup , name , sub , method , detail , size )
1216 if trace_defining then
1217 report_defining ( " stop stage one " )
1218 end
1219 end
1220 }
1221
1222 local function nice_cs ( cs )
1223 return ( gsub ( cs , " .-> " , " " ) )
1224 end
1225
1226 local n = 0
1227 local busy = false
1228 local combinefeatures = false
1229
1230 directives . register ( " fonts.features.combine " , function ( v )
1231 combinefeatures = v
1232 end )
1233
1234
1235
1236
1237
1238
1239 implement {
1240 name = " definefont_two " ,
1241 arguments = {
1242 " boolean " , " string " , " string " , " integer " , " integer " , " string " , " string " , " string " , " string " ,
1243 " integer " , " integer " , " integer " , " string " , " string " , " string " , " string " , " integer " ,
1244 } ,
1245 actions = function (
1246 global ,
1247 cs ,
1248 str ,
1249 size ,
1250 inheritancemode ,
1251 classfeatures ,
1252 fontfeatures ,
1253 classfallbacks ,
1254 fontfallbacks ,
1255 mathsize ,
1256 textsize ,
1257 relativeid ,
1258 classgoodies ,
1259 goodies ,
1260 classdesignsize ,
1261 fontdesignsize ,
1262 scaledfontmode
1263 )
1264
1265 if trace_defining then
1266 report_defining ( " start stage two: %s, size %s, features %a & %a, mode %a " , str , size , classfeatures , fontfeatures , inheritancemode )
1267 end
1268
1269 local lookup , name , sub , method , detail = getspecification ( str or " " )
1270
1271 local designsize = fontdesignsize ~ = " " and fontdesignsize or classdesignsize or " "
1272 local designname = designsizefilename ( name , designsize , size )
1273 if designname and designname ~ = " " then
1274 if trace_defining or trace_designsize then
1275 report_defining ( " remapping name %a, specification %a, size %a, designsize %a " , name , designsize , size , designname )
1276 end
1277
1278 local o_lookup , o_name , o_sub , o_method , o_detail = getspecification ( designname )
1279 if o_lookup and o_lookup ~ = " " then lookup = o_lookup end
1280 if o_method and o_method ~ = " " then method = o_method end
1281 if o_detail and o_detail ~ = " " then detail = o_detail end
1282 name = o_name
1283 sub = o_sub
1284 end
1285
1286
1287 if lookup and lookup ~ = " " then
1288 specification . lookup = lookup
1289 end
1290 if relativeid and relativeid ~ = " " then
1291 local id = tonumber ( relativeid ) or 0
1292 specification . relativeid = id > 0 and id
1293 end
1294
1295 specification . name = name
1296 specification . size = size
1297 specification . sub = ( sub and sub ~ = " " and sub ) or specification . sub
1298 specification . mathsize = mathsize
1299 specification . textsize = textsize
1300 specification . goodies = goodies
1301 specification . cs = cs
1302 specification . global = global
1303 specification . scalemode = scaledfontmode
1304 if detail and detail ~ = " " then
1305 specification . method = method or " * "
1306 specification . detail = detail
1307 elseif specification . detail and specification . detail ~ = " " then
1308
1309 elseif inheritancemode = = 0 then
1310
1311 elseif inheritancemode = = 1 then
1312
1313 if fontfeatures and fontfeatures ~ = " " then
1314 specification . method = " * "
1315 specification . detail = fontfeatures
1316 end
1317 if fontfallbacks and fontfallbacks ~ = " " then
1318 specification . fallbacks = fontfallbacks
1319 end
1320 elseif inheritancemode = = 2 then
1321
1322 if classfeatures and classfeatures ~ = " " then
1323 specification . method = " * "
1324 specification . detail = classfeatures
1325 end
1326 if classfallbacks and classfallbacks ~ = " " then
1327 specification . fallbacks = classfallbacks
1328 end
1329 elseif inheritancemode = = 3 then
1330
1331 if combinefeatures then
1332 if classfeatures and classfeatures ~ = " " then
1333 specification . method = " * "
1334 if fontfeatures and fontfeatures ~ = " " and fontfeatures ~ = classfeatures then
1335 specification . detail = classfeatures . . " , " . . fontfeatures
1336 else
1337 specification . detail = classfeatures
1338 end
1339 elseif fontfeatures and fontfeatures ~ = " " then
1340 specification . method = " * "
1341 specification . detail = fontfeatures
1342 end
1343 else
1344 if fontfeatures and fontfeatures ~ = " " then
1345 specification . method = " * "
1346 specification . detail = fontfeatures
1347 elseif classfeatures and classfeatures ~ = " " then
1348 specification . method = " * "
1349 specification . detail = classfeatures
1350 end
1351 end
1352 if fontfallbacks and fontfallbacks ~ = " " then
1353 specification . fallbacks = fontfallbacks
1354 elseif classfallbacks and classfallbacks ~ = " " then
1355 specification . fallbacks = classfallbacks
1356 end
1357 elseif inheritancemode = = 4 then
1358
1359 if combinefeatures then
1360 if fontfeatures and fontfeatures ~ = " " then
1361 specification . method = " * "
1362 if classfeatures and classfeatures ~ = " " and classfeatures ~ = fontfeatures then
1363 specification . detail = fontfeatures . . " , " . . classfeatures
1364 else
1365 specification . detail = fontfeatures
1366 end
1367 elseif classfeatures and classfeatures ~ = " " then
1368 specification . method = " * "
1369 specification . detail = classfeatures
1370 end
1371 else
1372 if classfeatures and classfeatures ~ = " " then
1373 specification . method = " * "
1374 specification . detail = classfeatures
1375 elseif fontfeatures and fontfeatures ~ = " " then
1376 specification . method = " * "
1377 specification . detail = fontfeatures
1378 end
1379 end
1380 if classfallbacks and classfallbacks ~ = " " then
1381 specification . fallbacks = classfallbacks
1382 elseif fontfallbacks and fontfallbacks ~ = " " then
1383 specification . fallbacks = fontfallbacks
1384 end
1385 end
1386
1387 local tfmdata = definers . read ( specification , size )
1388
1389 local lastfontid = 0
1390 local tfmtype = type ( tfmdata )
1391 if tfmtype = = " table " then
1392
1393 local characters = tfmdata . characters
1394 local parameters = tfmdata . parameters
1395 local properties = tfmdata . properties
1396
1397 characters [ 0 ] = nil
1398
1399
1400
1401
1402 local fallbacks = specification . fallbacks or " "
1403 local mathsize = ( mathsize = = 1 or mathsize = = 2 or mathsize = = 3 ) and mathsize or nil
1404 if fallbacks ~ = " " and mathsize and not busy then
1405 busy = true
1406
1407
1408
1409
1410
1411 if trace_defining then
1412 report_defining ( " defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a " ,
1413 name , id , nice_cs ( cs ) , classfeatures , fontfeatures , classfallbacks , fontfallbacks , 1 )
1414 end
1415 mathematics . resolvefallbacks ( tfmdata , specification , fallbacks )
1416 context ( function ( )
1417 busy = false
1418 mathematics . finishfallbacks ( tfmdata , specification , fallbacks )
1419 local id = definefont ( tfmdata )
1420 csnames [ id ] = specification . cs
1421 properties . id = id
1422 definers . register ( tfmdata , id )
1423 texdefinefont ( global , cs , id )
1424 constructors . cleanuptable ( tfmdata )
1425 constructors . finalize ( tfmdata )
1426 if trace_defining then
1427 report_defining ( " defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a " ,
1428 name , id , nice_cs ( cs ) , classfeatures , fontfeatures , classfallbacks , fontfallbacks , 2 )
1429 end
1430
1431 local size = round ( tfmdata . parameters . size or 655360 )
1432 setmacro ( " somefontsize " , size . . " sp " )
1433
1434 texsetcount ( " scaledfontsize " , size )
1435 lastfontid = id
1436
1437 if trace_defining then
1438 report_defining ( " memory usage after: %s " , statistics . memused ( ) )
1439 report_defining ( " stop stage two " )
1440 end
1441
1442 texsetcount ( " global " , " lastfontid " , lastfontid )
1443 specifiers [ lastfontid ] = { str , size }
1444 if not mathsize then
1445
1446 elseif mathsize = = 0 then
1447
1448 else
1449
1450 lastmathids [ mathsize ] = lastfontid
1451 end
1452 stoptiming ( fonts )
1453 end )
1454 return
1455 else
1456 local id = definefont ( tfmdata )
1457 csnames [ id ] = specification . cs
1458 properties . id = id
1459 definers . register ( tfmdata , id )
1460 texdefinefont ( global , cs , id )
1461 constructors . cleanuptable ( tfmdata )
1462 constructors . finalize ( tfmdata )
1463 if trace_defining then
1464 report_defining ( " defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a " ,
1465 name , id , nice_cs ( cs ) , classfeatures , fontfeatures , classfallbacks , fontfallbacks , " - " )
1466 end
1467
1468 local size = round ( tfmdata . parameters . size or 655360 )
1469 setmacro ( " somefontsize " , size . . " sp " )
1470
1471 texsetcount ( " scaledfontsize " , size )
1472 lastfontid = id
1473 end
1474 elseif tfmtype = = " number " then
1475 if trace_defining then
1476 report_defining ( " reusing %s, id %a, target %a, features %a / %a, fallbacks %a / %a, goodies %a / %a, designsize %a / %a " ,
1477 name , tfmdata , nice_cs ( cs ) , classfeatures , fontfeatures , classfallbacks , fontfallbacks , classgoodies , goodies , classdesignsize , fontdesignsize )
1478 end
1479 csnames [ tfmdata ] = specification . cs
1480 texdefinefont ( global , cs , tfmdata )
1481
1482 local size = round ( fontdata [ tfmdata ] . parameters . size or 0 )
1483
1484 setmacro ( " somefontsize " , size . . " sp " )
1485 texsetcount ( " scaledfontsize " , size )
1486 lastfontid = tfmdata
1487 else
1488 report_defining ( " unable to define %a as %a " , name , nice_cs ( cs ) )
1489 lastfontid = -1
1490 texsetcount ( " scaledfontsize " , 0 )
1491
1492 end
1493 if trace_defining then
1494 report_defining ( " memory usage after: %s " , statistics . memused ( ) )
1495 report_defining ( " stop stage two " )
1496 end
1497
1498 texsetcount ( " global " , " lastfontid " , lastfontid )
1499 specifiers [ lastfontid ] = { str , size }
1500 if not mathsize then
1501
1502 elseif mathsize = = 0 then
1503
1504 else
1505
1506 lastmathids [ mathsize ] = lastfontid
1507 end
1508
1509 stoptiming ( fonts )
1510 end
1511 }
1512
1513 implement {
1514 name = " specifiedfontspec " ,
1515 arguments = " integer " ,
1516 actions = function ( id )
1517 local f = specifiers [ id ]
1518 if f then
1519 context ( f [ 1 ] )
1520 end
1521 end
1522 }
1523
1524 implement {
1525 name = " specifiedfontsize " ,
1526 arguments = " integer " ,
1527 actions = function ( id )
1528 local f = specifiers [ id ]
1529 if f then
1530 context ( f [ 2 ] )
1531 end
1532 end
1533 }
1534
1535 implement {
1536 name = " specifiedfont " ,
1537 arguments = { " integer " , " number " } ,
1538 actions = function ( id , size )
1539 local f = specifiers [ id ]
1540 if f and size then
1541 context ( " %s at %0.2p " , f [ 1 ] , size * f [ 2 ] )
1542 end
1543 end
1544 }
1545
1546
1547 local function define ( specification )
1548
1549 local name = specification . name
1550 if not name or name = = " " then
1551 return -1
1552 else
1553 starttiming ( fonts )
1554
1555
1556
1557 local lookup , name , sub , method , detail = getspecification ( name or " " )
1558
1559 specification . name = ( name ~ = " " and name ) or specification . name
1560
1561 specification . lookup = specification . lookup or ( lookup ~ = " " and lookup ) or " file "
1562 specification . size = specification . size or 655260
1563 specification . sub = specification . sub or ( sub ~ = " " and sub ) or " "
1564 specification . method = specification . method or ( method ~ = " " and method ) or " * "
1565 specification . detail = specification . detail or ( detail ~ = " " and detail ) or " "
1566
1567 if type ( specification . size ) = = " string " then
1568 specification . size = texsp ( specification . size ) or 655260
1569 end
1570
1571 specification . specification = " "
1572 specification . resolved = " "
1573 specification . forced = " "
1574 specification . features = { }
1575
1576
1577
1578 local cs = specification . cs
1579 if cs = = " " then
1580 cs = nil
1581 specification . cs = nil
1582 specification . global = false
1583 elseif specification . global = = nil then
1584 specification . global = false
1585 end
1586
1587 local tfmdata = definers . read ( specification , specification . size )
1588 if not tfmdata then
1589 return -1 , nil
1590 elseif type ( tfmdata ) = = " number " then
1591 if cs then
1592 texdefinefont ( specification . global , cs , tfmdata )
1593 csnames [ tfmdata ] = cs
1594 end
1595 stoptiming ( fonts )
1596 return tfmdata , fontdata [ tfmdata ]
1597 else
1598 local id = definefont ( tfmdata )
1599 tfmdata . properties . id = id
1600 definers . register ( tfmdata , id )
1601 if cs then
1602 texdefinefont ( specification . global , cs , id )
1603 csnames [ id ] = cs
1604 end
1605 constructors . cleanuptable ( tfmdata )
1606 constructors . finalize ( tfmdata )
1607 stoptiming ( fonts )
1608 return id , tfmdata
1609 end
1610 end
1611 end
1612
1613 definers . define = define
1614
1615
1616
1617
1618
1619 local n = 0
1620
1621 function definers . internal ( specification , cs )
1622 specification = specification or { }
1623 local name = specification . name
1624 local size = tonumber ( specification . size )
1625 local number = tonumber ( specification . number )
1626 local id = nil
1627 if not size then
1628 size = texgetdimen ( " bodyfontsize " )
1629 end
1630 if number then
1631 id = number
1632 elseif name and name ~ = " " then
1633 local cs = cs or specification . cs
1634 if not cs then
1635 n = n + 1
1636
1637 cs = " internal font " . . n
1638 else
1639 specification . cs = cs
1640 end
1641 id = define {
1642 name = name ,
1643 size = size ,
1644 cs = cs ,
1645 }
1646 end
1647 if not id then
1648 id = currentfont ( )
1649 end
1650 return id , csnames [ id ]
1651 end
1652
1653 local read
1654
1655 if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then
1656 read = function ( name , size )
1657 return ( define { name = name , size = size } or 0 )
1658 end
1659 else
1660 read = definers . read
1661 end
1662
1663 callbacks . register ( ' define_font ' , read , " definition of fonts (tfmdata preparation) " )
1664
1665
1666
1667 local infofont = 0
1668
1669 function fonts . infofont ( )
1670 if infofont = = 0 then
1671 infofont = define { name = " dejavusansmono " , size = texsp ( " 6pt " ) }
1672 end
1673 return infofont
1674 end
1675
1676
1677
1678 implement { name = " tf " , actions = function ( ) setmacro ( " fontalternative " , " tf " ) end }
1679 implement { name = " bf " , actions = function ( ) setmacro ( " fontalternative " , " bf " ) end }
1680 implement { name = " it " , actions = function ( ) setmacro ( " fontalternative " , " it " ) end }
1681 implement { name = " sl " , actions = function ( ) setmacro ( " fontalternative " , " sl " ) end }
1682 implement { name = " bi " , actions = function ( ) setmacro ( " fontalternative " , " bi " ) end }
1683 implement { name = " bs " , actions = function ( ) setmacro ( " fontalternative " , " bs " ) end }
1684
1685end
1686
1687local enable_auto_r_scale = false
1688
1689experiments . register ( " fonts.autorscale " , function ( v )
1690 enable_auto_r_scale = v
1691end )
1692
1693
1694
1695
1696
1697local calculatescale = constructors . calculatescale
1698
1699function constructors . calculatescale ( tfmdata , scaledpoints , relativeid , specification )
1700 if specification then
1701 local scalemode = specification . scalemode
1702 local special = scalemode and specialscale [ scalemode ]
1703 if special then
1704
1705 local parameters = tfmdata . parameters
1706
1707 if special = = " ht " then
1708 local height = parameters . ascender / parameters . units
1709 scaledpoints = scaledpoints / height
1710 elseif special = = " cp " then
1711 local glyph = tfmdata . descriptions [ utfbyte ( " X " ) ]
1712 local height = ( glyph and glyph . height or parameters . ascender ) / parameters . units
1713 scaledpoints = scaledpoints / height
1714 end
1715 end
1716 end
1717 local scaledpoints , delta = calculatescale ( tfmdata , scaledpoints )
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729 return round ( scaledpoints ) , round ( delta )
1730end
1731
1732local designsizes = constructors . designsizes
1733
1734
1735
1736
1737function constructors . hashinstance ( specification , force )
1738 local hash = specification . hash
1739 local size = specification . size
1740 local fallbacks = specification . fallbacks
1741 if force or not hash then
1742 hash = constructors . hashfeatures ( specification )
1743 specification . hash = hash
1744 end
1745 if size < 1000 and designsizes [ hash ] then
1746 size = round ( constructors . scaled ( size , designsizes [ hash ] ) )
1747 else
1748 size = round ( size )
1749 end
1750 specification . size = size
1751 if fallbacks then
1752 return hash . . ' @ ' . . size . . ' @ ' . . fallbacks
1753 else
1754 local scalemode = specification . scalemode
1755 local special = scalemode and specialscale [ scalemode ]
1756 if special then
1757 return hash . . ' @ ' . . size . . ' @ ' . . special
1758 else
1759 return hash . . ' @ ' . . size
1760 end
1761 end
1762end
1763
1764
1765
1766local resolvers = definers . resolvers
1767local hashfeatures = constructors . hashfeatures
1768
1769function definers . resolve ( specification )
1770 if not specification . resolved or specification . resolved = = " " then
1771 local r = resolvers [ specification . lookup ]
1772 if r then
1773 r ( specification )
1774 end
1775 end
1776 if specification . forced = = " " then
1777 specification . forced = nil
1778 else
1779 specification . forced = specification . forced
1780 end
1781
1782
1783
1784 local goodies = specification . goodies
1785 if goodies and goodies ~ = " " then
1786
1787 local normal = specification . features . normal
1788 if not normal then
1789 specification . features . normal = { goodies = goodies }
1790 elseif not normal . goodies then
1791 local g = normal . goodies
1792 if g and g ~ = " " then
1793 normal . goodies = formatters [ " %s,%s " ] ( g , goodies )
1794 else
1795 normal . goodies = goodies
1796 end
1797 end
1798 end
1799
1800 local hash = hashfeatures ( specification )
1801 local name = specification . name or " badfont "
1802 local sub = specification . sub
1803 if sub and sub ~ = " " then
1804 specification . hash = lower ( name . . " @ " . . sub . . ' @ ' . . hash )
1805 else
1806 specification . hash = lower ( name . . " @ " . . ' @ ' . . hash )
1807 end
1808
1809 return specification
1810end
1811
1812
1813
1814local mappings = fonts . mappings
1815
1816local loaded = {
1817
1818
1819
1820
1821}
1822
1823function mappings . loadfile ( name )
1824 name = file . addsuffix ( name , " map " )
1825 if not loaded [ name ] then
1826 if trace_mapfiles then
1827 report_mapfiles ( " loading map file %a " , name )
1828 end
1829 lpdf . setmapfile ( name )
1830 loaded [ name ] = true
1831 end
1832end
1833
1834local loaded = {
1835}
1836
1837function mappings . loadline ( how , line )
1838 if line then
1839 how = how . . " " . . line
1840 elseif how = = " " then
1841 how = " = " . . line
1842 end
1843 if not loaded [ how ] then
1844 if trace_mapfiles then
1845 report_mapfiles ( " processing map line %a " , line )
1846 end
1847 lpdf . setmapline ( how )
1848 loaded [ how ] = true
1849 end
1850end
1851
1852function mappings . reset ( )
1853 lpdf . setmapfile ( " " )
1854end
1855
1856implement {
1857 name = " loadmapfile " ,
1858 actions = mappings . loadfile ,
1859 arguments = " string "
1860}
1861
1862implement {
1863 name = " loadmapline " ,
1864 actions = mappings . loadline ,
1865 arguments = " string "
1866}
1867
1868implement {
1869 name = " resetmapfiles " ,
1870 actions = mappings . reset ,
1871 arguments = " string "
1872}
1873
1874
1875
1876
1877
1878local pattern = P ( " P " )
1879 * ( lpeg . patterns . hexdigit ^ 4 / function ( s ) return tonumber ( s , 16 ) end )
1880 * P ( -1 )
1881
1882local function nametoslot ( name )
1883 local t = type ( name )
1884 if t = = " string " then
1885 local unic = unicodes [ true ]
1886 local slot = unic [ name ]
1887 if slot then
1888 return slot
1889 end
1890
1891 local slot = unic [ gsub ( name , " _ " , " " ) ] or unic [ gsub ( name , " _ " , " - " ) ] or
1892 unic [ gsub ( name , " - " , " " ) ] or unic [ gsub ( name , " - " , " _ " ) ] or
1893 unic [ gsub ( name , " " , " _ " ) ] or unic [ gsub ( name , " " , " - " ) ]
1894 if slot then
1895 return slot
1896 end
1897
1898 if not aglunicodes then
1899 aglunicodes = encodings . agl . unicodes
1900 end
1901 local char = characters [ true ]
1902 local slot = aglunicodes [ name ]
1903 if slot and char [ slot ] then
1904 return slot
1905 end
1906 local slot = lpegmatch ( pattern , name )
1907 if slot and char [ slot ] then
1908 return slot
1909 end
1910
1911 elseif t = = " number " then
1912 if characters [ true ] [ name ] then
1913 return slot
1914 else
1915
1916 end
1917 end
1918end
1919
1920local found = { }
1921
1922local function descriptiontoslot ( name )
1923 local t = type ( name )
1924 if t = = " string " then
1925
1926 local list = sortedkeys ( chardata )
1927 local slot = found [ name ]
1928 local char = characters [ true ]
1929 if slot then
1930 return char [ slot ] and slot or nil
1931 end
1932 local NAME = upper ( name )
1933 for i = 1 , # list do
1934 slot = list [ i ]
1935 local c = chardata [ slot ]
1936 local d = c . description
1937 if d = = NAME then
1938 found [ name ] = slot
1939 return char [ slot ] and slot or nil
1940 end
1941 end
1942 for i = 1 , # list do
1943 slot = list [ i ]
1944 local c = chardata [ slot ]
1945 local s = c . synonyms
1946 if s then
1947 for i = 1 , # s do
1948 local si = s [ i ]
1949 if si = = name then
1950 found [ name ] = si
1951 return char [ slot ] and slot or nil
1952 end
1953 end
1954 end
1955 end
1956 for i = 1 , # list do
1957 slot = list [ i ]
1958 local c = chardata [ slot ]
1959 local d = c . description
1960 if d and find ( d , NAME ) then
1961 found [ name ] = slot
1962 return char [ slot ] and slot or nil
1963 end
1964 end
1965 for i = 1 , # list do
1966 slot = list [ i ]
1967 local c = chardata [ slot ]
1968 local s = c . synonyms
1969 if s then
1970 for i = 1 , # s do
1971 local si = s [ i ]
1972 if find ( s [ i ] , name ) then
1973 found [ name ] = si
1974 return char [ slot ] and slot or nil
1975 end
1976 end
1977 end
1978 end
1979
1980 elseif t = = " number " then
1981 if characters [ true ] [ name ] then
1982 return slot
1983 else
1984
1985 end
1986 end
1987end
1988
1989local function indextoslot ( font , index )
1990 if not index then
1991 index = font
1992 font = true
1993 end
1994 local r = resources [ font ]
1995 if r then
1996 local indices = r . indices
1997 if not indices then
1998 indices = { }
1999 local c = characters [ font ]
2000 for unicode , data in next , c do
2001 local di = data . index
2002 if di then
2003 indices [ di ] = unicode
2004 end
2005 end
2006 r . indices = indices
2007 end
2008 return indices [ tonumber ( index ) ]
2009 end
2010end
2011
2012do
2013
2014 local entities = characters . entities
2015 local lowered = { }
2016
2017 setmetatableindex ( lowered , function ( t , k )
2018 for k , v in next , entities do
2019 local l = lower ( k )
2020 if not entities [ l ] then
2021 lowered [ l ] = v
2022 end
2023 end
2024 setmetatableindex ( lowered , nil )
2025 return lowered [ k ]
2026 end )
2027
2028 local methods = {
2029
2030 e = function ( name )
2031 return entities [ name ] or lowered [ name ] or name
2032 end ,
2033
2034 x = function ( name )
2035 local n = tonumber ( name , 16 )
2036 return n and utfchar ( n ) or name
2037 end ,
2038
2039 d = function ( name )
2040 local n = tonumber ( name )
2041 return n and utfchar ( n ) or name
2042 end ,
2043
2044 s = function ( name )
2045 local n = tonumber ( name , 16 )
2046 local n = n and indextoslot ( n )
2047 return n and utfchar ( n ) or name
2048 end ,
2049
2050 i = function ( name )
2051 local n = tonumber ( name )
2052 local n = n and indextoslot ( n )
2053 return n and utfchar ( n ) or name
2054 end ,
2055
2056 n = function ( name )
2057 local n = nametoslot ( name )
2058 return n and utfchar ( n ) or name
2059 end ,
2060
2061 u = function ( name )
2062 local n = descriptiontoslot ( name , false )
2063 return n and utfchar ( n ) or name
2064 end ,
2065
2066 a = function ( name )
2067 local n = nametoslot ( name ) or descriptiontoslot ( name )
2068 return n and utfchar ( n ) or name
2069 end ,
2070
2071 c = function ( name )
2072 return name
2073 end ,
2074 }
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086 local splitter = C ( 1 ) * P ( " : " ) * C ( P ( 1 ) ^ 1 ) / function ( method , name )
2087 local action = methods [ method ]
2088 return action and action ( name ) or name
2089 end
2090
2091 local function tochar ( str )
2092 local t = type ( str )
2093 if t = = " number " then
2094 return utfchar ( str )
2095 elseif t = = " string " then
2096 return lpegmatch ( splitter , str ) or str
2097 else
2098 return str
2099 end
2100 end
2101
2102 helpers . nametoslot = nametoslot
2103 helpers . descriptiontoslot = descriptiontoslot
2104 helpers . indextoslot = indextoslot
2105 helpers . tochar = tochar
2106
2107
2108
2109 implement {
2110 name = " fontchar " ,
2111 actions = { nametoslot , ctx_char } ,
2112 arguments = " string " ,
2113 }
2114
2115 implement {
2116 name = " fontcharbyindex " ,
2117 actions = { indextoslot , ctx_char } ,
2118 arguments = " integer " ,
2119 }
2120
2121 implement {
2122 name = " tochar " ,
2123 actions = { tochar , ctx_safechar } ,
2124 arguments = " string " ,
2125 }
2126
2127end
2128
2129
2130
2131function loggers . reportdefinedfonts ( )
2132 if trace_usage then
2133 local t , tn = { } , 0
2134 for id , data in sortedhash ( fontdata ) do
2135 local properties = data . properties or { }
2136 local parameters = data . parameters or { }
2137 tn = tn + 1
2138 t [ tn ] = {
2139 formatters [ " %03i " ] ( id or 0 ) ,
2140 formatters [ " %p " ] ( parameters . size or 0 ) ,
2141 properties . type or " real " ,
2142 properties . format or " unknown " ,
2143 properties . name or " " ,
2144 properties . psname or " " ,
2145 properties . fullname or " " ,
2146 properties . sharedwith or " " ,
2147 }
2148 end
2149 formatcolumns ( t , " " )
2150
2151 logs . startfilelogging ( report , " defined fonts " )
2152 for k = 1 , tn do
2153 report ( t [ k ] )
2154 end
2155 logs . stopfilelogging ( )
2156 end
2157end
2158
2159logs . registerfinalactions ( loggers . reportdefinedfonts )
2160
2161function loggers . reportusedfeatures ( )
2162
2163 if trace_usage then
2164 local t , n = { } , # numbers
2165 for i = 1 , n do
2166 local name = numbers [ i ]
2167 local setup = setups [ name ]
2168 local n = setup . number
2169 setup . number = nil
2170 t [ i ] = { i , name , sequenced ( setup , false , true ) }
2171 setup . number = n
2172 end
2173 formatcolumns ( t , " " )
2174 logs . startfilelogging ( report , " defined featuresets " )
2175 for k = 1 , n do
2176 report ( t [ k ] )
2177 end
2178 logs . stopfilelogging ( )
2179 end
2180end
2181
2182logs . registerfinalactions ( loggers . reportusedfeatures )
2183
2184
2185
2186statistics . register ( " font engine " , function ( )
2187 local elapsed = statistics . elapsedseconds ( fonts )
2188 local nofshared = constructors . nofsharedfonts or 0
2189 local nofloaded = constructors . noffontsloaded or 0
2190 if nofshared > 0 then
2191 return format ( " otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, %s shared in backend, %s common vectors, %s common hashes, load time %s " ,
2192 otf . version , afm . version , tfm . version , nofloaded ,
2193 nofshared , constructors . nofsharedvectors , constructors . nofsharedhashes ,
2194 elapsed )
2195 elseif nofloaded > 0 and elapsed then
2196 return format ( " otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, load time %s " ,
2197 otf . version , afm . version , tfm . version , nofloaded ,
2198 elapsed )
2199 else
2200 return format ( " otf %0.3f, afm %0.3f, tfm %0.3f " ,
2201 otf . version , afm . version , tfm . version )
2202 end
2203end )
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220local Shapes = {
2221 serif = " Serif " ,
2222 sans = " Sans " ,
2223 mono = " Mono " ,
2224}
2225
2226local ctx_startfontclass = context . startfontclass
2227local ctx_stopfontclass = context . stopfontclass
2228local ctx_definefontsynonym = context . definefontsynonym
2229local ctx_dofastdefinetypeface = context . dofastdefinetypeface
2230
2231function fonts . definetypeface ( name , t )
2232 if type ( name ) = = " table " then
2233
2234 t = name
2235 elseif t then
2236 if type ( t ) = = " string " then
2237
2238 t = settings_to_hash ( name )
2239 else
2240
2241 end
2242 t . name = t . name or name
2243 else
2244
2245 t = settings_to_hash ( name )
2246 end
2247 local p = t . preset and fonts . typefaces [ t . preset ] or { }
2248 local name = t . name or " unknowntypeface "
2249 local shortcut = t . shortcut or p . shortcut or " rm "
2250 local size = t . size or p . size or " default "
2251 local shape = t . shape or p . shape or " serif "
2252 local fontname = t . fontname or p . fontname or " unknown "
2253 local normalweight = t . normalweight or t . weight or p . normalweight or p . weight or " normal "
2254 local boldweight = t . boldweight or t . weight or p . boldweight or p . weight or " normal "
2255 local normalwidth = t . normalwidth or t . width or p . normalwidth or p . width or " normal "
2256 local boldwidth = t . boldwidth or t . width or p . boldwidth or p . width or " normal "
2257 Shape = Shapes [ shape ] or " Serif "
2258 ctx_startfontclass { name }
2259 ctx_definefontsynonym ( { formatters [ " %s " ] ( Shape ) } , { formatters [ " spec:%s-%s-regular-%s " ] ( fontname , normalweight , normalwidth ) } )
2260 ctx_definefontsynonym ( { formatters [ " %sBold " ] ( Shape ) } , { formatters [ " spec:%s-%s-regular-%s " ] ( fontname , boldweight , boldwidth ) } )
2261 ctx_definefontsynonym ( { formatters [ " %sBoldItalic " ] ( Shape ) } , { formatters [ " spec:%s-%s-italic-%s " ] ( fontname , boldweight , boldwidth ) } )
2262 ctx_definefontsynonym ( { formatters [ " %sItalic " ] ( Shape ) } , { formatters [ " spec:%s-%s-italic-%s " ] ( fontname , normalweight , normalwidth ) } )
2263 ctx_stopfontclass ( )
2264 local settings = sequenced ( { features = t . features } , " , " )
2265 ctx_dofastdefinetypeface ( name , shortcut , shape , size , settings )
2266end
2267
2268implement {
2269 name = " definetypeface " ,
2270 actions = fonts . definetypeface ,
2271 arguments = " 2 strings "
2272}
2273
2274function fonts . current ( )
2275 return fontdata [ currentfont ( ) ] or fontdata [ 0 ]
2276end
2277
2278function fonts . currentid ( )
2279 return currentfont ( ) or 0
2280end
2281
2282
2283
2284
2285local dimenfactors = number . dimenfactors
2286
2287function helpers . dimenfactor ( unit , id )
2288 if unit = = " ex " then
2289 return id and exheights [ id ] or 282460
2290 elseif unit = = " em " then
2291 return id and emwidths [ id ] or 655360
2292 else
2293 local du = dimenfactors [ unit ]
2294 return du and 1 / du or tonumber ( unit ) or 1
2295 end
2296end
2297
2298local function digitwidth ( font )
2299 local tfmdata = fontdata [ font ]
2300 local parameters = tfmdata . parameters
2301 local width = parameters . digitwidth
2302 if not width then
2303 width = round ( parameters . quad / 2 )
2304 local characters = tfmdata . characters
2305 for i = 48 , 57 do
2306 local wd = round ( characters [ i ] . width )
2307 if wd > width then
2308 width = wd
2309 end
2310 end
2311 parameters . digitwidth = width
2312 end
2313 return width
2314end
2315
2316helpers . getdigitwidth = digitwidth
2317helpers . setdigitwidth = digitwidth
2318
2319
2320
2321function helpers . getparameters ( tfmdata )
2322 local p = { }
2323 local m = p
2324 local parameters = tfmdata . parameters
2325 while true do
2326 for k , v in next , parameters do
2327 m [ k ] = v
2328 end
2329 parameters = getmetatable ( parameters )
2330 parameters = parameters and parameters . __index
2331 if type ( parameters ) = = " table " then
2332 m = { }
2333 p . metatable = m
2334 else
2335 break
2336 end
2337 end
2338 return p
2339end
2340
2341if environment . initex then
2342
2343 local function names ( t )
2344 local nt = # t
2345 if nt > 0 then
2346 local n = { }
2347 for i = 1 , nt do
2348 n [ i ] = t [ i ] . name
2349 end
2350 return concat ( n , " " )
2351 else
2352 return " - "
2353 end
2354 end
2355
2356 statistics . register ( " font processing " , function ( )
2357 local l = { }
2358 for what , handler in table . sortedpairs ( handlers ) do
2359 local features = handler and handler . features
2360 if features then
2361 l [ # l + 1 ] = format ( " [%s (base initializers: %s) (base processors: %s) (base manipulators: %s) (node initializers: %s) (node processors: %s) (node manipulators: %s)] " ,
2362 what ,
2363 names ( features . initializers . base ) ,
2364 names ( features . processors . base ) ,
2365 names ( features . manipulators . base ) ,
2366 names ( features . initializers . node ) ,
2367 names ( features . processors . node ) ,
2368 names ( features . manipulators . node )
2369 )
2370 end
2371 end
2372 return concat ( l , " | " )
2373 end )
2374
2375end
2376
2377
2378
2379
2380
2381
2382
2383setmetatableindex ( dimenfactors , function ( t , k )
2384 if k = = " ex " then
2385 return 1 / exheights [ currentfont ( ) ]
2386 elseif k = = " em " then
2387 return 1 / emwidths [ currentfont ( ) ]
2388 elseif k = = " pct " or k = = " % " then
2389 return 1 / ( texget ( " hsize " ) / 100 )
2390 else
2391
2392 return false
2393 end
2394end )
2395
2396dimenfactors . ex = nil
2397dimenfactors . em = nil
2398dimenfactors [ " % " ] = nil
2399dimenfactors . pct = nil
2400
2401
2405
2406do
2407
2408
2409
2410 local texsetglyphdata = tex . setglyphdata
2411 local texgetglyphdata = tex . getglyphdata
2412
2413 if not texsetglyphdata then
2414
2415 local texsetattribute = tex . setattribute
2416 local texgetattribute = tex . getattribute
2417
2418 texsetglyphdata = function ( n ) return texsetattribute ( 0 , n ) end
2419 texgetglyphdata = function ( ) return texgetattribute ( 0 ) end
2420
2421 tex . setglyphdata = texsetglyphdata
2422 tex . getglyphdata = texgetglyphdata
2423
2424 end
2425
2426
2427
2428 local setmacro = tokens . setters . macro
2429
2430 function constructors . currentfonthasfeature ( n )
2431 local f = fontdata [ currentfont ( ) ]
2432 if not f then return end f = f . shared
2433 if not f then return end f = f . rawdata
2434 if not f then return end f = f . resources
2435 if not f then return end f = f . features
2436 return f and ( f . gpos [ n ] or f . gsub [ n ] )
2437 end
2438
2439 local ctx_doifelse = commands . doifelse
2440 local ctx_doif = commands . doif
2441
2442 implement {
2443 name = " doifelsecurrentfonthasfeature " ,
2444 actions = { constructors . currentfonthasfeature , ctx_doifelse } ,
2445 arguments = " string "
2446 }
2447
2448 local f_strip = formatters [ " %0.2fpt " ]
2449 local stripper = lpeg . patterns . stripzeros
2450
2451 local cache = { }
2452
2453 local hows = {
2454 [ " + " ] = " add " ,
2455 [ " - " ] = " subtract " ,
2456 [ " = " ] = " replace " ,
2457 }
2458
2459 local function setfeature ( how , parent , name , font )
2460 if not how or how = = 0 then
2461 if trace_features and texgetglyphdata ( ) ~ = 0 then
2462 report_cummulative ( " font %!font:name!, reset " , fontdata [ font or true ] )
2463 end
2464 texsetglyphdata ( 0 )
2465 elseif how = = true or how = = 1 then
2466 local hash = " feature > " . . parent
2467 local done = cache [ hash ]
2468 if trace_features and done then
2469 report_cummulative ( " font %!font:name!, revive %a : %!font:features! " , fontdata [ font or true ] , parent , setups [ numbers [ done ] ] )
2470 end
2471 texsetglyphdata ( done or 0 )
2472 else
2473 local full = parent . . how . . name
2474 local hash = " feature > " . . full
2475 local done = cache [ hash ]
2476 if not done then
2477 local n = setups [ full ]
2478 if n then
2479
2480 else
2481 n = mergecontextfeatures ( parent , name , how , full )
2482 end
2483 done = registercontextfeature ( hash , full , how )
2484 cache [ hash ] = done
2485 if trace_features then
2486 report_cummulative ( " font %!font:name!, %s %a : %!font:features! " , fontdata [ font or true ] , hows [ how ] , full , setups [ numbers [ done ] ] )
2487 end
2488 end
2489 texsetglyphdata ( done )
2490 end
2491 end
2492
2493 local function resetfeature ( )
2494 if trace_features and texgetglyphdata ( ) ~ = 0 then
2495 report_cummulative ( " font %!font:name!, reset " , fontdata [ true ] )
2496 end
2497 texsetglyphdata ( 0 )
2498 end
2499
2500 local function setfontfeature ( tag )
2501 texsetglyphdata ( contextnumber ( tag ) )
2502 end
2503
2504 local function resetfontfeature ( )
2505 texsetglyphdata ( 0 )
2506 end
2507
2508 implement {
2509 name = " nbfs " ,
2510 arguments = " dimen " ,
2511 actions = function ( d )
2512 context ( lpegmatch ( stripper , f_strip ( d / 65536 ) ) )
2513 end
2514 }
2515
2516 implement {
2517 name = " featureattribute " ,
2518 arguments = " string " ,
2519 actions = { contextnumber , context }
2520 }
2521
2522 implement {
2523 name = " setfontfeature " ,
2524 arguments = " string " ,
2525 actions = setfontfeature ,
2526 }
2527
2528 implement {
2529 name = " resetfontfeature " ,
2530
2531 actions = resetfontfeature ,
2532 }
2533
2534 implement {
2535 name = " setfontofid " ,
2536 arguments = " integer " ,
2537 actions = function ( id )
2538 ctx_getvalue ( csnames [ id ] )
2539 end
2540 }
2541
2542 implement {
2543 name = " definefontfeature " ,
2544 arguments = " 3 strings " ,
2545 actions = presetcontext ,
2546 }
2547
2548 implement {
2549 name = " doifelsefontfeature " ,
2550 arguments = " string " ,
2551 actions = function ( name ) ctx_doifelse ( contextnumber ( name ) > 1 ) end ,
2552 }
2553
2554 implement {
2555 name = " doifunknownfontfeature " ,
2556 arguments = " string " ,
2557 actions = function ( name ) ctx_doif ( contextnumber ( name ) = = 0 ) end ,
2558 }
2559
2560 implement {
2561 name = " adaptfontfeature " ,
2562 arguments = " 2 strings " ,
2563 actions = adaptcontext
2564 }
2565
2566 local function registerlanguagefeatures ( )
2567 local specifications = languages . data . specifications
2568 for i = 1 , # specifications do
2569 local specification = specifications [ i ]
2570 local language = specification . opentype
2571 if language then
2572 local script = specification . opentypescript or specification . script
2573 if script then
2574 local context = specification . context
2575 if type ( context ) = = " table " then
2576 for i = 1 , # context do
2577 definecontext ( context [ i ] , { language = language , script = script } )
2578 end
2579 elseif type ( context ) = = " string " then
2580 definecontext ( context , { language = language , script = script } )
2581 end
2582 end
2583 end
2584 end
2585 end
2586
2587 constructors . setfeature = setfeature
2588 constructors . resetfeature = resetfeature
2589
2590 implement { name = " resetfeature " , actions = resetfeature }
2591 implement { name = " addfeature " , actions = setfeature , arguments = { " '+' " , " string " , " string " } }
2592 implement { name = " subtractfeature " , actions = setfeature , arguments = { " '-' " , " string " , " string " } }
2593 implement { name = " replacefeature " , actions = setfeature , arguments = { " '=' " , " string " , " string " } }
2594 implement { name = " revivefeature " , actions = setfeature , arguments = { true , " string " } }
2595
2596 implement {
2597 name = " featurelist " ,
2598 actions = { fonts . specifiers . contexttostring , context } ,
2599 arguments = { " string " , " 'otf' " , " string " , " 'yes' " , " 'no' " , true }
2600 }
2601
2602 implement {
2603 name = " registerlanguagefeatures " ,
2604 actions = registerlanguagefeatures ,
2605 }
2606
2607end
2608
2609
2610
2611
2612
2613do
2614
2615 local report = logs . reporter ( " otf " , " variants " )
2616
2617 local function replace ( tfmdata , feature , value )
2618 local characters = tfmdata . characters
2619 local variants = tfmdata . resources . variants
2620 if variants then
2621 local t = { }
2622 for k , v in sortedhash ( variants ) do
2623 t [ # t + 1 ] = formatters [ " 0x%X (%i) " ] ( k , k )
2624 end
2625 value = tonumber ( value ) or 0xFE00
2626 report ( " fontname : %s " , tfmdata . properties . fontname )
2627 report ( " available: % t " , t )
2628 local v = variants [ value ]
2629 if v then
2630 report ( " using : %X (%i) " , value , value )
2631 for k , v in next , v do
2632 local c = characters [ v ]
2633 if c then
2634 characters [ k ] = c
2635 end
2636 end
2637 else
2638 report ( " unknown : %X (%i) " , value , value )
2639 end
2640 end
2641 end
2642
2643 registerotffeature {
2644 name = ' variant ' ,
2645 description = ' unicode variant ' ,
2646 manipulators = {
2647 base = replace ,
2648 node = replace ,
2649 }
2650 }
2651
2652end
2653
2654
2655
2656
2657
2658do
2659
2660 local trace_analyzing = false trackers . register ( " otf.analyzing " , function ( v ) trace_analyzing = v end )
2661
2662 local analyzers = fonts . analyzers
2663 local methods = analyzers . methods
2664
2665 local unsetvalue = attributes . unsetvalue
2666
2667 local a_color = attributes . private ( ' color ' )
2668 local a_colormodel = attributes . private ( ' colormodel ' )
2669 local m_color = attributes . list [ a_color ] or { }
2670
2671 local glyph_code = nodes . nodecodes . glyph
2672
2673 local states = analyzers . states
2674
2675 local colornames = {
2676 [ states . init ] = " font:1 " ,
2677 [ states . medi ] = " font:2 " ,
2678 [ states . fina ] = " font:3 " ,
2679 [ states . isol ] = " font:4 " ,
2680 [ states . mark ] = " font:5 " ,
2681 [ states . rest ] = " font:6 " ,
2682 [ states . rphf ] = " font:1 " ,
2683 [ states . half ] = " font:2 " ,
2684 [ states . pref ] = " font:3 " ,
2685 [ states . blwf ] = " font:4 " ,
2686 [ states . pstf ] = " font:5 " ,
2687 }
2688
2689
2690
2691
2692 local function markstates ( head )
2693 if head then
2694 head = tonut ( head )
2695 local model = getattr ( head , a_colormodel ) or 1
2696 for glyph in nextchar , head do
2697 local a = getstate ( glyph )
2698 if a then
2699 local name = colornames [ a ]
2700 if name then
2701 local color = m_color [ name ]
2702 if color then
2703 setattr ( glyph , a_colormodel , model )
2704 setattr ( glyph , a_color , color )
2705 end
2706 end
2707 end
2708 end
2709 end
2710 end
2711
2712 local function analyzeprocessor ( head , font , attr )
2713 local tfmdata = fontdata [ font ]
2714 local script , language = otf . scriptandlanguage ( tfmdata , attr )
2715 local action = methods [ script ]
2716 if not action then
2717 return head , false
2718 end
2719 if type ( action ) = = " function " then
2720 local head , done = action ( head , font , attr )
2721 if done and trace_analyzing then
2722 markstates ( head )
2723 end
2724 return head , done
2725 end
2726 action = action [ language ]
2727 if action then
2728 local head , done = action ( head , font , attr )
2729 if done and trace_analyzing then
2730 markstates ( head )
2731 end
2732 return head , done
2733 else
2734 return head , false
2735 end
2736 end
2737
2738 registerotffeature {
2739 name = " analyze " ,
2740 processors = {
2741 node = analyzeprocessor ,
2742 }
2743 }
2744
2745
2746 function methods . nocolor ( head , font , attr )
2747 for n , c , f in nextchar , head do
2748 if not font or f = = font then
2749 setattr ( n , a_color , unsetvalue )
2750 end
2751 end
2752 return head , true
2753 end
2754
2755end
2756
2757
2758local function purefontname ( name )
2759 if type ( name ) = = " number " then
2760 name = getfontname ( name )
2761 end
2762 if type ( name ) = = " string " then
2763 return basename ( name )
2764 end
2765end
2766
2767implement {
2768 name = " purefontname " ,
2769 actions = { purefontname , context } ,
2770 arguments = " string " ,
2771}
2772
2773local sharedstorage = storage . shared
2774
2775local list = sharedstorage . bodyfontsizes or { }
2776local unknown = sharedstorage . unknownbodyfontsizes or { }
2777
2778sharedstorage . bodyfontsizes = list
2779sharedstorage . unknownbodyfontsizes = unknown
2780
2781implement {
2782 name = " registerbodyfontsize " ,
2783 arguments = " string " ,
2784 actions = function ( size )
2785 list [ size ] = true
2786 end
2787}
2788
2789interfaces . implement {
2790 name = " registerunknownbodysize " ,
2791 arguments = " string " ,
2792 actions = function ( size )
2793 if not unknown [ size ] then
2794 interfaces . showmessage ( " fonts " , 14 , size )
2795 end
2796 unknown [ size ] = true
2797 end ,
2798}
2799
2800implement {
2801 name = " getbodyfontsizes " ,
2802 arguments = " string " ,
2803 actions = function ( separator )
2804 context ( concat ( sortedkeys ( list ) , separator ) )
2805 end
2806}
2807
2808implement {
2809 name = " processbodyfontsizes " ,
2810 arguments = " string " ,
2811 actions = function ( command )
2812 local keys = sortedkeys ( list )
2813 if command then
2814 local action = context [ command ]
2815 for i = 1 , # keys do
2816 action ( keys [ i ] )
2817 end
2818 else
2819 context ( concat ( keys , " , " ) )
2820 end
2821 end
2822}
2823
2824implement {
2825 name = " cleanfontname " ,
2826 actions = { cleanname , context } ,
2827 arguments = " string "
2828}
2829
2830implement {
2831 name = " fontlookupinitialize " ,
2832 actions = names . lookup ,
2833 arguments = " string " ,
2834}
2835
2836implement {
2837 name = " fontlookupnoffound " ,
2838 actions = { names . noflookups , context } ,
2839}
2840
2841implement {
2842 name = " fontlookupgetkeyofindex " ,
2843 actions = { names . getlookupkey , context } ,
2844 arguments = { " string " , " integer " }
2845}
2846
2847implement {
2848 name = " fontlookupgetkey " ,
2849 actions = { names . getlookupkey , context } ,
2850 arguments = " string "
2851}
2852
2853
2854
2855function commands . showchardata ( n )
2856 local tfmdata = fontdata [ currentfont ( ) ]
2857 if tfmdata then
2858 if type ( n ) = = " string " then
2859 n = utfbyte ( n )
2860 end
2861 local chr = tfmdata . characters [ n ]
2862 if chr then
2863 report_status ( " %s @ %s => %U => %c => %s " , tfmdata . properties . fullname , tfmdata . parameters . size , n , n , serialize ( chr , false ) )
2864 end
2865 end
2866end
2867
2868function commands . showfontparameters ( tfmdata )
2869
2870 local tfmdata = tfmdata or fontdata [ currentfont ( ) ]
2871 if tfmdata then
2872 local parameters = tfmdata . parameters
2873 local mathparameters = tfmdata . mathparameters
2874 local properties = tfmdata . properties
2875 local hasparameters = parameters and next ( parameters )
2876 local hasmathparameters = mathparameters and next ( mathparameters )
2877 if hasparameters then
2878 report_status ( " %s @ %s => text parameters => %s " , properties . fullname , parameters . size , serialize ( parameters , false ) )
2879 end
2880 if hasmathparameters then
2881 report_status ( " %s @ %s => math parameters => %s " , properties . fullname , parameters . size , serialize ( mathparameters , false ) )
2882 end
2883 if not hasparameters and not hasmathparameters then
2884 report_status ( " %s @ %s => no text parameters and/or math parameters " , properties . fullname , parameters . size )
2885 end
2886 end
2887end
2888
2889implement {
2890 name = " currentdesignsize " ,
2891 actions = function ( )
2892 context ( parameters [ currentfont ( ) ] . designsize )
2893 end
2894}
2895
2896implement {
2897 name = " doifelsefontpresent " ,
2898 actions = { names . exists , commands . doifelse } ,
2899 arguments = " string "
2900}
2901
2902
2903
2904
2905constructors . privateslots = constructors . privateslots or { }
2906
2907storage . register ( " fonts/constructors/privateslots " , constructors . privateslots , " fonts.constructors.privateslots " )
2908
2909do
2910
2911 local privateslots = constructors . privateslots
2912 local lastprivateslot = 0xFD000
2913
2914 constructors . privateslots = setmetatableindex ( privateslots , function ( t , k )
2915 local v = lastprivateslot
2916 lastprivateslot = lastprivateslot + 1
2917 t [ k ] = v
2918 return v
2919 end )
2920
2921 implement {
2922 name = " getprivateglyphslot " ,
2923 actions = function ( name ) context ( privateslots [ name ] ) end ,
2924 arguments = " string " ,
2925 }
2926
2927end
2928
2929
2930
2931function helpers . getcoloredglyphs ( tfmdata )
2932 if type ( tfmdata ) = = " number " then
2933 tfmdata = fontdata [ tfmdata ]
2934 end
2935 if not tfmdata then
2936 tfmdata = fontdata [ true ]
2937 end
2938 local characters = tfmdata . characters
2939 local descriptions = tfmdata . descriptions
2940 local collected = { }
2941 for unicode , character in next , characters do
2942 local description = descriptions [ unicode ]
2943 if description and ( description . colors or character . svg ) then
2944 collected [ # collected + 1 ] = unicode
2945 end
2946 end
2947 table . sort ( collected )
2948 return collected
2949end
2950
2951
2952
2953statistics . register ( " body font sizes " , function ( )
2954 if next ( unknown ) then
2955 return formatters [ " defined: % t, undefined: % t " ] ( sortedkeys ( list ) , sortedkeys ( unknown ) )
2956 end
2957end )
2958
2959statistics . register ( " used fonts " , function ( )
2960 if trace_usage then
2961 local filename = file . nameonly ( environment . jobname ) . . " -fonts-usage.lua "
2962 if next ( fontdata ) then
2963 local files = { }
2964 local list = { }
2965 for id , tfmdata in sortedhash ( fontdata ) do
2966 local filename = tfmdata . properties . filename
2967 if filename then
2968 local filedata = files [ filename ]
2969 if filedata then
2970 filedata . instances = filedata . instances + 1
2971 else
2972 local rawdata = tfmdata . shared and tfmdata . shared . rawdata
2973 local metadata = rawdata and rawdata . metadata
2974 files [ filename ] = {
2975 instances = 1 ,
2976 filename = filename ,
2977 version = metadata and metadata . version ,
2978 size = rawdata and rawdata . size ,
2979 }
2980 end
2981 else
2982
2983 end
2984 end
2985 for k , v in sortedhash ( files ) do
2986 list [ # list + 1 ] = v
2987 end
2988 table . save ( filename , list )
2989 else
2990 os . remove ( filename )
2991 end
2992 end
2993end )
2994
2995
2996
2997do
2998
2999 local settings_to_array = utilities . parsers . settings_to_array
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022 implement {
3023 name = " definefontcolorpalette " ,
3024 arguments = " 2 strings " ,
3025 actions = function ( name , set )
3026 otf . registerpalette ( name , settings_to_array ( set ) )
3027 end
3028 }
3029
3030end
3031
3032do
3033
3034 local pattern = C ( ( 1 - S ( " * " ) ) ^ 1 )
3035
3036 implement {
3037 name = " truefontname " ,
3038 arguments = " string " ,
3039 actions = function ( s )
3040
3041 context ( lpegmatch ( pattern , s ) or s )
3042 end
3043 }
3044
3045end
3046
3047do
3048
3049 local function getinstancespec ( id )
3050 local data = fontdata [ id or true ]
3051 local shared = data . shared
3052 local resources = shared and shared . rawdata . resources
3053 if resources then
3054 local instancespec = data . properties . instance
3055 if instancespec then
3056 local variabledata = resources . variabledata
3057 if variabledata then
3058 local instances = variabledata . instances
3059 if instances then
3060 for i = 1 , # instances do
3061 local instance = instances [ i ]
3062 if cleanname ( instance . subfamily ) = = instancespec then
3063 local values = table . copy ( instance . values )
3064 local axis = variabledata . axis
3065 for i = 1 , # values do
3066 for j = 1 , # axis do
3067 if values [ i ] . axis = = axis [ j ] . tag then
3068 values [ i ] . name = axis [ j ] . name
3069 break
3070 end
3071 end
3072 end
3073 return values
3074 end
3075 end
3076 end
3077 end
3078 end
3079 end
3080 end
3081
3082 helpers . getinstancespec = getinstancespec
3083
3084 implement {
3085 name = " currentfontinstancespec " ,
3086 actions = function ( )
3087 local t = getinstancespec ( )
3088 if t then
3089 for i = 1 , # t do
3090 if i > 1 then
3091 context . space ( )
3092 end
3093 local ti = t [ i ]
3094 context ( " %s=%s " , ti . name , ti . value )
3095 end
3096 end
3097 end
3098 }
3099
3100end
3101
3102
3103
3104do
3105
3106 local identical = table . identical
3107 local copy = table . copy
3108 local fontdata = fonts . hashes . identifiers
3109 local addcharacters = font . addcharacters
3110
3111
3112
3113
3114
3115 local trace_adding = false
3116 local report_adding = logs . reporter ( " fonts " , " add characters " )
3117
3118 trackers . register ( " fonts.addcharacters " , function ( v ) trace_adding = v end )
3119
3120 if addcharacters then
3121
3122 function fonts . constructors . addcharacters ( id , list )
3123 local newchar = list . characters
3124 if newchar then
3125 local data = fontdata [ id ]
3126 local newfont = list . fonts
3127 local oldchar = data . characters
3128 local oldfont = data . fonts
3129 addcharacters ( id , {
3130 characters = newchar ,
3131 fonts = newfont ,
3132 nomath = not data . properties . hasmath ,
3133 } )
3134
3135
3136 if newfont then
3137 if oldfont then
3138 local oldn = # oldfont
3139 local newn = # newfont
3140 for n = 1 , newn do
3141 local ok = false
3142 local nf = newfont [ n ]
3143 for o = 1 , oldn do
3144 if identical ( nf , oldfont [ o ] ) then
3145 ok = true
3146 break
3147 end
3148 end
3149 if not ok then
3150 oldn = oldn + 1
3151 oldfont [ oldn ] = newfont [ i ]
3152 end
3153 end
3154 else
3155 data . fonts = newfont
3156 end
3157 end
3158
3159
3160 for u , c in next , newchar do
3161 if trace_adding then
3162 report_adding ( " adding character %U to font %!font:name! " , u , id )
3163 end
3164 oldchar [ u ] = c
3165 end
3166 end
3167 end
3168
3169 else
3170 function fonts . constructors . addcharacters ( id , list )
3171 report_adding ( " adding characters to %!font:name! is not yet supported " , id )
3172 end
3173 end
3174
3175 implement {
3176 name = " addfontpath " ,
3177 arguments = " string " ,
3178 actions = function ( list )
3179 names . addruntimepath ( settings_to_array ( list ) )
3180 end
3181 }
3182
3183end
3184
3185
3186
3187do
3188
3189 local getfontoffamily = font . getfontoffamily
3190 local new_glyph = nodes . pool . glyph
3191 local fontproperties = fonts . hashes . properties
3192
3193 local function getprivateslot ( id , name )
3194 if not name then
3195 name = id
3196 id = currentfont ( )
3197 end
3198 local properties = fontproperties [ id ]
3199 local privates = properties and properties . privates
3200 return privates and privates [ name ]
3201 end
3202
3203 local function getprivatenode ( tfmdata , name )
3204 if type ( tfmdata ) = = " number " then
3205 tfmdata = fontdata [ tfmdata ]
3206 end
3207 local properties = tfmdata . properties
3208 local font = properties . id
3209 local slot = getprivateslot ( font , name )
3210 if slot then
3211
3212 local char = tfmdata . characters [ slot ]
3213 local tonode = char . tonode
3214 if tonode then
3215 return tonode ( font , char )
3216 else
3217 return new_glyph ( font , slot )
3218 end
3219 end
3220 end
3221
3222 local function getprivatecharornode ( tfmdata , name )
3223 if type ( tfmdata ) = = " number " then
3224 tfmdata = fontdata [ tfmdata ]
3225 end
3226 local properties = tfmdata . properties
3227 local font = properties . id
3228 local slot = getprivateslot ( font , name )
3229 if slot then
3230
3231 local char = tfmdata . characters [ slot ]
3232 local tonode = char . tonode
3233 if tonode then
3234 return " node " , tonode ( tfmdata , char )
3235 else
3236 return " char " , slot
3237 end
3238 end
3239 end
3240
3241 helpers . getprivateslot = getprivateslot
3242 helpers . getprivatenode = getprivatenode
3243 helpers . getprivatecharornode = getprivatecharornode
3244
3245 implement {
3246 name = " getprivatechar " ,
3247 arguments = " string " ,
3248 actions = function ( name )
3249 local p = getprivateslot ( name )
3250 if p then
3251 context ( utfchar ( p ) )
3252 end
3253 end
3254 }
3255
3256 implement {
3257 name = " getprivatemathchar " ,
3258 arguments = " string " ,
3259 actions = function ( name )
3260 local p = getprivateslot ( getfontoffamily ( 0 ) , name )
3261 if p then
3262 context ( utfchar ( p ) )
3263 end
3264 end
3265 }
3266
3267 implement {
3268 name = " getprivateslot " ,
3269 arguments = " string " ,
3270 actions = function ( name )
3271 local p = getprivateslot ( name )
3272 if p then
3273 context ( p )
3274 end
3275 end
3276 }
3277
3278end
3279
3280
3281
3282function fonts . helpers . collectanchors ( tfmdata )
3283
3284 local resources = tfmdata . resources
3285
3286 if not resources or resources . anchors then
3287 return resources . anchors
3288 end
3289
3290 local anchors = { }
3291
3292 local function set ( unicode , target , class , anchor )
3293 local a = anchors [ unicode ]
3294 if not a then
3295 anchors [ unicode ] = { [ target ] = { anchor } }
3296 return
3297 end
3298 local t = a [ target ]
3299 if not t then
3300 a [ target ] = { anchor }
3301 return
3302 end
3303 local x = anchor [ 1 ]
3304 local y = anchor [ 2 ]
3305 for k , v in next , t do
3306 if v [ 1 ] = = x and v [ 2 ] = = y then
3307 return
3308 end
3309 end
3310 t [ # t + 1 ] = anchor
3311 end
3312
3313 local function getanchors ( steps , target )
3314 for i = 1 , # steps do
3315 local step = steps [ i ]
3316 local coverage = step . coverage
3317 for unicode , data in next , coverage do
3318 local class = data [ 1 ]
3319 local anchor = data [ 2 ]
3320 if anchor [ 1 ] ~ = 0 or anchor [ 2 ] ~ = 0 then
3321 set ( unicode , target , class , anchor )
3322 end
3323 end
3324 end
3325 end
3326
3327 local function getcursives ( steps )
3328 for i = 1 , # steps do
3329 local step = steps [ i ]
3330 local coverage = step . coverage
3331 for unicode , data in next , coverage do
3332 local class = data [ 1 ]
3333 local en = data [ 2 ]
3334 local ex = data [ 3 ]
3335 if en then
3336 set ( unicode , " entry " , class , en )
3337 end
3338 if ex then
3339 set ( unicode , " exit " , class , ex )
3340 end
3341 end
3342 end
3343 end
3344
3345 local function collect ( list )
3346 if list then
3347 for i = 1 , # list do
3348 local entry = list [ i ]
3349 local steps = entry . steps
3350 local kind = entry . type
3351 if kind = = " gpos_mark2mark " then
3352 getanchors ( steps , " mark " )
3353 elseif kind = = " gpos_mark2base " then
3354 getanchors ( steps , " base " )
3355 elseif kind = = " gpos_mark2ligature " then
3356 getanchors ( steps , " ligature " )
3357 elseif kind = = " gpos_cursive " then
3358 getcursives ( steps )
3359 end
3360 end
3361 end
3362 end
3363
3364 collect ( resources . sequences )
3365 collect ( resources . sublookups )
3366
3367 local function sorter ( a , b )
3368 if a [ 1 ] = = b [ 1 ] then
3369 return a [ 2 ] < b [ 2 ]
3370 else
3371 return a [ 1 ] < b [ 1 ]
3372 end
3373 end
3374
3375 for unicode , old in next , anchors do
3376 for target , list in next , old do
3377 sort ( list , sorter )
3378 end
3379 end
3380
3381 resources . anchors = anchors
3382
3383 return anchors
3384
3385end
3386
3387if CONTEXTLMTXMODE > 0 then
3388 fonts . constructors . addtounicode = false
3389 fonts . constructors . autocleanup = false
3390end
3391 |