1if not modules then modules = { } end modules ['font-con'] = {
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
11local next, tostring, tonumber, rawget = next, tostring, tonumber, rawget
12local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find
13local sort, insert, concat = table.sort, table.insert, table.concat
14local sortedkeys, sortedhash, serialize, fastcopy = table.sortedkeys, table.sortedhash, table.serialize, table.fastcopy
15local derivetable = table.derive
16local ioflush = io.flush
17local round = math.round
18local setmetatable, getmetatable, rawget, rawset = setmetatable, getmetatable, rawget, rawset
19
20local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
21local trace_scaling = false trackers.register("fonts.scaling", function(v) trace_scaling = v end)
22
23local report_defining = logs.reporter("fonts","defining")
24
25
26
27
28
29local fonts = fonts
30local constructors = fonts.constructors or { }
31fonts.constructors = constructors
32local handlers = fonts.handlers or { }
33fonts.handlers = handlers
34
35local allocate = utilities.storage.allocate
36local setmetatableindex = table.setmetatableindex
37
38
39
40constructors.version = 1.01
41constructors.cache = containers.define("fonts", "constructors", constructors.version, false)
42
43constructors.privateoffset = fonts.privateoffsets.textbase or 0xF0000
44
45
46
47local designsizes = allocate()
48constructors.designsizes = designsizes
49local loadedfonts = allocate()
50constructors.loadedfonts = loadedfonts
51
52
53
54
55
56
57
58local factors = {
59 pt = 65536.0,
60 bp = 65781.8,
61}
62
63function constructors.setfactor(f)
64 constructors.factor = factors[f or 'pt'] or factors.pt
65end
66
67constructors.setfactor()
68
69function constructors.scaled(scaledpoints, designsize)
70 if scaledpoints < 0 then
71 local factor = constructors.factor
72 if designsize then
73 if designsize > factor then
74 return (- scaledpoints/1000) * designsize
75 else
76 return (- scaledpoints/1000) * designsize * factor
77 end
78 else
79 return (- scaledpoints/1000) * 10 * factor
80 end
81 else
82 return scaledpoints
83 end
84end
85
86function constructors.getprivate(tfmdata)
87 local properties = tfmdata.properties
88 local private = properties.private
89 properties.private = private + 1
90 return private
91end
92
93function constructors.setmathparameter(tfmdata,name,value)
94 local m = tfmdata.mathparameters
95 local c = tfmdata.MathConstants
96 if m then
97 m[name] = value
98 end
99 if c and c ~= m then
100 c[name] = value
101 end
102end
103
104function constructors.getmathparameter(tfmdata,name)
105 local p = tfmdata.mathparameters or tfmdata.MathConstants
106 if p then
107 return p[name]
108 end
109end
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135function constructors.calculatescale(tfmdata,scaledpoints)
136
137end
138
139function constructors.assignmathparameters(target,original)
140
141end
142
143function constructors.beforecopyingcharacters(target,original)
144
145end
146
147function constructors.aftercopyingcharacters(target,original)
148
149end
150
151function constructors.beforepassingfonttotex(target,tfmdata)
152
153end
154
155function constructors.trytosharefont(target,tfmdata)
156
157end
158
159local synonyms = {
160 exheight = "xheight",
161 xheight = "xheight",
162 ex = "xheight",
163 emwidth = "quad",
164 em = "quad",
165 spacestretch = "spacestretch",
166 stretch = "spacestretch",
167 spaceshrink = "spaceshrink",
168 shrink = "spaceshrink",
169 extraspace = "extraspace",
170 xspace = "extraspace",
171 slantperpoint = "slant",
172}
173
174function constructors.enhanceparameters(parameters)
175 local mt = getmetatable(parameters)
176 local getter = function(t,k)
177 if not k then
178 return nil
179 end
180 local s = synonyms[k]
181 if s then
182 return rawget(t,s) or (mt and mt[s]) or nil
183 end
184 if k == "spacing" then
185 return {
186 width = t.space,
187 stretch = t.spacestretch,
188 shrink = t.spaceshrink,
189 extra = t.extraspace,
190 }
191 end
192 return mt and mt[k] or nil
193 end
194 local setter = function(t,k,v)
195 if not k then
196 return 0
197 end
198 local s = synonyms[k]
199 if s then
200 rawset(t,s,v)
201 elseif k == "spacing" then
202 if type(v) == "table" then
203 rawset(t,"space",v.width or 0)
204 rawset(t,"spacestretch",v.stretch or 0)
205 rawset(t,"spaceshrink",v.shrink or 0)
206 rawset(t,"extraspace",v.extra or 0)
207 end
208 else
209 rawset(t,k,v)
210 end
211 end
212 setmetatable(parameters, {
213 __index = getter,
214 __newindex = setter,
215 })
216end
217
218local function mathkerns(v,vdelta)
219 local k = { }
220 for i=1,#v do
221 local entry = v[i]
222 local height = entry.height
223 local kern = entry.kern
224 k[i] = {
225 height = height and vdelta*height or 0,
226 kern = kern and vdelta*kern or 0,
227 }
228 end
229 return k
230end
231
232local psfake = 0
233
234local function fixedpsname(psname,fallback)
235 local usedname = psname
236 if psname and psname ~= "" then
237 if find(psname," ",1,true) then
238 usedname = gsub(psname,"[%s]+","-")
239 else
240
241 end
242 elseif not fallback or fallback == "" then
243 psfake = psfake + 1
244 psname = "fakename-" .. psfake
245 else
246
247 psname = fallback
248 usedname = gsub(psname,"[^a-zA-Z0-9]+","-")
249 end
250 return usedname, psname ~= usedname
251end
252
253local function scaleparts(parts,delta)
254 local t = { }
255 for i=1,#parts do
256 local p = parts[i]
257 local s = p["start"] or 0
258 local e = p["end"] or 0
259 local a = p["advance"] or 0
260 t[i] = {
261 ["start"] = s == 0 and 0 or s * delta,
262 ["end"] = e == 0 and 0 or e * delta,
263 ["advance"] = a == 0 and 0 or a * delta,
264 ["extender"] = p["extender"],
265 ["glyph"] = p["glyph"],
266 }
267 end
268 return t
269end
270
271local abovebaseline_tag <const> = tex.charactertagcodes.abovebaseline
272local belowbaseline_tag <const> = tex.charactertagcodes.belowbaseline
273
274local textcontrolcodes = tex.textcontrolcodes
275local collapse_hyphens <const> = textcontrolcodes.collapsehyphens
276local base_ligaturing <const> = textcontrolcodes.baseligaturing
277local base_kerning <const> = textcontrolcodes.basekerning
278local none_protected <const> = textcontrolcodes.noneprotected
279local has_italics <const> = textcontrolcodes.hasitalics
280local auto_italics <const> = textcontrolcodes.autoitalics
281
282function constructors.scale(tfmdata,specification)
283 local target = { }
284
285 if tonumber(specification) then
286 specification = { size = specification }
287 end
288 target.specification = specification
289
290 local scalecommands = fonts.helpers.scalecommands
291
292 local scaledpoints = specification.size
293 local relativeid = specification.relativeid
294
295 local properties = tfmdata.properties or { }
296 local goodies = tfmdata.goodies or { }
297 local resources = tfmdata.resources or { }
298 local descriptions = tfmdata.descriptions or { }
299 local characters = tfmdata.characters or { }
300 local changed = tfmdata.changed or { }
301 local shared = tfmdata.shared or { }
302 local parameters = tfmdata.parameters or { }
303 local mathparameters = tfmdata.mathparameters or { }
304
305 local targetcharacters = { }
306 local targetdescriptions = derivetable(descriptions)
307 local targetparameters = derivetable(parameters)
308 local targetproperties = derivetable(properties)
309 local targetgoodies = goodies
310 target.characters = targetcharacters
311 target.descriptions = targetdescriptions
312 target.parameters = targetparameters
313
314 target.properties = targetproperties
315 target.goodies = targetgoodies
316 target.shared = shared
317 target.resources = resources
318 target.unscaled = tfmdata
319
320
321
322
323
324 local mathsize = tonumber(specification.mathsize) or 0
325 local textsize = tonumber(specification.textsize) or scaledpoints
326
327 local extrafactor = tonumber(specification.factor ) or 1
328
329
330
331
332
333
334
335
336
337 targetparameters.mathsize = mathsize
338 targetparameters.textsize = textsize
339
340 targetparameters.extrafactor = extrafactor
341
342 local defaultwidth = resources.defaultwidth or 0
343 local defaultheight = resources.defaultheight or 0
344 local defaultdepth = resources.defaultdepth or 0
345 local units = parameters.units or 1000
346
347
348
349
350
351
352
353
354
355 targetproperties.language = properties.language or "dflt"
356 targetproperties.script = properties.script or "dflt"
357 targetproperties.mode = properties.mode or "base"
358 targetproperties.method = properties.method
359
360 local askedscaledpoints = scaledpoints
361 local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints,nil,specification)
362
363
364
365
366 local hdelta = delta
367 local vdelta = delta
368
369 target.designsize = parameters.designsize
370 target.units = units
371
372 target.size = scaledpoints
373
374 target.subfont = properties.subfont
375 target.cidinfo = properties.cidinfo
376 target.format = properties.format
377
378 local original = properties.original or tfmdata.original
379 local fontname = properties.fontname or tfmdata.fontname
380 local fullname = properties.fullname or tfmdata.fullname
381 local filename = properties.filename or tfmdata.filename
382 local psname = properties.psname or tfmdata.psname
383 local name = properties.name or tfmdata.name
384
385
386
387
388 local psname, psfixed = fixedpsname(psname,fontname or fullname or file.nameonly(filename))
389
390 target.original = original
391 target.fontname = fontname
392 target.fullname = fullname
393 target.filename = filename
394 target.psname = psname
395 target.name = name
396
397 properties.fontname = fontname
398 properties.fullname = fullname
399 properties.filename = filename
400 properties.psname = psname
401 properties.name = name
402
403 local expansion = parameters.expansion
404 if expansion then
405 target.stretch = expansion.stretch
406 target.shrink = expansion.shrink
407 target.step = expansion.step
408 end
409
410
411 local effect = properties.effect
412 if effect then
413 local slant = effect.slant or 0
414 if slant ~= 0 then
415 target.slant = slant * 1000
416
417 else
418 target.slant = 0
419 end
420 local extend = effect.extend or 1
421 if extend ~= 0 and extend ~= 1 then
422 hdelta = hdelta * extend
423 target.extend = extend * 1000
424
425 else
426 target.extend = 1000
427 end
428 local squeeze = effect.squeeze or 1
429 if squeeze ~= 0 and squeeze ~= 1 then
430 vdelta = vdelta * squeeze
431 target.squeeze = squeeze * 1000
432
433 else
434 target.squeeze = 1000
435 end
436 local mode = effect.mode or 0
437 if mode ~= 0 then
438 target.mode = mode
439 end
440
441 local width = effect.width or 0
442 if width ~= 0 then
443 target.width = width * 1000
444 end
445 end
446
447 targetproperties.effect = effect
448 targetparameters.factor = delta
449 targetparameters.hfactor = hdelta
450 targetparameters.vfactor = vdelta
451 targetparameters.size = scaledpoints
452 targetparameters.units = units
453 targetparameters.scaledpoints = askedscaledpoints
454 targetparameters.mode = mode
455 targetparameters.width = width
456
457 local hasquality = parameters.expansion or parameters.protrusion
458 local hasitalics = properties.hasitalics
459 local useditalicangle = properties.useditalicangle or properties.italicangle or 0
460 local usedslant = properties.usedslant or properties.usedslant or 0
461
462 local haskerns = properties.haskerns or properties.mode == "base"
463 local hasligatures = properties.hasligatures or properties.mode == "base"
464 local writingmode = properties.writingmode or "horizontal"
465
466 targetparameters.useditalicangle = useditalicangle
467 targetproperties.useditalicangle = useditalicangle
468 targetparameters.usedslant = usedslant
469 targetproperties.usedslant = usedslant
470
471 if changed and not next(changed) then
472 changed = false
473 end
474
475 target.writingmode = writingmode == "vertical" and "vertical" or "horizontal"
476
477 target.postprocessors = tfmdata.postprocessors
478
479 local targetslant = (parameters.slant or parameters[1] or 0) * factors.pt
480 local targetspace = (parameters.space or parameters[2] or 0) * hdelta
481 local targetspacestretch = (parameters.spacestretch or parameters[3] or 0) * hdelta
482 local targetspaceshrink = (parameters.spaceshrink or parameters[4] or 0) * hdelta
483 local targetxheight = (parameters.xheight or parameters[5] or 0) * vdelta
484 local targetquad = (parameters.quad or parameters[6] or 0) * hdelta
485 local targetextraspace = (parameters.extraspace or parameters[7] or 0) * hdelta
486
487 targetparameters.expansion = parameters.expansion
488 targetparameters.protrusion = parameters.protrusion
489
490 targetparameters.slant = targetslant
491 targetparameters.space = targetspace
492 targetparameters.spacestretch = targetspacestretch
493 targetparameters.spaceshrink = targetspaceshrink
494 targetparameters.xheight = targetxheight
495 targetparameters.quad = targetquad
496 targetparameters.extraspace = targetextraspace
497
498 local hshift = parameters.hshift
499 if hshift then
500 targetparameters.hshift = delta * hshift
501 end
502 local vshift = parameters.vshift
503 if vshift then
504 targetparameters.vshift = delta * vshift
505 end
506
507 local ascender = parameters.ascender if ascender then targetparameters.ascender = vdelta * ascender end
508 local descender = parameters.descender if descender then targetparameters.descender = vdelta * descender end
509 local capheight = parameters.capheight if capheight then targetparameters.capheight = vdelta * capheight end
510 local ascent = parameters.ascent if ascent then targetparameters.ascent = vdelta * ascent end
511 local descent = parameters.descent if descent then targetparameters.descent = vdelta * descent end
512
513 constructors.enhanceparameters(targetparameters)
514
515 local scaledwidth = defaultwidth * hdelta
516 local scaledheight = defaultheight * vdelta
517 local scaleddepth = defaultdepth * vdelta
518
519 local textcontrol = properties.textcontrol or 0
520 if targetproperties.mode == "base" then
521 textcontrol = textcontrol | base_ligaturing | base_kerning
522
523
524
525
526
527
528
529
530
531
532 elseif targetproperties.mode == "none" then
533 textcontrol = textcontrol | none_protected
534 end
535 if hasitalics then
536 textcontrol = textcontrol | has_italics
537 end
538 if useditalicangle ~= 0 or usedslant ~= 0 then
539 textcontrol = textcontrol | auto_italics
540 end
541 targetproperties.textcontrol = textcontrol
542 target.textcontrol = textcontrol
543
544 local hasmath = (properties.hasmath or next(mathparameters)) and true
545
546 if hasmath then
547 constructors.assignmathparameters(target,tfmdata)
548
549 properties.hasmath = true
550 target.nomath = false
551 target.MathConstants = target.mathparameters
552
553 local compactmath = properties.compactmath
554
555 targetproperties.compactmath = compactmath
556 target.compactmath = compactmath
557
558 local textscale = parameters.textscale
559 local scriptscale = parameters.scriptscale
560 local scriptscriptscale = parameters.scriptscriptscale
561
562 parameters.textscale = textscale
563 parameters.scriptscale = scriptscale
564 parameters.scriptscriptscale = scriptscriptscale
565
566 target.textscale = textscale
567 target.scriptscale = scriptscale
568 target.scriptscriptscale = scriptscriptscale
569
570 targetparameters.textscale = textscale
571 targetparameters.scriptscale = scriptscale
572 targetparameters.scriptscriptscale = scriptscriptscale
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589 local mathcontrol = properties.mathcontrol
590 targetproperties.mathcontrol = mathcontrol
591 target.mathcontrol = mathcontrol
592 else
593 properties.hasmath = false
594 target.nomath = true
595 target.mathparameters = nil
596 end
597
598 if trace_defining then
599 report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a",
600 name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta,
601 hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
602 end
603
604 constructors.beforecopyingcharacters(target,tfmdata)
605
606 local sharedkerns = { }
607
608
609
610 for unicode, character in next, characters do
611 local chr, description, index
612
613 if changed then
614 local c = changed[unicode]
615 if c and c ~= unicode then
616 local cc = changed[c]
617 if cc then
618 while cc do
619 c = cc
620 cc = changed[c]
621 end
622 end
623
624 if c then
625 description = descriptions[c] or descriptions[unicode] or character
626 character = characters[c] or character
627 index = description.index or c
628 else
629 description = descriptions[unicode] or character
630 index = description.index or unicode
631 end
632 else
633 description = descriptions[unicode] or character
634 index = description.index or unicode
635 end
636 else
637 description = descriptions[unicode] or character
638 index = description.index or unicode
639 end
640 local width = description.width
641 local height = description.height
642 local depth = description.depth
643 local isunicode = description.unicode
644 if width then width = hdelta*width else width = scaledwidth end
645 if height then height = vdelta*height else height = scaledheight end
646
647 if depth and depth ~= 0 then
648 depth = vdelta*depth
649 if isunicode then
650 chr = {
651 index = index,
652 height = height,
653 depth = depth,
654 width = width,
655 unicode = isunicode,
656 }
657 else
658 chr = {
659 index = index,
660 height = height,
661 depth = depth,
662 width = width,
663 }
664 end
665 else
666 if isunicode then
667 chr = {
668 index = index,
669 height = height,
670 width = width,
671 unicode = isunicode,
672 }
673 else
674 chr = {
675 index = index,
676 height = height,
677 width = width,
678 }
679 end
680 end
681 local bb = description.boundingbox
682 if bb then
683 if bb[2] > 0 then
684 chr.tag = abovebaseline_tag
685 elseif bb[4] < 0 then
686 chr.tag = belowbaseline_tag
687 end
688 end
689 if hasquality then
690
691 local ve = character.expansion
692 if ve then
693 chr.expansion = ve*1000
694 end
695 local vc = character.compression
696 if vc then
697 chr.compression = vc*1000
698 end
699 local vl = character.leftprotrusion
700 if vl then
701 chr.leftprotrusion = width*vl
702 end
703 local vr = character.rightprotrusion
704 if vr then
705 chr.rightprotrusion = width*vr
706 end
707 end
708
709 if hasmath then
710 local nxt = character.next
711 if nxt then
712 chr.next = nxt
713 end
714 local parts = character.parts
715 if parts then
716 local orientation = character.partsorientation or "vertical"
717 chr.parts = scaleparts(parts,orientation == "horizontal" and hdelta or vdelta)
718 chr.partsorientation = orientation
719 end
720 local vi = character.partsitalic
721 if vi and vi ~= 0 then
722 chr.partsitalic = vi*hdelta
723 end
724 local va = character.topanchor
725 if va and va ~= 0 then
726
727 chr.topanchor = va*hdelta
728 end
729 va = character.bottomanchor
730 if va and va ~= 0 then
731
732 chr.bottomanchor = va*hdelta
733 end
734
735 local mk = character.mathkerns
736 if mk then
737 local tr = mk.topright
738 local tl = mk.topleft
739 local br = mk.bottomright
740 local bl = mk.bottomleft
741 chr.mathkerns = {
742 topright = tr and mathkerns(tr,vdelta) or nil,
743 topleft = tl and mathkerns(tl,vdelta) or nil,
744 bottomright = br and mathkerns(br,vdelta) or nil,
745 bottomleft = bl and mathkerns(bl,vdelta) or nil,
746 }
747 end
748
749 if hasitalics then
750 local vi = character.italic
751 if vi and vi ~= 0 then
752 chr.italic = vi*hdelta
753 end
754 end
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783 local sm = character.smaller
784 if sm then
785 chr.smaller = sm
786 end
787
788
789
790
791 local fa = character.flataccent
792 if fa then
793 chr.flataccent = fa
794 end
795 elseif hasitalics then
796 local vi = character.italic
797 if vi and vi ~= 0 then
798 chr.italic = vi*hdelta
799 end
800 end
801 if haskerns then
802 local vk = character.kerns
803 if vk then
804 local s = sharedkerns[vk]
805 if not s then
806 s = { }
807 for k,v in next, vk do s[k] = v*hdelta end
808 sharedkerns[vk] = s
809 end
810 chr.kerns = s
811 end
812 end
813 if hasligatures then
814 local vl = character.ligatures
815 if vl then
816 if true then
817 chr.ligatures = vl
818 else
819 local tt = { }
820 for i, l in next, vl do
821 tt[i] = l
822 end
823 chr.ligatures = tt
824 end
825 end
826 end
827
828 local vc = character.commands
829 if vc then
830 chr.commands = scalecommands(vc,hdelta,vdelta)
831 end
832 local cb = character.callback
833 if cb then
834 chr.callback = cb
835 end
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850 targetcharacters[unicode] = chr
851 end
852
853 properties.setitalics = hasitalics
854
855
856
857 constructors.aftercopyingcharacters(target,tfmdata)
858
859 constructors.beforepassingfonttotex(target,tfmdata)
860
861 constructors.trytosharefont(target,tfmdata)
862
863
864
865 local vfonts = target.fonts
866 if not vfonts or #vfonts == 0 then
867 target.fonts = { { id = 0 } }
868 end
869
870 if trace_defining then
871 report_defining("font %a, size %N, delta %N",name,scaledpoints,delta)
872 end
873
874 return target
875end
876
877function constructors.finalize(tfmdata)
878 if tfmdata.properties and tfmdata.properties.finalized then
879 return
880 end
881
882 if not tfmdata.characters then
883 return nil
884 end
885
886 if not tfmdata.goodies then
887 tfmdata.goodies = { }
888 end
889
890 local parameters = tfmdata.parameters
891 local properties = tfmdata.properties
892 if not parameters then
893 return nil
894 end
895
896
897
898
899
900
901
902
903
904 if not parameters.size then
905 parameters.size = tfmdata.size
906 end
907
908
909
910
911
912 local effect = properties and properties.effect
913 if effect then
914 parameters.mode = effect.mode
915 parameters.width = effect.width
916 parameters.slantfactor = effect.slant
917 parameters.extendfactor = effect.extend
918 parameters.squeezefactor = effect.squeeze
919 else
920 parameters.mode = 0
921 parameters.width = 0
922 parameters.slantfactor = 0
923 parameters.extendfactor = 1
924 parameters.squeezefactor = 1
925 end
926 local designsize = parameters.designsize
927 if designsize then
928 parameters.minsize = tfmdata.minsize or designsize
929 parameters.maxsize = tfmdata.maxsize or designsize
930 else
931 designsize = factors.pt * 10
932 parameters.designsize = designsize
933 parameters.minsize = designsize
934 parameters.maxsize = designsize
935 end
936 parameters.minsize = tfmdata.minsize or parameters.designsize
937 parameters.maxsize = tfmdata.maxsize or parameters.designsize
938
939 if not parameters.units then
940 parameters.units = tfmdata.units or 1000
941 end
942
943 if not tfmdata.descriptions then
944 local descriptions = { }
945 setmetatableindex(descriptions, function(t,k) local v = { } t[k] = v return v end)
946 tfmdata.descriptions = descriptions
947 end
948
949 local properties = tfmdata.properties
950 if not properties then
951 properties = { }
952 tfmdata.properties = properties
953 end
954
955 properties.fontname = properties.fontname or tfmdata.fontname
956 properties.filename = properties.filename or tfmdata.filename
957 properties.fullname = properties.fullname or tfmdata.fullname
958 properties.name = properties.name or tfmdata.name
959 properties.psname = properties.psname or tfmdata.psname
960
961 properties.subfont = tfmdata.subfont or nil
962 properties.cidinfo = tfmdata.cidinfo or nil
963 properties.format = tfmdata.format or "type1"
964 properties.writingmode = tfmdata.writingmode or "horizontal"
965 properties.usedbitmap = tfmdata.usedbitmap
966
967 if not tfmdata.resources then
968 tfmdata.resources = { }
969 end
970 if not tfmdata.shared then
971 tfmdata.shared = { }
972 end
973
974
975
976
977 if not properties.hasmath then
978 properties.hasmath = not tfmdata.nomath
979 end
980
981 tfmdata.MathConstants = nil
982 tfmdata.postprocessors = nil
983
984 tfmdata.fontname = nil
985 tfmdata.filename = nil
986 tfmdata.fullname = nil
987 tfmdata.name = nil
988 tfmdata.psname = nil
989
990 tfmdata.subfont = nil
991 tfmdata.cidinfo = nil
992 tfmdata.format = nil
993 tfmdata.nomath = nil
994 tfmdata.designsize = nil
995
996 tfmdata.size = nil
997
998
999
1000 tfmdata.slant = nil
1001 tfmdata.extend = nil
1002 tfmdata.squeeze = nil
1003 tfmdata.mode = nil
1004 tfmdata.width = nil
1005 tfmdata.units = nil
1006
1007 tfmdata.cache = nil
1008
1009 properties.finalized = true
1010
1011 return tfmdata
1012end
1013
1014
1015
1016local hashmethods = { }
1017constructors.hashmethods = hashmethods
1018
1019function constructors.hashfeatures(specification)
1020 local features = specification.features
1021 if features then
1022 local t, n = { }, 0
1023 for category, list in sortedhash(features) do
1024 if next(list) then
1025 local hasher = hashmethods[category]
1026 if hasher then
1027 local hash = hasher(list)
1028 if hash then
1029 n = n + 1
1030 t[n] = category .. ":" .. hash
1031 end
1032 end
1033 end
1034 end
1035 if n > 0 then
1036 return concat(t," & ")
1037 end
1038 end
1039 return "unknown"
1040end
1041
1042hashmethods.normal = function(list)
1043 local s = { }
1044 local n = 0
1045 for k, v in next, list do
1046 if not k then
1047
1048 elseif k == "number" or k == "features" then
1049
1050 else
1051 n = n + 1
1052 if type(v) == "table" then
1053
1054 local t = { }
1055 local m = 0
1056 for k, v in next, v do
1057 m = m + 1
1058 t[m] = k .. '=' .. tostring(v)
1059 end
1060 sort(t)
1061 s[n] = k .. '={' .. concat(t,",") .. "}"
1062 else
1063 s[n] = k .. '=' .. tostring(v)
1064 end
1065 end
1066 end
1067 if n > 0 then
1068 sort(s)
1069 return concat(s,"+")
1070 end
1071end
1072
1073
1074
1075
1076
1077
1078
1079function constructors.hashinstance(specification,force)
1080
1081end
1082
1083function constructors.setname(tfmdata,specification)
1084
1085end
1086
1087function constructors.checkedfilename(data)
1088 local foundfilename = data.foundfilename
1089 if not foundfilename then
1090 local askedfilename = data.filename or ""
1091 if askedfilename ~= "" then
1092 askedfilename = resolvers.resolve(askedfilename)
1093 foundfilename = resolvers.findbinfile(askedfilename,"") or ""
1094 if foundfilename == "" then
1095 report_defining("source file %a is not found",askedfilename)
1096 foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or ""
1097 if foundfilename ~= "" then
1098 report_defining("using source file %a due to cache mismatch",foundfilename)
1099 end
1100 end
1101 end
1102 data.foundfilename = foundfilename
1103 end
1104 return foundfilename
1105end
1106
1107local formats = allocate()
1108fonts.formats = formats
1109
1110setmetatableindex(formats, function(t,k)
1111 local l = lower(k)
1112 if rawget(t,k) then
1113 t[k] = l
1114 return l
1115 end
1116 return rawget(t,file.suffix(l))
1117end)
1118
1119do
1120
1121 local function setindeed(mode,source,target,group,name,position)
1122 local action = source[mode]
1123 if not action then
1124 return
1125 end
1126 local t = target[mode]
1127 if not t then
1128 report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
1129 os.exit()
1130 elseif position then
1131
1132 insert(t, position, { name = name, action = action })
1133 else
1134 for i=1,#t do
1135 local ti = t[i]
1136 if ti.name == name then
1137 ti.action = action
1138 return
1139 end
1140 end
1141 insert(t, { name = name, action = action })
1142 end
1143 end
1144
1145 local function set(group,name,target,source)
1146 target = target[group]
1147 if not target then
1148 report_defining("fatal target error in setting feature %a, group %a",name,group)
1149 os.exit()
1150 end
1151 local source = source[group]
1152 if not source then
1153 report_defining("fatal source error in setting feature %a, group %a",name,group)
1154 os.exit()
1155 end
1156 local position = source.position
1157 setindeed("node",source,target,group,name,position)
1158 setindeed("base",source,target,group,name,position)
1159 setindeed("none",source,target,group,name,position)
1160 setindeed("plug",source,target,group,name,position)
1161 end
1162
1163 local function register(where,specification)
1164 local name = specification.name
1165 if name and name ~= "" then
1166 local default = specification.default
1167 local description = specification.description
1168 local initializers = specification.initializers
1169 local processors = specification.processors
1170 local manipulators = specification.manipulators
1171 local modechecker = specification.modechecker
1172 if default then
1173 where.defaults[name] = default
1174 end
1175 if description and description ~= "" then
1176 where.descriptions[name] = description
1177 end
1178 if initializers then
1179 set('initializers',name,where,specification)
1180 end
1181 if processors then
1182 set('processors', name,where,specification)
1183 end
1184 if manipulators then
1185 set('manipulators',name,where,specification)
1186 end
1187 if modechecker then
1188 where.modechecker = modechecker
1189 end
1190 end
1191 end
1192
1193 constructors.registerfeature = register
1194
1195 function constructors.getfeatureaction(what,where,mode,name)
1196 what = handlers[what].features
1197 if what then
1198 where = what[where]
1199 if where then
1200 mode = where[mode]
1201 if mode then
1202 for i=1,#mode do
1203 local m = mode[i]
1204 if m.name == name then
1205 return m.action
1206 end
1207 end
1208 end
1209 end
1210 end
1211 end
1212
1213 local newfeatures = { }
1214 constructors.newfeatures = newfeatures
1215 constructors.features = newfeatures
1216
1217 local function setnewfeatures(what)
1218 local handler = handlers[what]
1219 local features = handler.features
1220 if not features then
1221 local tables = handler.tables
1222 local statistics = handler.statistics
1223 features = allocate {
1224 defaults = { },
1225 descriptions = tables and tables.features or { },
1226 used = statistics and statistics.usedfeatures or { },
1227 initializers = { base = { }, node = { }, none = { }, plug = { } },
1228 processors = { base = { }, node = { }, none = { }, plug = { } },
1229 manipulators = { base = { }, node = { }, none = { }, plug = { } },
1230 finalizers = { base = { }, node = { }, none = { }, plug = { } },
1231 }
1232 features.register = function(specification) return register(features,specification) end
1233 handler.features = features
1234 end
1235 return features
1236 end
1237
1238 setmetatable(newfeatures, {
1239 __call = function(t,k) local v = t[k] return v end,
1240 __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end,
1241 })
1242
1243end
1244
1245do
1246
1247 local newhandler = { }
1248 constructors.handlers = newhandler
1249 constructors.newhandler = newhandler
1250
1251 local function setnewhandler(what)
1252 local handler = handlers[what]
1253 if not handler then
1254 handler = { }
1255 handlers[what] = handler
1256 end
1257 return handler
1258 end
1259
1260 setmetatable(newhandler, {
1261 __call = function(t,k) local v = t[k] return v end,
1262 __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end,
1263 })
1264
1265end
1266
1267do
1268
1269
1270 local newenhancer = { }
1271 constructors.enhancers = newenhancer
1272 constructors.newenhancer = newenhancer
1273
1274 local function setnewenhancer(format)
1275
1276 local handler = handlers[format]
1277 local enhancers = handler.enhancers
1278
1279 if not enhancers then
1280
1281 local actions = allocate()
1282 local before = allocate()
1283 local after = allocate()
1284 local order = allocate()
1285 local known = { }
1286 local nofsteps = 0
1287 local patches = { before = before, after = after }
1288
1289 local trace = false
1290 local report = logs.reporter("fonts",format .. " enhancing")
1291
1292 trackers.register(format .. ".loading", function(v) trace = v end)
1293
1294 local function enhance(name,data,filename,raw)
1295 local enhancer = actions[name]
1296 if enhancer then
1297 if trace then
1298 report("apply enhancement %a to file %a",name,filename)
1299 ioflush()
1300 end
1301 enhancer(data,filename,raw)
1302 else
1303
1304 end
1305 end
1306
1307 local function apply(data,filename,raw)
1308 local basename = file.basename(lower(filename))
1309 if trace then
1310 report("%s enhancing file %a","start",filename)
1311 end
1312 ioflush()
1313 for e=1,nofsteps do
1314 local enhancer = order[e]
1315 local b = before[enhancer]
1316 if b then
1317 for pattern, action in next, b do
1318 if find(basename,pattern) then
1319 action(data,filename,raw)
1320 end
1321 end
1322 end
1323 enhance(enhancer,data,filename,raw)
1324 local a = after[enhancer]
1325 if a then
1326 for pattern, action in next, a do
1327 if find(basename,pattern) then
1328 action(data,filename,raw)
1329 end
1330 end
1331 end
1332 ioflush()
1333 end
1334 if trace then
1335 report("%s enhancing file %a","stop",filename)
1336 end
1337 ioflush()
1338 end
1339
1340 local function register(what,action)
1341 if action then
1342 if actions[what] then
1343
1344 else
1345 nofsteps = nofsteps + 1
1346 order[nofsteps] = what
1347 known[what] = nofsteps
1348 end
1349 actions[what] = action
1350 else
1351 report("bad enhancer %a",what)
1352 end
1353 end
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369 local function patch(what,where,pattern,action)
1370 local pw = patches[what]
1371 if pw then
1372 local ww = pw[where]
1373 if ww then
1374 ww[pattern] = action
1375 else
1376 pw[where] = { [pattern] = action }
1377 if not known[where] then
1378 nofsteps = nofsteps + 1
1379 order[nofsteps] = where
1380 known[where] = nofsteps
1381 end
1382 end
1383 end
1384 end
1385
1386 enhancers = {
1387 register = register,
1388 apply = apply,
1389 patch = patch,
1390 report = report,
1391 patches = {
1392 register = patch,
1393 report = report,
1394 },
1395 }
1396
1397 handler.enhancers = enhancers
1398 end
1399 return enhancers
1400 end
1401
1402 setmetatable(newenhancer, {
1403 __call = function(t,k) local v = t[k] return v end,
1404 __index = function(t,k) local v = setnewenhancer(k) t[k] = v return v end,
1405 })
1406
1407end
1408
1409
1410
1411function constructors.checkedfeatures(what,features)
1412 local defaults = handlers[what].features.defaults
1413 if features and next(features) then
1414 features = fastcopy(features)
1415 for key, value in next, defaults do
1416 if features[key] == nil then
1417 features[key] = value
1418 end
1419 end
1420 return features
1421 else
1422 return fastcopy(defaults)
1423 end
1424end
1425
1426
1427
1428function constructors.initializefeatures(what,tfmdata,features,trace,report)
1429 if features and next(features) then
1430 local properties = tfmdata.properties or { }
1431 local whathandler = handlers[what]
1432 local whatfeatures = whathandler.features
1433 local whatmodechecker = whatfeatures.modechecker
1434
1435 local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
1436 properties.mode = mode
1437 features.mode = mode
1438
1439 local done = { }
1440 while true do
1441 local redo = false
1442 local initializers = whatfeatures.initializers[mode]
1443 if initializers then
1444 for i=1,#initializers do
1445 local step = initializers[i]
1446 local feature = step.name
1447
1448 local value = features[feature]
1449 if not value then
1450
1451 elseif done[feature] then
1452
1453 else
1454 local action = step.action
1455 if trace then
1456 report("initializing feature %a to %a for mode %a for font %a",feature,
1457 value,mode,tfmdata.properties.fullname)
1458 end
1459 action(tfmdata,value,features)
1460 if mode ~= properties.mode or mode ~= features.mode then
1461 if whatmodechecker then
1462 properties.mode = whatmodechecker(tfmdata,features,properties.mode)
1463 features.mode = properties.mode
1464 end
1465 if mode ~= properties.mode then
1466 mode = properties.mode
1467 redo = true
1468 end
1469 end
1470 done[feature] = true
1471 end
1472 if redo then
1473 break
1474 end
1475 end
1476 if not redo then
1477 break
1478 end
1479 else
1480 break
1481 end
1482 end
1483 properties.mode = mode
1484 return true
1485 else
1486 return false
1487 end
1488end
1489
1490
1491
1492function constructors.collectprocessors(what,tfmdata,features,trace,report)
1493 local processes = { }
1494 local nofprocesses = 0
1495 if features and next(features) then
1496 local properties = tfmdata.properties
1497 local whathandler = handlers[what]
1498 local whatfeatures = whathandler.features
1499 local whatprocessors = whatfeatures.processors
1500 local mode = properties.mode
1501 local processors = whatprocessors[mode]
1502 if processors then
1503 for i=1,#processors do
1504 local step = processors[i]
1505 local feature = step.name
1506 if features[feature] then
1507 local action = step.action
1508 if trace then
1509 report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
1510 end
1511 if action then
1512 nofprocesses = nofprocesses + 1
1513 processes[nofprocesses] = action
1514 end
1515 end
1516 end
1517 elseif trace then
1518 report("no feature processors for mode %a for font %a",mode,properties.fullname)
1519 end
1520 end
1521 return processes
1522end
1523
1524
1525
1526local function apply(key,what,tfmdata,features,trace,report)
1527 if features and next(features) then
1528 local properties = tfmdata.properties
1529 local whathandler = handlers[what]
1530 local whatfeatures = whathandler.features
1531 local whatactions = whatfeatures[key]
1532 local mode = properties.mode
1533 local actions = whatactions[mode]
1534 if actions then
1535 for i=1,#actions do
1536 local step = actions[i]
1537 local feature = step.name
1538 local value = features[feature]
1539 if value then
1540 local action = step.action
1541 if trace then
1542 report("applying feature %s %a for mode %a for font %a",key,feature,mode,properties.fullname)
1543 end
1544 if action then
1545 action(tfmdata,feature,value)
1546 end
1547 end
1548 end
1549 end
1550 end
1551end
1552
1553function constructors.applymanipulators(what,tfmdata,features,trace,report)
1554 if features and next(features) then
1555 apply("manipulators",what,tfmdata,features,trace,report)
1556 end
1557end
1558
1559function constructors.applyfinalizers(what,tfmdata,features,trace,report)
1560 if features and next(features) then
1561 apply("finalizers",what,tfmdata,features,trace,report)
1562 end
1563end
1564
1565function constructors.addcoreunicodes(unicodes)
1566 if not unicodes then
1567 unicodes = { }
1568 end
1569 unicodes.space = 0x0020
1570 unicodes.hyphen = 0x002D
1571 unicodes.zwj = 0x200D
1572 unicodes.zwnj = 0x200C
1573 return unicodes
1574end
1575 |