1if not modules then modules = { } end modules ['font-imp-quality'] = {
2 version = 1.001,
3 comment = "companion to font-ini.mkiv and hand-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, tonumber = next, type, tonumber
12
13local fonts = fonts
14local utilities = utilities
15
16local handlers = fonts.handlers
17local otf = handlers.otf
18local afm = handlers.afm
19local registerotffeature = otf.features.register
20local registerafmfeature = afm.features.register
21
22local allocate = utilities.storage.allocate
23local getparameters = utilities.parsers.getparameters
24
25local implement = interfaces and interfaces.implement
26
27local trace_protrusion = false trackers.register("fonts.protrusion", function(v) trace_protrusion = v end)
28local trace_expansion = false trackers.register("fonts.expansion", function(v) trace_expansion = v end)
29
30local report_expansions = logs.reporter("fonts","expansions")
31local report_protrusions = logs.reporter("fonts","protrusions")
32
33
34
35
36
37local function get_class_and_vector(tfmdata,value,where)
38 local g_where = tfmdata.goodies and tfmdata.goodies[where]
39 local f_where = fonts[where]
40 local g_classes = g_where and g_where.classes
41 local f_classes = f_where and f_where.classes
42 local class = (g_classes and g_classes[value]) or (f_classes and f_classes[value])
43 if class then
44 local class_vector = class.vector
45 local g_vectors = g_where and g_where.vectors
46 local f_vectors = f_where and f_where.vectors
47 local vector = (g_vectors and g_vectors[class_vector]) or (f_vectors and f_vectors[class_vector])
48 return class, vector
49 end
50end
51
52
53
54
55
56local expansions = fonts.expansions or allocate()
57
58fonts.expansions = expansions
59
60local classes = expansions.classes or allocate()
61local vectors = expansions.vectors or allocate()
62
63expansions.classes = classes
64expansions.vectors = vectors
65
66classes.preset = {
67 stretch = 2,
68 shrink = 2,
69 step = .5,
70 factor = 1,
71}
72
73classes['quality'] = {
74 stretch = 2,
75 shrink = 2,
76 step = .5,
77 vector = 'default',
78 factor = 1,
79}
80
81vectors['default'] = {
82 [0x0041] = 0.5,
83 [0x0042] = 0.7,
84 [0x0043] = 0.7,
85 [0x0044] = 0.5,
86 [0x0045] = 0.7,
87 [0x0046] = 0.7,
88 [0x0047] = 0.5,
89 [0x0048] = 0.7,
90 [0x004B] = 0.7,
91 [0x004D] = 0.7,
92 [0x004E] = 0.7,
93 [0x004F] = 0.5,
94 [0x0050] = 0.7,
95 [0x0051] = 0.5,
96 [0x0052] = 0.7,
97 [0x0053] = 0.7,
98 [0x0055] = 0.7,
99 [0x0057] = 0.7,
100 [0x005A] = 0.7,
101 [0x0061] = 0.7,
102 [0x0062] = 0.7,
103 [0x0063] = 0.7,
104 [0x0064] = 0.7,
105 [0x0065] = 0.7,
106 [0x0067] = 0.7,
107 [0x0068] = 0.7,
108 [0x006B] = 0.7,
109 [0x006D] = 0.7,
110 [0x006E] = 0.7,
111 [0x006F] = 0.7,
112 [0x0070] = 0.7,
113 [0x0071] = 0.7,
114 [0x0073] = 0.7,
115 [0x0075] = 0.7,
116 [0x0077] = 0.7,
117 [0x007A] = 0.7,
118 [0x0032] = 0.7,
119 [0x0033] = 0.7,
120 [0x0036] = 0.7,
121 [0x0038] = 0.7,
122 [0x0039] = 0.7,
123}
124
125vectors['quality'] = vectors['default']
126
127
128
129
130local function initialize(tfmdata,value)
131 if value then
132 local class, vector = get_class_and_vector(tfmdata,value,"expansions")
133 if class then
134 if vector then
135 local stretch = class.stretch or 0
136 local shrink = class.shrink or 0
137 local step = class.step or 0
138 local factor = class.factor or 1
139 if trace_expansion then
140 report_expansions("setting class %a, vector %a, factor %a, stretch %a, shrink %a, step %a",
141 value,class.vector,factor,stretch,shrink,step)
142 end
143 tfmdata.parameters.expansion = {
144 stretch = 10 * stretch,
145 shrink = 10 * shrink,
146 step = 10 * step,
147 factor = factor,
148 class = class,
149 vector = vector,
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 elseif trace_expansion then
188 report_expansions("unknown vector %a in class %a",class.vector,value)
189 end
190 elseif trace_expansion then
191 report_expansions("unknown class %a",value)
192 end
193 end
194end
195
196local specification = {
197 name = "expansion",
198 description = "apply hz optimization",
199 initializers = {
200 base = initialize,
201 node = initialize,
202 }
203}
204
205registerotffeature(specification)
206registerafmfeature(specification)
207
208fonts.goodies.register("expansions", function(...) return fonts.goodies.report("expansions", trace_expansion, ...) end)
209
210implement {
211 name = "setupfontexpansion",
212 arguments = "2 strings",
213 actions = function(class,settings) getparameters(classes,class,'preset',settings) end
214}
215
216
217
218classes.math = {
219 stretch = 2,
220 shrink = 2,
221 step = .5,
222 vector = 'math',
223 factor = 1,
224}
225
226vectors.math = {
227 [0x002B] = { 4, 0 },
228 [0x2212] = { 4, 0 },
229 [0x003C] = { 8, 0 },
230 [0x003D] = { 8, 0 },
231 [0x003E] = { 8, 0 },
232 [0x002F] = { 2, 0 },
233 [0x0028] = { 2, 0 },
234 [0x0029] = { 2, 0 },
235 [0x005B] = { 2, 0 },
236 [0x005D] = { 2, 0 },
237}
238
239local function initialize(tfmdata,value)
240 if value then
241 local class, vector = get_class_and_vector(tfmdata,value,"expansions")
242 if class then
243 if vector then
244 local stretch = class.stretch or 0
245 local shrink = class.shrink or 0
246 local step = class.step or 0
247 local factor = class.factor or 1
248 if trace_expansion then
249 report_expansions("setting class %a, vector %a, factor %a, stretch %a, shrink %a, step %a",
250 value,class.vector,factor,stretch,shrink,step)
251 end
252 tfmdata.parameters.expansion = {
253 stretch = 10 * stretch,
254 shrink = 10 * shrink,
255 step = 10 * step,
256 factor = factor,
257 }
258 local characters = tfmdata.characters
259 for u, v in next, vector do
260 local chr = characters[u]
261 if type(v) == "table" then
262 local e = v[1]
263 local c = v[2] or 0
264 if e ~= 0 then
265 chr.expansion = e*factor
266 end
267 if c ~= 0 then
268 chr.compression = c*factor
269 end
270 elseif v ~= 0 then
271 chr.expansion = v*factor
272 end
273 end
274 elseif trace_expansion then
275 report_expansions("unknown vector %a in class %a",class.vector,value)
276 end
277 elseif trace_expansion then
278 report_expansions("unknown class %a",value)
279 end
280 end
281end
282
283registerotffeature {
284 name = "mathexpansion",
285 description = "apply hz optimization to math",
286 initializers = {
287 base = initialize,
288 node = initialize,
289 }
290}
291
292
293
294
295
296fonts.protrusions = allocate()
297local protrusions = fonts.protrusions
298
299protrusions.classes = allocate()
300protrusions.vectors = allocate()
301
302local classes = protrusions.classes
303local vectors = protrusions.vectors
304
305
306
307classes.preset = {
308 factor = 1,
309 left = 1,
310 right = 1,
311}
312
313classes['pure'] = { vector = 'pure', factor = 1 }
314classes['punctuation'] = { vector = 'punctuation', factor = 1 }
315classes['alpha'] = { vector = 'alpha', factor = 1 }
316classes['quality'] = { vector = 'quality', factor = 1 }
317
318vectors['pure'] = {
319
320 [0x002C] = { 0, 1 },
321 [0x002E] = { 0, 1 },
322 [0x003A] = { 0, 1 },
323 [0x003B] = { 0, 1 },
324 [0x002D] = { 0, 1 },
325 [0x00AD] = { 0, 1 },
326 [0x2013] = { 0, 0.50 },
327 [0x2014] = { 0, 0.33 },
328 [0x3001] = { 0, 1 },
329 [0x3002] = { 0, 1 },
330 [0x060C] = { 0, 1 },
331 [0x061B] = { 0, 1 },
332 [0x06D4] = { 0, 1 },
333
334}
335
336vectors['punctuation'] = {
337
338 [0x003F] = { 0, 0.20 },
339 [0x00BF] = { 0.20, 0 },
340 [0x0021] = { 0, 0.20 },
341 [0x00A1] = { 0.20, 0, },
342 [0x0028] = { 0.05, 0 },
343 [0x0029] = { 0, 0.05 },
344 [0x005B] = { 0.05, 0 },
345 [0x005D] = { 0, 0.05 },
346 [0x002C] = { 0, 0.70 },
347 [0x002E] = { 0, 0.70 },
348 [0x003A] = { 0, 0.50 },
349 [0x003B] = { 0, 0.50 },
350 [0x002D] = { 0, 0.70 },
351 [0x00AD] = { 0, 0.70 },
352 [0x2013] = { 0, 0.30 },
353 [0x2014] = { 0, 0.20 },
354 [0x060C] = { 0, 0.70 },
355 [0x061B] = { 0, 0.50 },
356 [0x06D4] = { 0, 0.70 },
357 [0x061F] = { 0, 0.20 },
358
359
360
361 [0x2039] = { 0.70, 0.70 },
362 [0x203A] = { 0.70, 0.70 },
363 [0x00AB] = { 0.50, 0.50 },
364 [0x00BB] = { 0.50, 0.50 },
365
366 [0x2018] = { 0.70, 0.70 },
367 [0x2019] = { 0, 0.70 },
368 [0x201A] = { 0.70, 0 },
369 [0x201B] = { 0.70, 0 },
370 [0x201C] = { 0.50, 0.50 },
371 [0x201D] = { 0, 0.50 },
372 [0x201E] = { 0.50, 0 },
373 [0x201F] = { 0.50, 0 },
374
375}
376
377vectors['alpha'] = {
378
379 [0x0041] = { .05, .05 },
380 [0x0046] = { 0, .05 },
381 [0x004A] = { .05, 0 },
382 [0x004B] = { 0, .05 },
383 [0x004C] = { 0, .05 },
384 [0x0054] = { .05, .05 },
385 [0x0056] = { .05, .05 },
386 [0x0057] = { .05, .05 },
387 [0x0058] = { .05, .05 },
388 [0x0059] = { .05, .05 },
389
390 [0x006B] = { 0, .05 },
391 [0x0072] = { 0, .05 },
392 [0x0074] = { 0, .05 },
393 [0x0076] = { .05, .05 },
394 [0x0077] = { .05, .05 },
395 [0x0078] = { .05, .05 },
396 [0x0079] = { .05, .05 },
397
398}
399
400vectors['quality'] = table.merged(
401 vectors['punctuation'],
402 vectors['alpha']
403)
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552local function initialize(tfmdata,value)
553 if value then
554
555
556
557
558
559 local class, vector = get_class_and_vector(tfmdata,value,"protrusions")
560 if class then
561 if vector then
562 local factor = class.factor or 1
563 local left = class.left or 1
564 local right = class.right or 1
565 if trace_protrusion then
566 report_protrusions("setting class %a, vector %a, factor %a, left %a, right %a",
567 value,class.vector,factor,left,right)
568 end
569 local data = characters.data
570 local lfactor = left * factor
571 local rfactor = right * factor
572 if trace_protrusion then
573 report_protrusions("left factor %0.3F, right factor %0.3F",lfactor,rfactor)
574 end
575 tfmdata.parameters.protrusion = {
576 factor = factor,
577 left = left,
578 right = right,
579 class = class,
580 vector = vector,
581 }
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624 elseif trace_protrusion then
625 report_protrusions("unknown vector %a in class %a",class.vector,value)
626 end
627 elseif trace_protrusion then
628 report_protrusions("unknown class %a",value)
629 end
630
631 end
632end
633
634local specification = {
635 name = "protrusion",
636 description = "l/r margin character protrusion",
637 initializers = {
638 base = initialize,
639 node = initialize,
640 }
641}
642
643registerotffeature(specification)
644registerafmfeature(specification)
645
646fonts.goodies.register("protrusions", function(...) return fonts.goodies.report("protrusions", trace_protrusion, ...) end)
647
648implement {
649 name = "setupfontprotrusion",
650 arguments = "2 strings",
651 actions = function(class,settings) getparameters(classes,class,'preset',settings) end
652}
653
654local function initialize(tfmdata,value)
655 local properties = tfmdata.properties
656 local parameters = tfmdata.parameters
657 if properties then
658 value = tonumber(value)
659 if value then
660 if value < 0 then
661 value = 0
662 elseif value > 10 then
663 report_expansions("threshold for %a @ %p limited to 10 pct",properties.fontname,parameters.size)
664 value = 10
665 end
666 if value > 5 then
667 report_expansions("threshold for %a @ %p exceeds 5 pct",properties.fontname,parameters.size)
668 end
669 end
670 properties.threshold = value or nil
671 end
672end
673
674local specification = {
675 name = "threshold",
676 description = "threshold for quality features",
677 initializers = {
678 base = initialize,
679 node = initialize,
680 }
681}
682
683registerotffeature(specification)
684registerafmfeature(specification)
685
686
687
688local function addquality(id)
689 local tfmdata = fonts.hashes.identifiers[id]
690 if tfmdata then
691 local expansion = tfmdata.parameters.expansion
692 if expansion then
693 local class = expansion.class
694 local vector = expansion.vector
695 if class and vector then
696 local stretch = class.stretch or 0
697 local shrink = class.shrink or 0
698 local step = class.step or 0
699 local factor = class.factor or 1
700 local data = characters and characters.data
701 for i, chr in next, tfmdata.characters do
702 local v = vector[i]
703 if data and not v then
704 local d = data[i]
705 if d then
706 local s = d.shcode
707 if not s then
708
709 elseif type(s) == "table" then
710 v = ((vector[s[1]] or 0) + (vector[s[#s]] or 0)) / 2
711 else
712 v = vector[s] or 0
713 end
714 end
715 end
716
717 if v then
718 if type(v) == "table" then
719 local e = v[1]
720 local c = v[2] or 0
721 if e ~= 0 then
722 chr.expansion = e * factor * 1000
723 end
724 if c ~= 0 then
725 chr.compression = c * factor * 1000
726 end
727 elseif v == 0 then
728
729 else
730 chr.expansion = v * factor * 1000
731 end
732 elseif factor ~= 1 then
733 chr.expansion = factor * 1000
734 end
735 end
736 end
737 end
738
739 local protrusion = tfmdata.parameters.protrusion
740 if protrusion then
741 local class = protrusion.class
742 local vector = protrusion.vector
743 if class and vector then
744 local factor = class.factor or 1
745 local left = class.left or 1
746 local right = class.right or 1
747 local data = characters.data
748 local lfactor = left * factor
749 local rfactor = right * factor
750 for i, chr in next, tfmdata.characters do
751 local v = vector[i]
752 local pl = nil
753 local pr = nil
754 if v then
755 pl = v[1]
756 pr = v[2]
757 else
758 local d = data[i]
759 if d then
760 local s = d.shcode
761 if not s then
762
763 elseif type(s) == "table" then
764 local vl = vector[s[1]]
765 local vr = vector[s[#s]]
766 if vl then pl = vl[1] end
767 if vr then pr = vr[2] end
768 else
769 v = vector[s]
770 if v then
771 pl = v[1]
772 pr = v[2]
773 end
774 end
775 end
776 end
777 if pl and pl ~= 0 then
778 pl = pl * lfactor * chr.width
779 if trace_protrusion then
780 report_protrusions("left -> %0.3F %C ",pl,i)
781 end
782 chr.leftprotrusion = pl
783 end
784 if pr and pr ~= 0 then
785 pr = pr * rfactor * chr.width
786 chr.rightprotrusion = pr
787 if trace_protrusion then
788 report_protrusions("right -> %0.3F %C",pr,i)
789 end
790 end
791 end
792 end
793 end
794
795 if expansion or protrusion then
796 font.addquality(id,tfmdata)
797 end
798 end
799end
800
801callback.register("quality_font",addquality)
802 |