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