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