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