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(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 = tex.charactertagcodes.abovebaseline
272local belowbaseline_tag = tex.charactertagcodes.belowbaseline
273
274local textcontrolcodes = tex.textcontrolcodes
275local collapse_hyphens = textcontrolcodes.collapsehyphens
276local base_ligaturing = textcontrolcodes.baseligaturing
277local base_kerning = textcontrolcodes.basekerning
278local none_protected = textcontrolcodes.noneprotected
279local has_italics = textcontrolcodes.hasitalics
280local auto_italics = 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
458 local hasquality = parameters.expansion or parameters.protrusion
459 local hasitalics = properties.hasitalics
460 local useditalicangle = properties.useditalicangle or properties.italicangle or 0
461 local usedslant = properties.usedslant or properties.usedslant or 0
462
463 local haskerns = properties.haskerns or properties.mode == "base"
464 local hasligatures = properties.hasligatures or properties.mode == "base"
465 local writingmode = properties.writingmode or "horizontal"
466
467 targetparameters.useditalicangle = useditalicangle
468 targetproperties.useditalicangle = useditalicangle
469 targetparameters.usedslant = usedslant
470 targetproperties.usedslant = usedslant
471
472 if changed and not next(changed) then
473 changed = false
474 end
475
476 target.writingmode = writingmode == "vertical" and "vertical" or "horizontal"
477
478 target.postprocessors = tfmdata.postprocessors
479
480 local targetslant = (parameters.slant or parameters[1] or 0) * factors.pt
481 local targetspace = (parameters.space or parameters[2] or 0) * hdelta
482 local targetspacestretch = (parameters.spacestretch or parameters[3] or 0) * hdelta
483 local targetspaceshrink = (parameters.spaceshrink or parameters[4] or 0) * hdelta
484 local targetxheight = (parameters.xheight or parameters[5] or 0) * vdelta
485 local targetquad = (parameters.quad or parameters[6] or 0) * hdelta
486 local targetextraspace = (parameters.extraspace or parameters[7] or 0) * hdelta
487
488 targetparameters.expansion = parameters.expansion
489 targetparameters.protrusion = parameters.protrusion
490
491 targetparameters.slant = targetslant
492 targetparameters.space = targetspace
493 targetparameters.spacestretch = targetspacestretch
494 targetparameters.spaceshrink = targetspaceshrink
495 targetparameters.xheight = targetxheight
496 targetparameters.quad = targetquad
497 targetparameters.extraspace = targetextraspace
498
499 local hshift = parameters.hshift
500 if hshift then
501 targetparameters.hshift = delta * hshift
502 end
503 local vshift = parameters.vshift
504 if vshift then
505 targetparameters.vshift = delta * vshift
506 end
507
508 local ascender = parameters.ascender if ascender then targetparameters.ascender = vdelta * ascender end
509 local descender = parameters.descender if descender then targetparameters.descender = vdelta * descender end
510 local capheight = parameters.capheight if capheight then targetparameters.capheight = vdelta * capheight end
511 local ascent = parameters.ascent if ascent then targetparameters.ascent = vdelta * ascent end
512 local descent = parameters.descent if descent then targetparameters.descent = vdelta * descent end
513
514 constructors.enhanceparameters(targetparameters)
515
516 local scaledwidth = defaultwidth * hdelta
517 local scaledheight = defaultheight * vdelta
518 local scaleddepth = defaultdepth * vdelta
519
520 local textcontrol = properties.textcontrol or 0
521 if targetproperties.mode == "base" then
522 textcontrol = textcontrol | base_ligaturing | base_kerning
523
524
525
526
527
528
529
530
531
532
533 elseif targetproperties.mode == "none" then
534 textcontrol = textcontrol | none_protected
535 end
536 if hasitalics then
537 textcontrol = textcontrol | has_italics
538 end
539 if useditalicangle ~= 0 or usedslant ~= 0 then
540 textcontrol = textcontrol | auto_italics
541 end
542 targetproperties.textcontrol = textcontrol
543 target.textcontrol = textcontrol
544
545 local hasmath = (properties.hasmath or next(mathparameters)) and true
546
547 if hasmath then
548 constructors.assignmathparameters(target,tfmdata)
549
550 properties.hasmath = true
551 target.nomath = false
552 target.MathConstants = target.mathparameters
553
554 local compactmath = properties.compactmath
555
556 targetproperties.compactmath = compactmath
557 target.compactmath = compactmath
558
559 local textscale = parameters.textscale
560 local scriptscale = parameters.scriptscale
561 local scriptscriptscale = parameters.scriptscriptscale
562
563 parameters.textscale = textscale
564 parameters.scriptscale = scriptscale
565 parameters.scriptscriptscale = scriptscriptscale
566
567 target.textscale = textscale
568 target.scriptscale = scriptscale
569 target.scriptscriptscale = scriptscriptscale
570
571 targetparameters.textscale = textscale
572 targetparameters.scriptscale = scriptscale
573 targetparameters.scriptscriptscale = scriptscriptscale
574
575 local mathcontrol = properties.mathcontrol
576 targetproperties.mathcontrol = mathcontrol
577 target.mathcontrol = mathcontrol
578
579 else
580 properties.hasmath = false
581 target.nomath = true
582 target.mathparameters = nil
583 end
584
585 if trace_defining then
586 report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a",
587 name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta,
588 hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
589 end
590
591 constructors.beforecopyingcharacters(target,tfmdata)
592
593 local sharedkerns = { }
594
595
596
597 for unicode, character in next, characters do
598 local chr, description, index
599
600 if changed then
601 local c = changed[unicode]
602 if c and c ~= unicode then
603 local cc = changed[c]
604 if cc then
605 while cc do
606 c = cc
607 cc = changed[c]
608 end
609 end
610
611 if c then
612 description = descriptions[c] or descriptions[unicode] or character
613 character = characters[c] or character
614 index = description.index or c
615 else
616 description = descriptions[unicode] or character
617 index = description.index or unicode
618 end
619 else
620 description = descriptions[unicode] or character
621 index = description.index or unicode
622 end
623 else
624 description = descriptions[unicode] or character
625 index = description.index or unicode
626 end
627 local width = description.width
628 local height = description.height
629 local depth = description.depth
630 local isunicode = description.unicode
631 if width then width = hdelta*width else width = scaledwidth end
632 if height then height = vdelta*height else height = scaledheight end
633
634 if depth and depth ~= 0 then
635 depth = vdelta*depth
636 if isunicode then
637 chr = {
638 index = index,
639 height = height,
640 depth = depth,
641 width = width,
642 unicode = isunicode,
643 }
644 else
645 chr = {
646 index = index,
647 height = height,
648 depth = depth,
649 width = width,
650 }
651 end
652 else
653 if isunicode then
654 chr = {
655 index = index,
656 height = height,
657 width = width,
658 unicode = isunicode,
659 }
660 else
661 chr = {
662 index = index,
663 height = height,
664 width = width,
665 }
666 end
667 end
668 local bb = description.boundingbox
669 if bb then
670 if bb[2] > 0 then
671 chr.tag = abovebaseline_tag
672 elseif bb[4] < 0 then
673 chr.tag = belowbaseline_tag
674 end
675 end
676 if hasquality then
677
678 local ve = character.expansion
679 if ve then
680 chr.expansion = ve*1000
681 end
682 local vc = character.compression
683 if vc then
684 chr.compression = vc*1000
685 end
686 local vl = character.leftprotrusion
687 if vl then
688 chr.leftprotrusion = width*vl
689 end
690 local vr = character.rightprotrusion
691 if vr then
692 chr.rightprotrusion = width*vr
693 end
694 end
695
696 if hasmath then
697 local nxt = character.next
698 if nxt then
699 chr.next = nxt
700 end
701 local parts = character.parts
702 if parts then
703 local orientation = character.partsorientation or "vertical"
704 chr.parts = scaleparts(parts,orientation == "horizontal" and hdelta or vdelta)
705 chr.partsorientation = orientation
706 end
707 local vi = character.partsitalic
708 if vi and vi ~= 0 then
709 chr.partsitalic = vi*hdelta
710 end
711 local va = character.topanchor
712 if va and va ~= 0 then
713
714 chr.topanchor = va*hdelta
715 end
716 va = character.bottomanchor
717 if va and va ~= 0 then
718
719 chr.bottomanchor = va*hdelta
720 end
721
722 local mk = character.mathkerns
723 if mk then
724 local tr = mk.topright
725 local tl = mk.topleft
726 local br = mk.bottomright
727 local bl = mk.bottomleft
728 chr.mathkerns = {
729 topright = tr and mathkerns(tr,vdelta) or nil,
730 topleft = tl and mathkerns(tl,vdelta) or nil,
731 bottomright = br and mathkerns(br,vdelta) or nil,
732 bottomleft = bl and mathkerns(bl,vdelta) or nil,
733 }
734 end
735
736 if hasitalics then
737 local vi = character.italic
738 if vi and vi ~= 0 then
739 chr.italic = vi*hdelta
740 end
741 end
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770 local sm = character.smaller
771 if sm then
772 chr.smaller = sm
773 end
774
775
776
777
778 local fa = character.flataccent
779 if fa then
780 chr.flataccent = fa
781 end
782 elseif hasitalics then
783 local vi = character.italic
784 if vi and vi ~= 0 then
785 chr.italic = vi*hdelta
786 end
787 end
788 if haskerns then
789 local vk = character.kerns
790 if vk then
791 local s = sharedkerns[vk]
792 if not s then
793 s = { }
794 for k,v in next, vk do s[k] = v*hdelta end
795 sharedkerns[vk] = s
796 end
797 chr.kerns = s
798 end
799 end
800 if hasligatures then
801 local vl = character.ligatures
802 if vl then
803 if true then
804 chr.ligatures = vl
805 else
806 local tt = { }
807 for i, l in next, vl do
808 tt[i] = l
809 end
810 chr.ligatures = tt
811 end
812 end
813 end
814
815 local vc = character.commands
816 if vc then
817 chr.commands = scalecommands(vc,hdelta,vdelta)
818 end
819 local cb = character.callback
820 if cb then
821 chr.callback = cb
822 end
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837 targetcharacters[unicode] = chr
838 end
839
840 properties.setitalics = hasitalics
841
842
843
844 constructors.aftercopyingcharacters(target,tfmdata)
845
846 constructors.trytosharefont(target,tfmdata)
847
848
849
850 local vfonts = target.fonts
851 if not vfonts or #vfonts == 0 then
852 target.fonts = { { id = 0 } }
853 end
854
855 if trace_defining then
856 report_defining("font %a, size %N, delta %N",name,scaledpoints,delta)
857 end
858
859 return target
860end
861
862function constructors.finalize(tfmdata)
863 if tfmdata.properties and tfmdata.properties.finalized then
864 return
865 end
866
867 if not tfmdata.characters then
868 return nil
869 end
870
871 if not tfmdata.goodies then
872 tfmdata.goodies = { }
873 end
874
875 local parameters = tfmdata.parameters
876 local properties = tfmdata.properties
877 if not parameters then
878 return nil
879 end
880
881 if not parameters.expansion then
882 parameters.expansion = {
883 stretch = tfmdata.stretch or 0,
884 shrink = tfmdata.shrink or 0,
885 step = tfmdata.step or 0,
886 }
887 end
888
889 if not parameters.size then
890 parameters.size = tfmdata.size
891 end
892
893
894
895
896
897 local effect = properties and properties.effect
898 if effect then
899 parameters.mode = effect.mode
900 parameters.width = effect.width
901 parameters.slantfactor = effect.slant
902 parameters.extendfactor = effect.extend
903 parameters.squeezefactor = effect.squeeze
904 else
905 parameters.mode = 0
906 parameters.width = 0
907 parameters.slantfactor = 0
908 parameters.extendfactor = 1
909 parameters.squeezefactor = 1
910 end
911 local designsize = parameters.designsize
912 if designsize then
913 parameters.minsize = tfmdata.minsize or designsize
914 parameters.maxsize = tfmdata.maxsize or designsize
915 else
916 designsize = factors.pt * 10
917 parameters.designsize = designsize
918 parameters.minsize = designsize
919 parameters.maxsize = designsize
920 end
921 parameters.minsize = tfmdata.minsize or parameters.designsize
922 parameters.maxsize = tfmdata.maxsize or parameters.designsize
923
924 if not parameters.units then
925 parameters.units = tfmdata.units or 1000
926 end
927
928 if not tfmdata.descriptions then
929 local descriptions = { }
930 setmetatableindex(descriptions, function(t,k) local v = { } t[k] = v return v end)
931 tfmdata.descriptions = descriptions
932 end
933
934 local properties = tfmdata.properties
935 if not properties then
936 properties = { }
937 tfmdata.properties = properties
938 end
939
940 properties.fontname = properties.fontname or tfmdata.fontname
941 properties.filename = properties.filename or tfmdata.filename
942 properties.fullname = properties.fullname or tfmdata.fullname
943 properties.name = properties.name or tfmdata.name
944 properties.psname = properties.psname or tfmdata.psname
945
946 properties.subfont = tfmdata.subfont or nil
947 properties.cidinfo = tfmdata.cidinfo or nil
948 properties.format = tfmdata.format or "type1"
949 properties.writingmode = tfmdata.writingmode or "horizontal"
950 properties.usedbitmap = tfmdata.usedbitmap
951
952 if not tfmdata.resources then
953 tfmdata.resources = { }
954 end
955 if not tfmdata.shared then
956 tfmdata.shared = { }
957 end
958
959
960
961
962 if not properties.hasmath then
963 properties.hasmath = not tfmdata.nomath
964 end
965
966 tfmdata.MathConstants = nil
967 tfmdata.postprocessors = nil
968
969 tfmdata.fontname = nil
970 tfmdata.filename = nil
971 tfmdata.fullname = nil
972 tfmdata.name = nil
973 tfmdata.psname = nil
974
975 tfmdata.subfont = nil
976 tfmdata.cidinfo = nil
977 tfmdata.format = nil
978 tfmdata.nomath = nil
979 tfmdata.designsize = nil
980
981 tfmdata.size = nil
982 tfmdata.stretch = nil
983 tfmdata.shrink = nil
984 tfmdata.step = nil
985 tfmdata.slant = nil
986 tfmdata.extend = nil
987 tfmdata.squeeze = nil
988 tfmdata.mode = nil
989 tfmdata.width = nil
990 tfmdata.units = nil
991
992 tfmdata.cache = nil
993
994 properties.finalized = true
995
996 return tfmdata
997end
998
999
1000
1001local hashmethods = { }
1002constructors.hashmethods = hashmethods
1003
1004function constructors.hashfeatures(specification)
1005 local features = specification.features
1006 if features then
1007 local t, n = { }, 0
1008 for category, list in sortedhash(features) do
1009 if next(list) then
1010 local hasher = hashmethods[category]
1011 if hasher then
1012 local hash = hasher(list)
1013 if hash then
1014 n = n + 1
1015 t[n] = category .. ":" .. hash
1016 end
1017 end
1018 end
1019 end
1020 if n > 0 then
1021 return concat(t," & ")
1022 end
1023 end
1024 return "unknown"
1025end
1026
1027hashmethods.normal = function(list)
1028 local s = { }
1029 local n = 0
1030 for k, v in next, list do
1031 if not k then
1032
1033 elseif k == "number" or k == "features" then
1034
1035 else
1036 n = n + 1
1037 if type(v) == "table" then
1038
1039 local t = { }
1040 local m = 0
1041 for k, v in next, v do
1042 m = m + 1
1043 t[m] = k .. '=' .. tostring(v)
1044 end
1045 sort(t)
1046 s[n] = k .. '={' .. concat(t,",") .. "}"
1047 else
1048 s[n] = k .. '=' .. tostring(v)
1049 end
1050 end
1051 end
1052 if n > 0 then
1053 sort(s)
1054 return concat(s,"+")
1055 end
1056end
1057
1058
1059
1060
1061
1062
1063
1064function constructors.hashinstance(specification,force)
1065
1066end
1067
1068function constructors.setname(tfmdata,specification)
1069
1070end
1071
1072function constructors.checkedfilename(data)
1073 local foundfilename = data.foundfilename
1074 if not foundfilename then
1075 local askedfilename = data.filename or ""
1076 if askedfilename ~= "" then
1077 askedfilename = resolvers.resolve(askedfilename)
1078 foundfilename = resolvers.findbinfile(askedfilename,"") or ""
1079 if foundfilename == "" then
1080 report_defining("source file %a is not found",askedfilename)
1081 foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or ""
1082 if foundfilename ~= "" then
1083 report_defining("using source file %a due to cache mismatch",foundfilename)
1084 end
1085 end
1086 end
1087 data.foundfilename = foundfilename
1088 end
1089 return foundfilename
1090end
1091
1092local formats = allocate()
1093fonts.formats = formats
1094
1095setmetatableindex(formats, function(t,k)
1096 local l = lower(k)
1097 if rawget(t,k) then
1098 t[k] = l
1099 return l
1100 end
1101 return rawget(t,file.suffix(l))
1102end)
1103
1104do
1105
1106 local function setindeed(mode,source,target,group,name,position)
1107 local action = source[mode]
1108 if not action then
1109 return
1110 end
1111 local t = target[mode]
1112 if not t then
1113 report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
1114 os.exit()
1115 elseif position then
1116
1117 insert(t, position, { name = name, action = action })
1118 else
1119 for i=1,#t do
1120 local ti = t[i]
1121 if ti.name == name then
1122 ti.action = action
1123 return
1124 end
1125 end
1126 insert(t, { name = name, action = action })
1127 end
1128 end
1129
1130 local function set(group,name,target,source)
1131 target = target[group]
1132 if not target then
1133 report_defining("fatal target error in setting feature %a, group %a",name,group)
1134 os.exit()
1135 end
1136 local source = source[group]
1137 if not source then
1138 report_defining("fatal source error in setting feature %a, group %a",name,group)
1139 os.exit()
1140 end
1141 local position = source.position
1142 setindeed("node",source,target,group,name,position)
1143 setindeed("base",source,target,group,name,position)
1144 setindeed("none",source,target,group,name,position)
1145 setindeed("plug",source,target,group,name,position)
1146 end
1147
1148 local function register(where,specification)
1149 local name = specification.name
1150 if name and name ~= "" then
1151 local default = specification.default
1152 local description = specification.description
1153 local initializers = specification.initializers
1154 local processors = specification.processors
1155 local manipulators = specification.manipulators
1156 local modechecker = specification.modechecker
1157 if default then
1158 where.defaults[name] = default
1159 end
1160 if description and description ~= "" then
1161 where.descriptions[name] = description
1162 end
1163 if initializers then
1164 set('initializers',name,where,specification)
1165 end
1166 if processors then
1167 set('processors', name,where,specification)
1168 end
1169 if manipulators then
1170 set('manipulators',name,where,specification)
1171 end
1172 if modechecker then
1173 where.modechecker = modechecker
1174 end
1175 end
1176 end
1177
1178 constructors.registerfeature = register
1179
1180 function constructors.getfeatureaction(what,where,mode,name)
1181 what = handlers[what].features
1182 if what then
1183 where = what[where]
1184 if where then
1185 mode = where[mode]
1186 if mode then
1187 for i=1,#mode do
1188 local m = mode[i]
1189 if m.name == name then
1190 return m.action
1191 end
1192 end
1193 end
1194 end
1195 end
1196 end
1197
1198 local newfeatures = { }
1199 constructors.newfeatures = newfeatures
1200 constructors.features = newfeatures
1201
1202 local function setnewfeatures(what)
1203 local handler = handlers[what]
1204 local features = handler.features
1205 if not features then
1206 local tables = handler.tables
1207 local statistics = handler.statistics
1208 features = allocate {
1209 defaults = { },
1210 descriptions = tables and tables.features or { },
1211 used = statistics and statistics.usedfeatures or { },
1212 initializers = { base = { }, node = { }, none = { }, plug = { } },
1213 processors = { base = { }, node = { }, none = { }, plug = { } },
1214 manipulators = { base = { }, node = { }, none = { }, plug = { } },
1215 finalizers = { base = { }, node = { }, none = { }, plug = { } },
1216 }
1217 features.register = function(specification) return register(features,specification) end
1218 handler.features = features
1219 end
1220 return features
1221 end
1222
1223 setmetatable(newfeatures, {
1224 __call = function(t,k) local v = t[k] return v end,
1225 __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end,
1226 })
1227
1228end
1229
1230do
1231
1232 local newhandler = { }
1233 constructors.handlers = newhandler
1234 constructors.newhandler = newhandler
1235
1236 local function setnewhandler(what)
1237 local handler = handlers[what]
1238 if not handler then
1239 handler = { }
1240 handlers[what] = handler
1241 end
1242 return handler
1243 end
1244
1245 setmetatable(newhandler, {
1246 __call = function(t,k) local v = t[k] return v end,
1247 __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end,
1248 })
1249
1250end
1251
1252do
1253
1254
1255 local newenhancer = { }
1256 constructors.enhancers = newenhancer
1257 constructors.newenhancer = newenhancer
1258
1259 local function setnewenhancer(format)
1260
1261 local handler = handlers[format]
1262 local enhancers = handler.enhancers
1263
1264 if not enhancers then
1265
1266 local actions = allocate()
1267 local before = allocate()
1268 local after = allocate()
1269 local order = allocate()
1270 local known = { }
1271 local nofsteps = 0
1272 local patches = { before = before, after = after }
1273
1274 local trace = false
1275 local report = logs.reporter("fonts",format .. " enhancing")
1276
1277 trackers.register(format .. ".loading", function(v) trace = v end)
1278
1279 local function enhance(name,data,filename,raw)
1280 local enhancer = actions[name]
1281 if enhancer then
1282 if trace then
1283 report("apply enhancement %a to file %a",name,filename)
1284 ioflush()
1285 end
1286 enhancer(data,filename,raw)
1287 else
1288
1289 end
1290 end
1291
1292 local function apply(data,filename,raw)
1293 local basename = file.basename(lower(filename))
1294 if trace then
1295 report("%s enhancing file %a","start",filename)
1296 end
1297 ioflush()
1298 for e=1,nofsteps do
1299 local enhancer = order[e]
1300 local b = before[enhancer]
1301 if b then
1302 for pattern, action in next, b do
1303 if find(basename,pattern) then
1304 action(data,filename,raw)
1305 end
1306 end
1307 end
1308 enhance(enhancer,data,filename,raw)
1309 local a = after[enhancer]
1310 if a then
1311 for pattern, action in next, a do
1312 if find(basename,pattern) then
1313 action(data,filename,raw)
1314 end
1315 end
1316 end
1317 ioflush()
1318 end
1319 if trace then
1320 report("%s enhancing file %a","stop",filename)
1321 end
1322 ioflush()
1323 end
1324
1325 local function register(what,action)
1326 if action then
1327 if actions[what] then
1328
1329 else
1330 nofsteps = nofsteps + 1
1331 order[nofsteps] = what
1332 known[what] = nofsteps
1333 end
1334 actions[what] = action
1335 else
1336 report("bad enhancer %a",what)
1337 end
1338 end
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354 local function patch(what,where,pattern,action)
1355 local pw = patches[what]
1356 if pw then
1357 local ww = pw[where]
1358 if ww then
1359 ww[pattern] = action
1360 else
1361 pw[where] = { [pattern] = action }
1362 if not known[where] then
1363 nofsteps = nofsteps + 1
1364 order[nofsteps] = where
1365 known[where] = nofsteps
1366 end
1367 end
1368 end
1369 end
1370
1371 enhancers = {
1372 register = register,
1373 apply = apply,
1374 patch = patch,
1375 report = report,
1376 patches = {
1377 register = patch,
1378 report = report,
1379 },
1380 }
1381
1382 handler.enhancers = enhancers
1383 end
1384 return enhancers
1385 end
1386
1387 setmetatable(newenhancer, {
1388 __call = function(t,k) local v = t[k] return v end,
1389 __index = function(t,k) local v = setnewenhancer(k) t[k] = v return v end,
1390 })
1391
1392end
1393
1394
1395
1396function constructors.checkedfeatures(what,features)
1397 local defaults = handlers[what].features.defaults
1398 if features and next(features) then
1399 features = fastcopy(features)
1400 for key, value in next, defaults do
1401 if features[key] == nil then
1402 features[key] = value
1403 end
1404 end
1405 return features
1406 else
1407 return fastcopy(defaults)
1408 end
1409end
1410
1411
1412
1413function constructors.initializefeatures(what,tfmdata,features,trace,report)
1414 if features and next(features) then
1415 local properties = tfmdata.properties or { }
1416 local whathandler = handlers[what]
1417 local whatfeatures = whathandler.features
1418 local whatmodechecker = whatfeatures.modechecker
1419
1420 local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
1421 properties.mode = mode
1422 features.mode = mode
1423
1424 local done = { }
1425 while true do
1426 local redo = false
1427 local initializers = whatfeatures.initializers[mode]
1428 if initializers then
1429 for i=1,#initializers do
1430 local step = initializers[i]
1431 local feature = step.name
1432
1433 local value = features[feature]
1434 if not value then
1435
1436 elseif done[feature] then
1437
1438 else
1439 local action = step.action
1440 if trace then
1441 report("initializing feature %a to %a for mode %a for font %a",feature,
1442 value,mode,tfmdata.properties.fullname)
1443 end
1444 action(tfmdata,value,features)
1445 if mode ~= properties.mode or mode ~= features.mode then
1446 if whatmodechecker then
1447 properties.mode = whatmodechecker(tfmdata,features,properties.mode)
1448 features.mode = properties.mode
1449 end
1450 if mode ~= properties.mode then
1451 mode = properties.mode
1452 redo = true
1453 end
1454 end
1455 done[feature] = true
1456 end
1457 if redo then
1458 break
1459 end
1460 end
1461 if not redo then
1462 break
1463 end
1464 else
1465 break
1466 end
1467 end
1468 properties.mode = mode
1469 return true
1470 else
1471 return false
1472 end
1473end
1474
1475
1476
1477function constructors.collectprocessors(what,tfmdata,features,trace,report)
1478 local processes = { }
1479 local nofprocesses = 0
1480 if features and next(features) then
1481 local properties = tfmdata.properties
1482 local whathandler = handlers[what]
1483 local whatfeatures = whathandler.features
1484 local whatprocessors = whatfeatures.processors
1485 local mode = properties.mode
1486 local processors = whatprocessors[mode]
1487 if processors then
1488 for i=1,#processors do
1489 local step = processors[i]
1490 local feature = step.name
1491 if features[feature] then
1492 local action = step.action
1493 if trace then
1494 report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
1495 end
1496 if action then
1497 nofprocesses = nofprocesses + 1
1498 processes[nofprocesses] = action
1499 end
1500 end
1501 end
1502 elseif trace then
1503 report("no feature processors for mode %a for font %a",mode,properties.fullname)
1504 end
1505 end
1506 return processes
1507end
1508
1509
1510
1511local function apply(key,what,tfmdata,features,trace,report)
1512 if features and next(features) then
1513 local properties = tfmdata.properties
1514 local whathandler = handlers[what]
1515 local whatfeatures = whathandler.features
1516 local whatactions = whatfeatures[key]
1517 local mode = properties.mode
1518 local actions = whatactions[mode]
1519 if actions then
1520 for i=1,#actions do
1521 local step = actions[i]
1522 local feature = step.name
1523 local value = features[feature]
1524 if value then
1525 local action = step.action
1526 if trace then
1527 report("applying feature %s %a for mode %a for font %a",key,feature,mode,properties.fullname)
1528 end
1529 if action then
1530 action(tfmdata,feature,value)
1531 end
1532 end
1533 end
1534 end
1535 end
1536end
1537
1538function constructors.applymanipulators(what,tfmdata,features,trace,report)
1539 if features and next(features) then
1540 apply("manipulators",what,tfmdata,features,trace,report)
1541 end
1542end
1543
1544function constructors.applyfinalizers(what,tfmdata,features,trace,report)
1545 if features and next(features) then
1546 apply("finalizers",what,tfmdata,features,trace,report)
1547 end
1548end
1549
1550function constructors.addcoreunicodes(unicodes)
1551 if not unicodes then
1552 unicodes = { }
1553 end
1554 unicodes.space = 0x0020
1555 unicodes.hyphen = 0x002D
1556 unicodes.zwj = 0x200D
1557 unicodes.zwnj = 0x200C
1558 return unicodes
1559end
1560 |