1if not modules then modules = { } end modules ['font-tfm'] = {
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
9if not context then return end
10
11local next, type = next, type
12local match, format = string.match, string.format
13local concat, sortedhash = table.concat, table.sortedhash
14local idiv = number.idiv
15local addsuffix = file.addsuffix
16
17local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
18local trace_features = false trackers.register("tfm.features", function(v) trace_features = v end)
19
20local report_defining = logs.reporter("fonts","defining")
21local report_tfm = logs.reporter("fonts","tfm loading")
22
23local findbinfile = resolvers.findbinfile
24local setmetatableindex = table.setmetatableindex
25
26local fonts = fonts
27local handlers = fonts.handlers
28local helpers = fonts.helpers
29local readers = fonts.readers
30local constructors = fonts.constructors
31local encodings = fonts.encodings
32
33local slotcommand = helpers.commands.slot
34
35local tfm = constructors.handlers.tfm
36tfm.version = 1.000
37tfm.maxnestingdepth = 5
38tfm.maxnestingsize = 65536*1024
39
40local otf = fonts.handlers.otf
41local otfenhancers = otf.enhancers
42
43local tfmfeatures = constructors.features.tfm
44local registertfmfeature = tfmfeatures.register
45
46local tfmenhancers = constructors.enhancers.tfm
47local registertfmenhancer = tfmenhancers.register
48
49local charcommand = helpers.commands.char
50
51constructors.resolvevirtualtoo = false
52
53fonts.formats.tfm = "type1"
54fonts.formats.ofm = "type1"
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78function tfm.setfeatures(tfmdata,features)
79 local okay = constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
80 if okay then
81 return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
82 else
83 return { }
84 end
85end
86
87local depth = { }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124local loadtfmvf = tfm.readers.loadtfmvf
125
126local function read_from_tfm(specification)
127 local filename = specification.filename
128 local size = specification.size
129 depth[filename] = (depth[filename] or 0) + 1
130 if trace_defining then
131 report_defining("loading tfm file %a at size %s",filename,size)
132 end
133 local tfmdata = loadtfmvf(filename,size)
134 if tfmdata then
135
136 local features = specification.features and specification.features.normal or { }
137 local features = constructors.checkedfeatures("tfm",features)
138 specification.features.normal = features
139
140
141
142
143 local getmapentry = fonts.mappings.getentry
144
145 if getmapentry and not features.reencode then
146
147
148 local encoding, pfbfile, encfile = getmapentry(filename)
149 if encoding and pfbfile then
150 features.reencode = encfile
151 features.pfbfile = pfbfile
152 end
153 end
154 local newtfmdata = (depth[filename] == 1) and tfm.reencode(tfmdata,specification)
155 if newtfmdata then
156 tfmdata = newtfmdata
157 end
158
159 local resources = tfmdata.resources or { }
160 local properties = tfmdata.properties or { }
161 local parameters = tfmdata.parameters or { }
162 local shared = tfmdata.shared or { }
163
164 shared.features = features
165 shared.resources = resources
166
167 properties.id = specification.id
168 properties.name = tfmdata.name
169 properties.fontname = tfmdata.fontname
170 properties.psname = tfmdata.psname
171 properties.fullname = tfmdata.fullname
172 properties.filename = tfmdata.filename
173 properties.format = tfmdata.format or fonts.formats.tfm
174 properties.usedbitmap = tfmdata.usedbitmap
175 properties.designsize = tfmdata.designsize
176 parameters.designsize = tfmdata.designsize
177
178 if getmapentry and newtfmdata then
179 properties.filename = features.pfbfile
180 end
181
182 tfmdata.properties = properties
183 tfmdata.resources = resources
184 tfmdata.parameters = parameters
185 tfmdata.shared = shared
186
187 shared.rawdata = { resources = resources }
188 shared.features = features
189
190
191
192
193
194 if newtfmdata then
195
196
197
198 if not resources.marks then
199 resources.marks = { }
200 end
201 if not resources.sequences then
202 resources.sequences = { }
203 end
204 if not resources.features then
205 resources.features = {
206 gsub = { },
207 gpos = { },
208 }
209 end
210 if not tfmdata.changed then
211 tfmdata.changed = { }
212 end
213 if not tfmdata.descriptions then
214 tfmdata.descriptions = tfmdata.characters
215 end
216
217
218
219 otf.readers.addunicodetable(tfmdata)
220
221
222
223 tfmenhancers.apply(tfmdata,filename)
224
225
226
227 constructors.applymanipulators("tfm",tfmdata,features,trace_features,report_tfm)
228
229
230
231
232
233 otf.readers.unifymissing(tfmdata)
234
235
236
237
238 fonts.mappings.addtounicode(tfmdata,filename)
239
240 local tounicode = fonts.mappings.tounicode
241 for unicode, v in next, tfmdata.characters do
242 local u = v.unicode
243 if u then
244 v.tounicode = tounicode(u)
245 end
246 end
247
248
249
250
251
252
253
254
255 end
256
257 shared.processes = next(features) and tfm.setfeatures(tfmdata,features) or nil
258
259 fonts.loggers.register(tfmdata,'tfm',specification)
260
261 if size < 0 then
262 size = idiv(65536 * -size,100)
263 end
264
265 parameters.factor = 1
266 parameters.units = 1000
267 parameters.size = size
268 parameters.slant = parameters.slant or parameters[1] or 0
269 parameters.space = parameters.space or parameters[2] or 0
270 parameters.spacestretch = parameters.spacestretch or parameters[3] or 0
271 parameters.spaceshrink = parameters.spaceshrink or parameters[4] or 0
272 parameters.xheight = parameters.xheight or parameters[5] or 0
273 parameters.quad = parameters.quad or parameters[6] or 0
274 parameters.extraspace = parameters.extraspace or parameters[7] or 0
275
276 constructors.enhanceparameters(parameters)
277
278 properties.private = properties.private or tfmdata.private or privateoffset
279
280
281 if newtfmdata then
282
283
284
285
286 else
287
288 local fonts = tfmdata.fonts
289 if fonts then
290 for i=1,#fonts do
291 local font = fonts[i]
292 local id = font.id
293 if not id then
294 local name = font.name
295 local size = font.size
296 if name and size then
297 local data, id = constructors.readanddefine(name,size)
298 if id then
299 font.id = id
300 font.name = nil
301 font.size = nil
302 end
303 end
304 end
305 end
306 end
307 end
308
309 properties.haskerns = true
310 properties.hasligatures = true
311 properties.hasitalics = true
312 properties.bitmapped = tfmdata.bitmapped
313 properties.resolution = features.resolution or 7200
314
315
316
317
318 local bitmap = features.bitmap
319 if bitmap == "cff" or bitmap == "outline" or bitmap == "pk" then
320 local resolution = features.resolution or 7200
321 local settings = nil
322 local potrace = features.potrace
323 if potrace then
324 settings = utilities.parsers.settings_to_hash_colon_too(potrace)
325 settings.resolution = resolution
326 else
327 settings = {resolution = resolution }
328 end
329 if bitmap == "cff" then
330 parameters.size = parameters.size / 10 * 7200 / resolution
331 settings.bmp = "c"
332 properties.potrace = settings
333 elseif bitmap == "outline" then
334 settings.bmp = "o"
335 properties.potrace = settings
336 else
337 settings.bmp = "p"
338 end
339 properties.extrahash = table.sequenced(settings,",")
340 else
341 end
342
343 resources.unicodes = { }
344 resources.lookuptags = { }
345
346
347 tfmdata.subfont = 0
348
349 depth[filename] = depth[filename] - 1
350
351 return tfmdata
352 else
353 depth[filename] = depth[filename] - 1
354 end
355end
356
357local function check_tfm(specification,fullname)
358 local foundname = findbinfile(fullname, 'tfm') or ""
359 if foundname == "" then
360 foundname = findbinfile(fullname, 'ofm') or ""
361 end
362 if foundname == "" then
363 foundname = fonts.names.getfilename(fullname,"tfm") or ""
364 end
365 if foundname ~= "" then
366 specification.filename = foundname
367 specification.format = "ofm"
368 return read_from_tfm(specification)
369 elseif trace_defining then
370 report_defining("loading tfm with name %a fails",specification.name)
371 end
372end
373
374readers.check_tfm = check_tfm
375
376function readers.tfm(specification)
377 local fullname = specification.filename or ""
378 if fullname == "" then
379 local forced = specification.forced or ""
380 if forced ~= "" then
381 fullname = specification.name .. "." .. forced
382 else
383 fullname = specification.name
384 end
385 end
386 return check_tfm(specification,fullname)
387end
388
389readers.ofm = readers.tfm
390
391
392
393
394
395
396do
397
398 local outfiles = { }
399
400 local tfmcache = table.setmetatableindex(function(t,tfmdata)
401 local id = font.define(tfmdata)
402 t[tfmdata] = id
403 return id
404 end)
405
406 local encdone = table.setmetatableindex("table")
407
408 function tfm.reencode(tfmdata,specification)
409
410 local features = specification.features
411
412 if not features then
413 return
414 end
415
416 local features = features.normal
417
418 if not features then
419 return
420 end
421
422 local tfmfile = file.basename(tfmdata.name)
423 local encfile = features.reencode
424 local pfbfile = features.pfbfile
425 local bitmap = features.bitmap
426 if not encfile then
427 return
428 end
429 local pfbfile = pfbfile or outfiles[tfmfile]
430
431 if pfbfile == nil then
432 if bitmap then
433 pfbfile = false
434 elseif type(pfbfile) ~= "string" then
435 pfbfile = tfmfile
436 end
437 if type(pfbfile) == "string" then
438 pfbfile = addsuffix(pfbfile,"pfb")
439
440 report_tfm("using type1 shapes from %a for %a",pfbfile,tfmfile)
441 else
442 report_tfm("using bitmap shapes for %a",tfmfile)
443 pfbfile = false
444 end
445 outfiles[tfmfile] = pfbfile
446 end
447
448 local encoding = false
449 local vector = false
450 local hash = false
451 if type(pfbfile) == "string" then
452 local pfb = constructors.handlers.pfb
453 if pfb and pfb.loadvector then
454 local v, e = pfb.loadvector(pfbfile)
455 if v then
456 vector = v
457 end
458 if e then
459 encoding = e
460 end
461 end
462 end
463 if type(encfile) == "string" and encfile ~= "auto" then
464 encoding = fonts.encodings.load(addsuffix(encfile,"enc"))
465 if encoding then
466 if not vector then
467 hash = encoding.hash
468 end
469 encoding = encoding.vector
470
471
472
473 end
474 end
475
476 if not encoding then
477 report_tfm("bad encoding for %a, quitting",tfmfile)
478 return
479 end
480 local unicoding = fonts.encodings.agl and fonts.encodings.agl.unicodes
481 local virtualid = tfmcache[tfmdata]
482 local tfmdata = table.copy(tfmdata)
483 local characters = { }
484 local originals = tfmdata.characters
485 local indices = { }
486
487 local private = tfmdata.privateoffset or constructors.privateoffset
488 local reported = encdone[tfmfile][encfile]
489
490
491
492
493
494
495
496
497
498 for k, v in next, originals do
499 v.order = k
500 end
501
502
503 local backmap = vector and table.swapped(vector) or hash
504 local done = { }
505
506 for tfmindex, name in sortedhash(encoding) do
507 local original = originals[tfmindex]
508 if original then
509 local unicode = unicoding[name]
510 if unicode then
511 original.unicode = unicode
512 else
513 unicode = private
514 private = private + 1
515 if trace_defining and not reported then
516 report_tfm("glyph %a in font %a with encoding %a gets unicode %U",name,tfmfile,encfile,unicode)
517 end
518 end
519 characters[unicode] = original
520 indices[tfmindex] = unicode
521 original.name = name
522 if backmap then
523 original.index = backmap[name]
524 else
525
526 original.commands = slotcommand[tfmindex]
527
528 original.index = tfmindex
529 end
530 done[name] = true
531 elseif not done[name] then
532 report_tfm("bad index %a in font %a with name %a",tfmindex,tfmfile,name)
533 end
534 end
535
536 encdone[tfmfile][encfile] = true
537
538
539
540 for k, v in next, characters do
541 local kerns = v.kerns
542 if kerns then
543 local t = { }
544 for k, v in next, kerns do
545 local i = indices[k]
546 if i then
547 t[i] = v
548 end
549 end
550 v.kerns = next(t) and t or nil
551 end
552 local ligatures = v.ligatures
553 if ligatures then
554 local t = { }
555 for k, v in next, ligatures do
556 local i = indices[k]
557 if i then
558 t[i] = v
559 v.char = indices[v.char]
560 end
561 end
562 v.ligatures = next(t) and t or nil
563 end
564 local parts = v.parts
565 if parts then
566 local t = { }
567 for i=1,#parts do
568 local p = parts[i]
569 local g = p.glyph
570 t[i] = {
571 glyph = indices[g] or g,
572 extender = p.extender,
573 }
574 end
575 v.parts = t
576 end
577 local next = v.next
578 if next then
579 v.next = indices[next]
580 end
581 end
582
583
584
585
586 tfmdata.characters = characters
587 tfmdata.fullname = tfmdata.fullname or tfmdata.name
588 tfmdata.filename = pfbfile or tfmdata.name
589 tfmdata.psname = file.nameonly(tfmdata.filename)
590 tfmdata.format = "type1"
591
592 if bitmap == "outline" or bitmap == "potrace" then
593 tfmdata.potraced = true
594 bitmap = "pk"
595 elseif bitmap == "cff" then
596 tfmdata.potraced = true
597 tfmdata.format = "pkcff"
598 bitmap = false
599 else
600 tfmdata.filename = addsuffix(tfmdata.filename,"pfb")
601 end
602
603 tfmdata.usedbitmap = bitmap and virtualid
604 tfmdata.private = private
605 tfmdata.bitmapped = bitmap and true or false
606
607 return tfmdata
608 end
609
610end
611
612
613
614
615
616do
617
618 local everywhere = { ["*"] = { ["*"] = true } }
619 local noflags = { false, false, false, false }
620
621 local function enhance_normalize_features(data)
622 local ligatures = setmetatableindex("table")
623 local kerns = setmetatableindex("table")
624 local characters = data.characters
625 for u, c in next, characters do
626 local l = c.ligatures
627 local k = c.kerns
628 if l then
629 ligatures[u] = l
630 for u, v in next, l do
631 l[u] = { ligature = v.char }
632 end
633 c.ligatures = nil
634 end
635 if k then
636 kerns[u] = k
637 for u, v in next, k do
638 k[u] = v
639 end
640 c.kerns = nil
641 end
642 end
643
644 for u, l in next, ligatures do
645 for k, v in next, l do
646 local vl = v.ligature
647 local dl = ligatures[vl]
648 if dl then
649 for kk, vv in next, dl do
650 v[kk] = vv
651 end
652 end
653 end
654 end
655
656 local features = {
657 gpos = { },
658 gsub = { },
659 }
660 local sequences = {
661
662 }
663 if next(ligatures) then
664 features.gsub.liga = everywhere
665 data.properties.hasligatures = true
666 sequences[#sequences+1] = {
667 features = {
668 liga = everywhere,
669 },
670 flags = noflags,
671 name = "s_s_0",
672 nofsteps = 1,
673 order = { "liga" },
674 type = "gsub_ligature",
675 steps = {
676 {
677 coverage = ligatures,
678 },
679 },
680 }
681 end
682 if next(kerns) then
683 features.gpos.kern = everywhere
684 data.properties.haskerns = true
685 sequences[#sequences+1] = {
686 features = {
687 kern = everywhere,
688 },
689 flags = noflags,
690 name = "p_s_0",
691 nofsteps = 1,
692 order = { "kern" },
693 type = "gpos_pair",
694 steps = {
695 {
696 format = "kern",
697 coverage = kerns,
698 },
699 },
700 }
701 end
702 data.resources.features = features
703 data.resources.sequences = sequences
704 data.shared.resources = data.shared.resources or resources
705 end
706
707 registertfmenhancer("normalize features", enhance_normalize_features)
708 registertfmenhancer("check extra features", otfenhancers.enhance)
709
710end
711
712
713
714registertfmfeature {
715 name = "mode",
716 description = "mode",
717 initializers = {
718 base = otf.modeinitializer,
719 node = otf.modeinitializer,
720 }
721}
722
723registertfmfeature {
724 name = "features",
725 description = "features",
726 default = true,
727 initializers = {
728 base = otf.basemodeinitializer,
729 node = otf.nodemodeinitializer,
730 },
731 processors = {
732 node = otf.featuresprocessor,
733 }
734}
735 |