1if not modules then modules = { } end modules [ ' font-one ' ] = {
2 version = 1 . 001 ,
3 optimize = true ,
4 comment = " companion to font-ini.mkiv " ,
5 author = " Hans Hagen, PRAGMA-ADE, Hasselt NL " ,
6 copyright = " PRAGMA ADE / ConTeXt Development Team " ,
7 license = " see context related readme files "
8}
9
10
22
23local fonts , logs , trackers , containers , resolvers = fonts , logs , trackers , containers , resolvers
24
25local next , type , tonumber , rawget = next , type , tonumber , rawget
26local match , gsub = string . match , string . gsub
27local abs = math . abs
28local P , S , R , Cmt , C , Ct , Cs , Carg = lpeg . P , lpeg . S , lpeg . R , lpeg . Cmt , lpeg . C , lpeg . Ct , lpeg . Cs , lpeg . Carg
29local lpegmatch , patterns = lpeg . match , lpeg . patterns
30local sortedhash = table . sortedhash
31
32local trace_features = false trackers . register ( " afm.features " , function ( v ) trace_features = v end )
33local trace_indexing = false trackers . register ( " afm.indexing " , function ( v ) trace_indexing = v end )
34local trace_loading = false trackers . register ( " afm.loading " , function ( v ) trace_loading = v end )
35local trace_defining = false trackers . register ( " fonts.defining " , function ( v ) trace_defining = v end )
36
37local report_afm = logs . reporter ( " fonts " , " afm loading " )
38
39local setmetatableindex = table . setmetatableindex
40local derivetable = table . derive
41
42local findbinfile = resolvers . findbinfile
43
44local privateoffset = fonts . constructors and fonts . constructors . privateoffset or 0xF0000
45
46local definers = fonts . definers
47local readers = fonts . readers
48local constructors = fonts . constructors
49
50local afm = constructors . handlers . afm
51local pfb = constructors . handlers . pfb
52local otf = fonts . handlers . otf
53
54local otfreaders = otf . readers
55local otfenhancers = otf . enhancers
56
57local afmfeatures = constructors . features . afm
58local registerafmfeature = afmfeatures . register
59
60local afmenhancers = constructors . enhancers . afm
61local registerafmenhancer = afmenhancers . register
62
63afm . version = 1 . 513
64afm . cache = containers . define ( " fonts " , " one " , afm . version , true )
65afm . autoprefixed = true
66
67afm . helpdata = { }
68afm . syncspace = true
69
70local overloads = fonts . mappings . overloads
71
72local applyruntimefixes = fonts . treatments and fonts . treatments . applyfixes
73
74
83
84function afm . load ( filename )
85 filename = resolvers . findfile ( filename , ' afm ' ) or " "
86 if filename ~ = " " and not fonts . names . ignoredfile ( filename ) then
87 local name = file . removesuffix ( file . basename ( filename ) )
88 local data = containers . read ( afm . cache , name )
89 local attr = lfs . attributes ( filename )
90 local size = attr and attr . size or 0
91 local time = attr and attr . modification or 0
92
93 local pfbfile = file . replacesuffix ( name , " pfb " )
94 local pfbname = resolvers . findfile ( pfbfile , " pfb " ) or " "
95 if pfbname = = " " then
96 pfbname = resolvers . findfile ( file . basename ( pfbfile ) , " pfb " ) or " "
97 end
98 local pfbsize = 0
99 local pfbtime = 0
100 if pfbname ~ = " " then
101 local attr = lfs . attributes ( pfbname )
102 pfbsize = attr . size or 0
103 pfbtime = attr . modification or 0
104 end
105 if not data or data . size ~ = size or data . time ~ = time or data . pfbsize ~ = pfbsize or data . pfbtime ~ = pfbtime then
106 report_afm ( " reading %a " , filename )
107 data = afm . readers . loadfont ( filename , pfbname )
108 if data then
109 afmenhancers . apply ( data , filename )
110
111 fonts . mappings . addtounicode ( data , filename )
112 otfreaders . stripredundant ( data )
113
114 otfreaders . pack ( data )
115 data . size = size
116 data . time = time
117 data . pfbsize = pfbsize
118 data . pfbtime = pfbtime
119 report_afm ( " saving %a in cache " , name )
120
121 data = containers . write ( afm . cache , name , data )
122 data = containers . read ( afm . cache , name )
123 end
124 end
125 if data then
126
127 otfreaders . unpack ( data )
128 otfreaders . expand ( data )
129 otfreaders . addunicodetable ( data )
130 otfenhancers . apply ( data , filename , data )
131 if applyruntimefixes then
132 applyruntimefixes ( filename , data )
133 end
134 end
135 return data
136 end
137end
138
139
140
141local uparser = fonts . mappings . makenameparser ( )
142
143local function enhance_unify_names ( data , filename )
144 local unicodevector = fonts . encodings . agl . unicodes
145 local unicodes = { }
146 local names = { }
147 local private = data . private or privateoffset
148 local descriptions = data . descriptions
149 for name , blob in sortedhash ( data . characters ) do
150 local code = unicodevector [ name ]
151 if not code then
152 code = lpegmatch ( uparser , name )
153 if type ( code ) ~ = " number " then
154 code = private
155 private = private + 1
156 report_afm ( " assigning private slot %U for unknown glyph name %a " , code , name )
157 end
158 end
159 local index = blob . index
160 unicodes [ name ] = code
161 names [ name ] = index
162 blob . name = name
163 descriptions [ code ] = {
164 boundingbox = blob . boundingbox ,
165 width = blob . width ,
166 kerns = blob . kerns ,
167 index = index ,
168 name = name ,
169 }
170 end
171 for unicode , description in next , descriptions do
172 local kerns = description . kerns
173 if kerns then
174 local krn = { }
175 for name , kern in next , kerns do
176 local unicode = unicodes [ name ]
177 if unicode then
178 krn [ unicode ] = kern
179 else
180
181 end
182 end
183 description . kerns = krn
184 end
185 end
186 data . characters = nil
187 data . private = private
188 local resources = data . resources
189 local filename = resources . filename or file . removesuffix ( file . basename ( filename ) )
190 resources . filename = resolvers . unresolve ( filename )
191 resources . unicodes = unicodes
192 resources . marks = { }
193
194end
195
196local everywhere = { [ " * " ] = { [ " * " ] = true } }
197local noflags = { false , false , false , false }
198
199local function enhance_normalize_features ( data )
200 local ligatures = setmetatableindex ( " table " )
201 local kerns = setmetatableindex ( " table " )
202 local extrakerns = setmetatableindex ( " table " )
203 for u , c in next , data . descriptions do
204 local l = c . ligatures
205 local k = c . kerns
206 local e = c . extrakerns
207 if l then
208 ligatures [ u ] = l
209 for u , v in next , l do
210 l [ u ] = { ligature = v }
211 end
212 c . ligatures = nil
213 end
214 if k then
215 kerns [ u ] = k
216 for u , v in next , k do
217 k [ u ] = v
218 end
219 c . kerns = nil
220 end
221 if e then
222 extrakerns [ u ] = e
223 for u , v in next , e do
224 e [ u ] = v
225 end
226 c . extrakerns = nil
227 end
228 end
229 local features = {
230 gpos = { } ,
231 gsub = { } ,
232 }
233 local sequences = {
234
235 }
236 if next ( ligatures ) then
237 features . gsub . liga = everywhere
238 data . properties . hasligatures = true
239 sequences [ # sequences + 1 ] = {
240 features = {
241 liga = everywhere ,
242 } ,
243 flags = noflags ,
244 name = " s_s_0 " ,
245 nofsteps = 1 ,
246 order = { " liga " } ,
247 type = " gsub_ligature " ,
248 steps = {
249 {
250 coverage = ligatures ,
251 } ,
252 } ,
253 }
254 end
255 if next ( kerns ) then
256 features . gpos . kern = everywhere
257 data . properties . haskerns = true
258 sequences [ # sequences + 1 ] = {
259 features = {
260 kern = everywhere ,
261 } ,
262 flags = noflags ,
263 name = " p_s_0 " ,
264 nofsteps = 1 ,
265 order = { " kern " } ,
266 type = " gpos_pair " ,
267 steps = {
268 {
269 format = " kern " ,
270 coverage = kerns ,
271 } ,
272 } ,
273 }
274 end
275 if next ( extrakerns ) then
276 features . gpos . extrakerns = everywhere
277 data . properties . haskerns = true
278 sequences [ # sequences + 1 ] = {
279 features = {
280 extrakerns = everywhere ,
281 } ,
282 flags = noflags ,
283 name = " p_s_1 " ,
284 nofsteps = 1 ,
285 order = { " extrakerns " } ,
286 type = " gpos_pair " ,
287 steps = {
288 {
289 format = " kern " ,
290 coverage = extrakerns ,
291 } ,
292 } ,
293 }
294 end
295
296 data . resources . features = features
297 data . resources . sequences = sequences
298end
299
300local function enhance_fix_names ( data )
301 for k , v in next , data . descriptions do
302 local n = v . name
303 local r = overloads [ n ]
304 if r then
305 local name = r . name
306 if trace_indexing then
307 report_afm ( " renaming characters %a to %a " , n , name )
308 end
309 v . name = name
310 v . unicode = r . unicode
311 end
312 end
313end
314
315
319
320local addthem = function ( rawdata , ligatures )
321 if ligatures then
322 local descriptions = rawdata . descriptions
323 local resources = rawdata . resources
324 local unicodes = resources . unicodes
325
326 for ligname , ligdata in next , ligatures do
327 local one = descriptions [ unicodes [ ligname ] ]
328 if one then
329 for _ , pair in next , ligdata do
330 local two = unicodes [ pair [ 1 ] ]
331 local three = unicodes [ pair [ 2 ] ]
332 if two and three then
333 local ol = one . ligatures
334 if ol then
335 if not ol [ two ] then
336 ol [ two ] = three
337 end
338 else
339 one . ligatures = { [ two ] = three }
340 end
341 end
342 end
343 end
344 end
345 end
346end
347
348local function enhance_add_ligatures ( rawdata )
349 addthem ( rawdata , afm . helpdata . ligatures )
350end
351
352
356
357
358
359
360
361
362
363
364local function enhance_add_extra_kerns ( rawdata )
365 local descriptions = rawdata . descriptions
366 local resources = rawdata . resources
367 local unicodes = resources . unicodes
368 local function do_it_left ( what )
369 if what then
370 for unicode , description in next , descriptions do
371 local kerns = description . kerns
372 if kerns then
373 local extrakerns
374 for complex , simple in next , what do
375 complex = unicodes [ complex ]
376 simple = unicodes [ simple ]
377 if complex and simple then
378 local ks = kerns [ simple ]
379 if ks and not kerns [ complex ] then
380 if extrakerns then
381 extrakerns [ complex ] = ks
382 else
383 extrakerns = { [ complex ] = ks }
384 end
385 end
386 end
387 end
388 if extrakerns then
389 description . extrakerns = extrakerns
390 end
391 end
392 end
393 end
394 end
395 local function do_it_copy ( what )
396 if what then
397 for complex , simple in next , what do
398 complex = unicodes [ complex ]
399 simple = unicodes [ simple ]
400 if complex and simple then
401 local complexdescription = descriptions [ complex ]
402 if complexdescription then
403 local simpledescription = descriptions [ complex ]
404 if simpledescription then
405 local extrakerns
406 local kerns = simpledescription . kerns
407 if kerns then
408 for unicode , kern in next , kerns do
409 if extrakerns then
410 extrakerns [ unicode ] = kern
411 else
412 extrakerns = { [ unicode ] = kern }
413 end
414 end
415 end
416 local extrakerns = simpledescription . extrakerns
417 if extrakerns then
418 for unicode , kern in next , extrakerns do
419 if extrakerns then
420 extrakerns [ unicode ] = kern
421 else
422 extrakerns = { [ unicode ] = kern }
423 end
424 end
425 end
426 if extrakerns then
427 complexdescription . extrakerns = extrakerns
428 end
429 end
430 end
431 end
432 end
433 end
434 end
435
436 do_it_left ( afm . helpdata . leftkerned )
437 do_it_left ( afm . helpdata . bothkerned )
438
439 do_it_copy ( afm . helpdata . bothkerned )
440 do_it_copy ( afm . helpdata . rightkerned )
441end
442
443
446
447local function adddimensions ( data )
448 if data then
449 for unicode , description in next , data . descriptions do
450 local bb = description . boundingbox
451 if bb then
452 local ht = bb [ 4 ]
453 local dp = - bb [ 2 ]
454 if ht = = 0 or ht < 0 then
455
456 else
457 description . height = ht
458 end
459 if dp = = 0 or dp < 0 then
460
461 else
462 description . depth = dp
463 end
464 end
465 end
466 end
467end
468
469local function copytotfm ( data )
470 if data and data . descriptions then
471 local metadata = data . metadata
472 local resources = data . resources
473 local properties = derivetable ( data . properties )
474 local descriptions = derivetable ( data . descriptions )
475 local goodies = derivetable ( data . goodies )
476 local characters = { }
477 local parameters = { }
478 local unicodes = resources . unicodes
479
480 for unicode , description in next , data . descriptions do
481 characters [ unicode ] = { }
482 end
483
484 local filename = constructors . checkedfilename ( resources )
485 local fontname = metadata . fontname or metadata . fullname
486 local fullname = metadata . fullname or metadata . fontname
487 local endash = 0x2013
488 local emdash = 0x2014
489 local space = 0x0020
490 local spacer = " space "
491 local spaceunits = 500
492
493 local monospaced = metadata . monospaced
494 local charwidth = metadata . charwidth
495 local italicangle = metadata . italicangle
496 local charxheight = metadata . xheight and metadata . xheight > 0 and metadata . xheight
497 properties . monospaced = monospaced
498 parameters . italicangle = italicangle
499 parameters . charwidth = charwidth
500 parameters . charxheight = charxheight
501
502 local d_endash = descriptions [ endash ]
503 local d_emdash = descriptions [ emdash ]
504 local d_space = descriptions [ space ]
505 if not d_space or d_space = = 0 then
506 d_space = d_endash
507 end
508 if d_space then
509 spaceunits , spacer = d_space . width or 0 , " space "
510 end
511 if properties . monospaced then
512 if spaceunits = = 0 and d_emdash then
513 spaceunits , spacer = d_emdash . width or 0 , " emdash "
514 end
515 else
516 if spaceunits = = 0 and d_endash then
517 spaceunits , spacer = d_emdash . width or 0 , " endash "
518 end
519 end
520 if spaceunits = = 0 and charwidth then
521 spaceunits , spacer = charwidth or 0 , " charwidth "
522 end
523 if spaceunits = = 0 then
524 spaceunits = tonumber ( spaceunits ) or 500
525 end
526 if spaceunits = = 0 then
527 spaceunits = 500
528 end
529
530 parameters . slant = 0
531 parameters . space = spaceunits
532 parameters . space_stretch = 500
533 parameters . space_shrink = 333
534 parameters . x_height = 400
535 parameters . quad = 1000
536
537 if italicangle and italicangle ~ = 0 then
538 parameters . italicangle = italicangle
539 parameters . italicfactor = math . cos ( math . rad ( 90 + italicangle ) )
540 parameters . slant = - math . tan ( italicangle * math . pi / 180 )
541 end
542 if monospaced then
543 parameters . space_stretch = 0
544 parameters . space_shrink = 0
545 elseif afm . syncspace then
546 parameters . space_stretch = spaceunits / 2
547 parameters . space_shrink = spaceunits / 3
548 end
549 parameters . extra_space = parameters . space_shrink
550 if charxheight then
551 parameters . x_height = charxheight
552 else
553
554 local x = 0x0078
555 if x then
556 local x = descriptions [ x ]
557 if x then
558 parameters . x_height = x . height
559 end
560 end
561
562 end
563
564 if metadata . sup then
565 local dummy = { 0 , 0 , 0 }
566 parameters [ 1 ] = metadata . designsize or 0
567 parameters [ 2 ] = metadata . checksum or 0
568 parameters [ 3 ] ,
569 parameters [ 4 ] ,
570 parameters [ 5 ] = unpack ( metadata . space or dummy )
571 parameters [ 6 ] = metadata . quad or 0
572 parameters [ 7 ] = metadata . extraspace or 0
573 parameters [ 8 ] ,
574 parameters [ 9 ] ,
575 parameters [ 10 ] = unpack ( metadata . num or dummy )
576 parameters [ 11 ] ,
577 parameters [ 12 ] = unpack ( metadata . denom or dummy )
578 parameters [ 13 ] ,
579 parameters [ 14 ] ,
580 parameters [ 15 ] = unpack ( metadata . sup or dummy )
581 parameters [ 16 ] ,
582 parameters [ 17 ] = unpack ( metadata . sub or dummy )
583 parameters [ 18 ] = metadata . supdrop or 0
584 parameters [ 19 ] = metadata . subdrop or 0
585 parameters [ 20 ] ,
586 parameters [ 21 ] = unpack ( metadata . delim or dummy )
587 parameters [ 22 ] = metadata . axisheight or 0
588 end
589
590 parameters . designsize = ( metadata . designsize or 10 ) * 65536
591 parameters . ascender = abs ( metadata . ascender or 0 )
592 parameters . descender = abs ( metadata . descender or 0 )
593 parameters . units = 1000
594
595 properties . spacer = spacer
596 properties . format = fonts . formats [ filename ] or " type1 "
597 properties . filename = filename
598 properties . fontname = fontname
599 properties . fullname = fullname
600 properties . psname = fullname
601 properties . name = filename or fullname or fontname
602 properties . private = properties . private or data . private or privateoffset
603
604if not CONTEXTLMTXMODE or CONTEXTLMTXMODE = = 0 then
605 properties . encodingbytes = 2
606end
607
608 if next ( characters ) then
609 return {
610 characters = characters ,
611 descriptions = descriptions ,
612 parameters = parameters ,
613 resources = resources ,
614 properties = properties ,
615 goodies = goodies ,
616 }
617 end
618 end
619 return nil
620end
621
622
627
628function afm . setfeatures ( tfmdata , features )
629 local okay = constructors . initializefeatures ( " afm " , tfmdata , features , trace_features , report_afm )
630 if okay then
631 return constructors . collectprocessors ( " afm " , tfmdata , features , trace_features , report_afm )
632 else
633 return { }
634 end
635end
636
637local function addtables ( data )
638 local resources = data . resources
639 local lookuptags = resources . lookuptags
640 local unicodes = resources . unicodes
641 if not lookuptags then
642 lookuptags = { }
643 resources . lookuptags = lookuptags
644 end
645 setmetatableindex ( lookuptags , function ( t , k )
646 local v = type ( k ) = = " number " and ( " lookup " . . k ) or k
647 t [ k ] = v
648 return v
649 end )
650 if not unicodes then
651 unicodes = { }
652 resources . unicodes = unicodes
653 setmetatableindex ( unicodes , function ( t , k )
654 setmetatableindex ( unicodes , nil )
655 for u , d in next , data . descriptions do
656 local n = d . name
657 if n then
658 t [ n ] = u
659 end
660 end
661 return rawget ( t , k )
662 end )
663 end
664 constructors . addcoreunicodes ( unicodes )
665end
666
667local function afmtotfm ( specification )
668 local afmname = specification . filename or specification . name
669 if specification . forced = = " afm " or specification . format = = " afm " then
670 if trace_loading then
671 report_afm ( " forcing afm format for %a " , afmname )
672 end
673 else
674 local tfmname = findbinfile ( afmname , " ofm " ) or " "
675 if tfmname ~ = " " then
676 if trace_loading then
677 report_afm ( " fallback from afm to tfm for %a " , afmname )
678 end
679 return
680 end
681 end
682 if afmname ~ = " " then
683
684 local features = constructors . checkedfeatures ( " afm " , specification . features . normal )
685 specification . features . normal = features
686 constructors . hashinstance ( specification , true )
687
688 specification = definers . resolve ( specification )
689 local cache_id = specification . hash
690 local tfmdata = containers . read ( constructors . cache , cache_id )
691 if not tfmdata then
692 local rawdata = afm . load ( afmname )
693 if rawdata and next ( rawdata ) then
694 addtables ( rawdata )
695 adddimensions ( rawdata )
696 tfmdata = copytotfm ( rawdata )
697 if tfmdata and next ( tfmdata ) then
698 local shared = tfmdata . shared
699 if not shared then
700 shared = { }
701 tfmdata . shared = shared
702 end
703 shared . rawdata = rawdata
704 shared . dynamics = { }
705 tfmdata . changed = { }
706 shared . features = features
707 shared . processes = afm . setfeatures ( tfmdata , features )
708 end
709 elseif trace_loading then
710 report_afm ( " no (valid) afm file found with name %a " , afmname )
711 end
712 tfmdata = containers . write ( constructors . cache , cache_id , tfmdata )
713 end
714 return tfmdata
715 end
716end
717
718
725
726local function read_from_afm ( specification )
727 local tfmdata = afmtotfm ( specification )
728 if tfmdata then
729 tfmdata . properties . name = specification . name
730 tfmdata . properties . id = specification . id
731 tfmdata = constructors . scale ( tfmdata , specification )
732 local allfeatures = tfmdata . shared . features or specification . features . normal
733 constructors . applymanipulators ( " afm " , tfmdata , allfeatures , trace_features , report_afm )
734 fonts . loggers . register ( tfmdata , ' afm ' , specification )
735 end
736 return tfmdata
737end
738
739
742
743registerafmfeature {
744 name = " mode " ,
745 description = " mode " ,
746 initializers = {
747 base = otf . modeinitializer ,
748 node = otf . modeinitializer ,
749 }
750}
751
752registerafmfeature {
753 name = " features " ,
754 description = " features " ,
755 default = true ,
756 initializers = {
757 node = otf . nodemodeinitializer ,
758 base = otf . basemodeinitializer ,
759 } ,
760 processors = {
761 node = otf . featuresprocessor ,
762 }
763}
764
765
766
767fonts . formats . afm = " type1 "
768fonts . formats . pfb = " type1 "
769
770local function check_afm ( specification , fullname )
771 local foundname = findbinfile ( fullname , ' afm ' ) or " "
772 if foundname = = " " then
773 foundname = fonts . names . getfilename ( fullname , " afm " ) or " "
774 end
775 if fullname and foundname = = " " and afm . autoprefixed then
776 local encoding , shortname = match ( fullname , " ^(.-)%-(.*)$ " )
777 if encoding and shortname and fonts . encodings . known [ encoding ] then
778 shortname = findbinfile ( shortname , ' afm ' ) or " "
779 if shortname ~ = " " then
780 foundname = shortname
781 if trace_defining then
782 report_afm ( " stripping encoding prefix from filename %a " , afmname )
783 end
784 end
785 end
786 end
787 if foundname ~ = " " then
788 specification . filename = foundname
789 specification . format = " afm "
790 return read_from_afm ( specification )
791 end
792end
793
794function readers . afm ( specification , method )
795 local fullname = specification . filename or " "
796 local tfmdata = nil
797 if fullname = = " " then
798 local forced = specification . forced or " "
799 if forced ~ = " " then
800 tfmdata = check_afm ( specification , specification . name . . " . " . . forced )
801 end
802 if not tfmdata then
803 local check_tfm = readers . check_tfm
804 method = ( check_tfm and ( method or definers . method or " afm or tfm " ) ) or " afm "
805 if method = = " tfm " then
806 tfmdata = check_tfm ( specification , specification . name )
807 elseif method = = " afm " then
808 tfmdata = check_afm ( specification , specification . name )
809 elseif method = = " tfm or afm " then
810 tfmdata = check_tfm ( specification , specification . name ) or check_afm ( specification , specification . name )
811 else
812 tfmdata = check_afm ( specification , specification . name ) or check_tfm ( specification , specification . name )
813 end
814 end
815 else
816 tfmdata = check_afm ( specification , fullname )
817 end
818 return tfmdata
819end
820
821function readers . pfb ( specification , method )
822 local original = specification . specification
823 if trace_defining then
824 report_afm ( " using afm reader for %a " , original )
825 end
826 specification . forced = " afm "
827 local function swap ( name )
828 local value = specification [ swap ]
829 if value then
830 specification [ swap ] = gsub ( " %.pfb " , " .afm " , 1 )
831 end
832 end
833 swap ( " filename " )
834 swap ( " fullname " )
835 swap ( " forcedname " )
836 swap ( " specification " )
837 return readers . afm ( specification , method )
838end
839
840
841
842registerafmenhancer ( " unify names " , enhance_unify_names )
843registerafmenhancer ( " add ligatures " , enhance_add_ligatures )
844registerafmenhancer ( " add extra kerns " , enhance_add_extra_kerns )
845registerafmenhancer ( " normalize features " , enhance_normalize_features )
846registerafmenhancer ( " check extra features " , otfenhancers . enhance )
847registerafmenhancer ( " fix names " , enhance_fix_names )
848 |