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