-
Notifications
You must be signed in to change notification settings - Fork 3
/
RasterInfo.m4
2918 lines (2478 loc) · 82.6 KB
/
RasterInfo.m4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
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
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
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
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="2.5">
<!--
Copyright (c) 2015, Werner Lemberg ([email protected]),
with Reserved Font Name `RasterInfo'.
This Font Software is licensed under the SIL Open Font License,
Version 1.1. The license text is available with a FAQ at
`https://opensource.org/licenses/OFL-1.1'.
-->
divert(`-1')
This file is a template for the GNU M4 macro processor. Other M4
implementations probably won't work, since we need the extended GNU M4
capabilities of the `define' and `patsubst' built-in functions. The used
version for development was 1.4.17.
Create the output file `RasterInfo.xml' with
m4 < RasterInfo.m4 > RasterInfo.xml
which in turn can be converted to a TrueType font `RasterInfo.ttf' with the
TrueType assembler from the `fonttools' package. The used ttx version for
development was 3.15.2.
ttx RasterInfo.xml
We use { and } as M4 quote characters.
changequote({, })
The `RasterInfo' TrueType font
==============================
This file defines a TrueType font called `RasterInfo.ttf'. It contains two
sets of characters mapped to the character codes: 0x30-0x3b (i.e.,
digits 0-9, `:', and `;') and 0x23-0x25 (`#', `$', and `%'). Both a
Macintosh 1/0 and a Microsoft 4/3 cmap are present.
If a TrueType bytecode interpreter is active, the characters of the first
set return various information on the rasterizer, using output of the
`GETINFO' bytecode instruction.
code glyph name description
--------------------------------------------------------------------
0x30 PPEM the current PPEM value
0x31 ScalerVersion the hinting engine version
0x32 IsRotated 1 if rotation is on
0x33 IsStretched 1 if stretching is on
0x34 GrayscaleRendering 1 if grayscale rendering
0x35 CleartypeEnabled 1 if ClearType enabled
0x36 CleartypeCompatibleWidths 1 if ClearType's
compatible width mode is on
0x37 CleartypeVerticalLCD 1 if ClearType uses
vertical LCD subpixels
0x38 CleartypeBGRRendering 1 if ClearType uses BGR
rendering instead of RGB
0x39 CleartypeSubpixelPositioning 1 if ClearType uses
subpixel positioning
0x3a CleartypeSymmetricalSmoothing 1 if ClearType's
symmetrical smoothing is on
0x3b CleartypeGrayRendering 1 if Gray ClearType is on
The `PPEM' glyph displays 1 to 5 digits, with the advance width properly
adjusted. The `ScalerVersion' glyph displays either one or two digits
(again with the advance width properly adjusted); see below for more.
All other characters display either `1' or `0', depending on whether the
described feature is active or not.
If any of the above characters is viewed without activated TrueType bytecode
hinting, one or more `E' glyphs (representing `Error') are shown.
The characters of the second set help find more information on the LCD
rendering and how it gets filtered. They are also useful to set up a
correct value for display gamma correction. Essentially, the characters
consist of slightly slanted zebra patterns; you have to view these glyphs at
a size of 96ppem (to be more precise, multiples of 24ppem like 72ppem will
work also, but not as good as 96ppem).
code glyph name stripe thickness distance between stripes
----------------------------------------------------------------
0x23 Black6White6 6 units 6 units
0x24 Black5White7 5 units 7 units
0x25 Black7White5 7 units 5 units
The scaler version
------------------
[This section is taken from FreeType's `ftttdrv.h' header file.]
Depending on the graphics framework, Microsoft uses different bytecode and
rendering engines. As a consequence, the version numbers returned by a call
to the `GETINFO' bytecode instruction are more convoluted than desired.
Here are two tables that try to shed some light on the possible values for
the (MS) rasterizer engine, together with the additional features introduced
by it.
GETINFO framework version feature
---------------------------------------------------------------------
[1 Macintosh System 6]
[2 Macintosh System 7]
3 GDI (Win 3.1), v1.0 16-bit, first version
TrueImage
[4 KanjiTalk 6.1 on the Mac]
33 GDI (Win NT 3.1), v1.5 32-bit
HP Laserjet
34 GDI (Win 95) v1.6 font smoothing,
new SCANTYPE instruction
35 GDI (Win 98/2000) v1.7 (UN)SCALED_COMPONENT_OFFSET
bits in composite glyphs
36 MGDI (Win CE 2) v1.6+ classic ClearType
37 GDI (XP and later), v1.8 ClearType
GDI+ old (before Vista)
38 GDI+ old (Vista, Win 7), v1.9 subpixel ClearType,
WPF Y-direction ClearType,
additional error checking
39 DWrite (before Win 8) v2.0 subpixel ClearType flags
in GETINFO instruction,
bug fixes
40 GDI+ (after Win 7), v2.1 Y-direction ClearType flag
DWrite (Win 8) in GETINFO instruction,
Gray ClearType
The `version' field gives a rough orientation only, since some applications
provided certain features much earlier (as an example, Microsoft Reader used
subpixel and Y-direction ClearType already in Windows 2000). Similarly,
updates to a given framework might include improved hinting support.
version sampling rendering comment
x y x y
--------------------------------------------------------------
v1.0 normal normal B/W B/W bi-level
v1.6 high high gray gray grayscale
v1.8 high normal color-filter B/W (GDI) ClearType
v1.9 high high color-filter gray Color ClearType
v2.1 high normal gray B/W Gray ClearType
v2.1 high high gray gray Gray ClearType
Color and Gray ClearType are the two available variants of `Y-direction
ClearType', meaning grayscale rasterization along the Y-direction; the name
used in the TrueType specification for this feature is `symmetric
smoothing'. `Classic ClearType' is the original algorithm used before
introducing a modified version in Win~XP. Another name for v1.6's grayscale
rendering is `font smoothing', and `Color ClearType' is sometimes also
called `DWrite ClearType'. To differentiate between today's Color ClearType
and the earlier ClearType variant with B/W rendering along the vertical
axis, the latter is sometimes called `GDI ClearType'.
`Normal' and `high' sampling describe the (virtual) resolution to access the
rasterized outline after the hinting process. `Normal' means 1 sample per
grid line (i.e., B/W). In the current Microsoft implementation, `high'
means an extra virtual resolution of 16x16 (or 16x1) grid lines per pixel
for bytecode instructions like `MIRP'. After hinting, these 16 grid lines
are mapped to 6x5 (or 6x1) grid lines for color filtering if Color ClearType
is activated.
Note that `Gray ClearType' is essentially the same as v1.6's grayscale
rendering. However, the GETINFO instruction handles it differently: v1.6
returns bit~12 (hinting for grayscale), while v2.1 returns bits~13 (hinting
for ClearType), 18 (symmetrical smoothing), and~19 (Gray ClearType). Also,
this mode respects bits 2 and~3 for the version~1 gasp table exclusively
(like Color ClearType), while v1.6 only respects the values of version~0
(bits 0 and~1).
FreeType doesn't provide all capabilities of the most recent ClearType
incarnation, thus its subpixel support is tagged as version~38.
The info glyph shape
--------------------
We construct a digit glyph as in the sketch below.
11111
012345678901234
30 +---------+ 30 12
29 + 6 + 29 11
28 ++---------++ 28 10
27 + + + + 27 9
26 | | | | 26
25 | | | | 25
24 | | | | 24 2 3
23 | | | | 23 +---------+
22 |4| |5| 22 1 + + 4
21 | | | | 21 +---------+
20 | | | | 20 0 5
19 | | | | 19
18 | | | | 18
17 + + + + 17 8 4
16 ++---------++ 16 7 +
15 + 3 + 15 6 3 + + 5
14 ++---------++ 14 5 | |
13 + + + + 13 4 | |
12 | | | | 12 | |
11 | | | | 11 | |
10 | | | | 10 | |
9 | | | | 9 | |
8 |1| |2| 8 | |
7 | | | | 7 | |
6 | | | | 6 | |
5 | | | | 5 2 + + 0
4 | | | | 4 +
3 + + + + 3 3 1
2 ++---------++ 2 2
1 + 0 + 1 1
0 +---------+ 0 0
012345678901234
11111
size: (lsb + 14 + rsb) x 30
horizontal element points: 0: ( 1, 0)
1: ( 0, 1)
2: ( 1, 2)
3: (11, 2)
4: (12, 1)
5: (11, 0)
vertical element points: 0: (2, 1)
1: (1, 0)
2: (0, 1)
3: (0, 11)
4: (1, 12)
5: (2, 11)
element offsets: 0: horz ( 1, 0)
1: vert ( 0, 2)
2: vert (12, 2)
3: horz ( 1,14)
4: vert ( 0,16)
5: vert (12,16)
6: horz ( 1,28)
In the macros below, generic values are used for the glyph dimensions so
that they can be easily modified.
To display correct digit shapes depending on GETINFO information, this must
be controlled directly within the TrueType bytecode. However, bytecode
can't create new outlines or points, it can only move points. The solution
to this problem implemented here is to make the digit glyph consist of
solitary points (this is, one-point contours ignored by the rendering
engine) that indicate the vertical positions where the outlines will be
moved to. Additionally, we create seven six-point outlines that have
correct x values but zero y values (and are thus ignored, too). If, for
example, we have to render element 1, the bytecode aligns the points of the
first six-point contour vertically to the corresponding placeholder points.
Since we only shift points vertically, the following placeholder points are
sufficient (referring to the y coordinates; the x coordinate is not of
interest).
0-3, 13-17, 27-30 (4 + 5 + 4 = 13 points)
In the above sketch of the complete glyph, placeholder point indices are
shown at the very right.
Subpixel positioning and advance width
--------------------------------------
The Windows bytecode engine doesn't allow modification of the advance width
if subpixel hinting is active. For this reason, the previously mentioned
adjustment of the shown number of digits per glyph and its advance width is
enabled for non-subpixel hinting only.
The LCD test glyph shape
------------------------
This is the shape for `Black6White6', with 32 stripes.
3 3 3 3 3
1 1 2 3 3 6 6 7 7 8
2 8 4 0 6 0 6 2 8 4
384 ++ ++ ++ .... ++ ++ ++
372 // // // // // //
360 // // // // // //
. .. .. .. .. .. ..
. 0 1 2 29 30 31
. .. .. .. .. .. ..
18 // // // // // //
12 // // // // // //
6 // // // // // //
0 ++ ++ ++ .... ++ ++ ++
0 6 1 1 2 3 3 3 3 3
2 8 4 4 5 6 6 7
8 4 0 6 2
For `Black5White7' and `Black7White5' the interval series is
0, 5, 12, 17, 24, ...
and
0, 7, 12, 19, 24, ... ,
respectively. Value 384 gets then mapped to the number of units per EM.
Naming conventions
------------------
The following conventions for names are used.
. M4 macros, constants, and variables: UPPERCASE_WITH_UNDERSCORES.
. Bytecode functions: CamelCase.
. Bytecode Storage Area locations: lowercase_with_underscores.
. Bytecode Control Value Table (CVT) indices: Mixed_Case_With_Underscores.
. Placeholder points: P_<point index> (example: `P_4').
. Element points: P_<element index>_<nth point>. For example, the fourth
point in element 2 is `P_1_3' (since both indices and elements start
with value 0).
. For positioning subglyphs and adjusting the advance width we need
alignment points: `P_0' (this is the first placeholder point), `P_00',
`P_000', `P_0000', `P_00000', and `P_000000'.
Note that point names refer to the `digit' glyph, not to the glyph indices
in the composite glyphs.
Global setup
------------
The font version.
define({VERSION}, 1.02)
We use 2048 units per EM.
define({UPEM}, 2048)
Similar to CJK characters, we set the descender of the zebra patterns to 20%
of the total height.
define({ZEBRA_DESCENDER}, UPEM / 5)
The thickness (which must be an even value because we need its half also)
and the length of an element.
define({THICKNESS}, 2)
define({LENGTH}, 12)
The left and right side bearings of the digit glyph.
define({LSB}, THICKNESS)
define({RSB}, THICKNESS)
The width, height, and advance width of the digit glyph are derived as
follows.
define({WIDTH},
eval(THICKNESS / 2 + LENGTH + THICKNESS / 2))
define({HEIGHT},
eval(THICKNESS + LENGTH + THICKNESS + LENGTH + THICKNESS))
define({ADV_WIDTH},
eval(LSB + WIDTH + RSB))
For bytecode hinting we also need the horizontal and vertical width of the
two digit `holes' (as shown in digit `8').
define({H_COUNTER},
eval(WIDTH - THICKNESS))
define({V_COUNTER},
eval(LENGTH))
We scale everything to font units using a heuristic factor.
define({SCALE}, 49)
Auxiliaries
-----------
To control the emitted whitespace while having a nicely vertical flow both
in the definition and the use of the M4 macros, we set up some auxiliary
macros.
We start with a loop macro that applies an iterator macro `var' to `stmt',
where `var' is incrementally assigned a number from a given number range
[from;to].
The macro trick using two macros for this definition is described in the GNU
M4 info manual in section `Building macros with macros'.
# FORLOOP(var, from, to, stmt)
define({FORLOOP},
{ifelse(eval({($2) <= ($3)}),
1,
{pushdef({$1})_$0({$1},
eval({$2}),
eval({$3}),
{$4})popdef({$1})})})
define({_FORLOOP},
{define({$1},
{$2})$4{}ifelse({$2},
{$3},
{},
{$0({$1},
incr({$2}),
{$3},
{$4})})})
This macro emits a newline.
define({NL}, {
})
The next one returns its arguments concatenated, doing recursion. We use
this to avoid unwanted addition of spaces to the output while being able to
use formatted input (in particular indentation).
define({CONCAT},
{ifelse({$#}, 0, {},
{$#}, 1, {$1{}},
{$1{}$0(shift($@))})})
Sometimes, we want to mention an already defined M4 macro in the explanatory
text. To avoid side effects it would be best to suppress expansion by
quoting the macro name in such cases. However, we no longer use ` and ' as
the M4 quotes but { and }, which look ugly if used for quoting in plain
text. We thus go a different route by introducing `sdefine', a variant of
the `define' built-in, which checks the number of arguments, doing nothing
if there aren't any. In other words, writing `FOO' expands to the string
`FOO' for a safely defined, argumentless macro `FOO', and we have to
explicitly append `()' to the macro name to make M4 expand it.
As an exception to our naming convention, this macro is all lowercase to
make it resemble the `define' built-in.
define({sdefine},
{_$0({$1},
{$2},
{$}{#},
{$}{0})})
define({_sdefine},
{define({$1},
{ifelse({$3},
0,
{{$4}},
{$2})})})
Elements
--------
We define two macros to set up the point positions of a horizontal and
vertical element prototype, shown on the right side in the sketch above.
Both macros take a horizontal and a vertical offset as parameters. Macros
`S', `ELEM_START', and `ELEM_END' are defined in various ways below to help
compute auxiliary variables, and to emit the necessary XML data.
sdefine({H_ELEM},
{CONCAT({ELEM_START()},
{S(eval(THICKNESS / 2 + $1),
eval($2))},
{S(eval($1),
eval(THICKNESS / 2 + $2))},
{S(eval(THICKNESS / 2 + $1),
eval(THICKNESS + $2))},
{S(eval(LENGTH - THICKNESS / 2 + $1),
eval(THICKNESS + $2))},
{S(eval(LENGTH + $1),
eval(THICKNESS / 2 + $2))},
{S(eval(LENGTH - THICKNESS / 2 + $1),
eval($2))},
{ELEM_END()})})
sdefine({V_ELEM},
{CONCAT({ELEM_START()},
{S(eval(THICKNESS + $1),
eval(THICKNESS / 2 + $2))},
{S(eval(THICKNESS / 2 + $1),
eval($2))},
{S(eval($1),
eval(THICKNESS / 2 + $2))},
{S(eval($1),
eval(LENGTH - THICKNESS / 2 + $2))},
{S(eval(THICKNESS / 2 + $1),
eval(LENGTH + $2))},
{S(eval(THICKNESS + $1),
eval(LENGTH - THICKNESS / 2 + $2))},
{ELEM_END()})})
For simplicity, we assume that both the horizontal and vertical elements
have the same number of points. We store this value in `ELEM_SIZE', which
gets computed incrementally with the following four macro definitions and
one macro call.
define({ELEM_START})
define({ELEM_END})
define({ELEM_SIZE}, 0)
sdefine({S},
{define({ELEM_SIZE},
incr(ELEM_SIZE))})
H_ELEM(0, 0)
Our digit glyph consists of `NUM_ELEM' elements; its value gets
incrementally computed below.
define({NUM_ELEM}, 0)
Call this macro to add an element; the element name is set to `ELEM_<i>',
with <i> an integer starting with zero.
sdefine({ELEMENT},
{CONCAT({sdefine(format({{ELEM_%d}},
NUM_ELEM),
{$1})},
{define({NUM_ELEM},
incr(NUM_ELEM))})})
And here are the elements.
ELEMENT({H_ELEM(eval(LSB + THICKNESS / 2),
eval(0))})
ELEMENT({V_ELEM(eval(LSB),
eval(THICKNESS))})
ELEMENT({V_ELEM(eval(LSB + LENGTH),
eval(THICKNESS))})
ELEMENT({H_ELEM(eval(LSB + THICKNESS / 2),
eval(LENGTH + THICKNESS))})
ELEMENT({V_ELEM(eval(LSB),
eval(LENGTH + 2 * THICKNESS))})
ELEMENT({V_ELEM(eval(LSB + LENGTH),
eval(LENGTH + 2 * THICKNESS))})
ELEMENT({H_ELEM(eval(LSB + THICKNESS / 2),
eval(2 * LENGTH + 2 * THICKNESS))})
The DIGIT macro
---------------
A digit consists of all elements. The macro below calls elements 0, 1, 2,
..., NUM_ELEM-1 sequentially.
sdefine({DIGIT},
{FORLOOP({i_},
0,
eval(NUM_ELEM - 1),
{indir(ELEM_{}i_, {})})})
The formatted output
--------------------
To control what our `DIGIT' macro emits, we have to define and redefine
lower-level macros.
Placeholder points
..................
First, we set up the placeholder points. As discussed above, we need one
placeholder point per vertical position. To get them, we iterate over all
points and fill a (simulated) point array, using the vertical coordinate as
an index. Since we use the horizontal position of `P_0' as an alignment
point also, we set the x coordinate to zero.
Here are the two macros to set and access elements of the point array.
Using `defn' in `POINT', its argument is not further expanded at reading
time.
sdefine({POINT},
{defn(format({{point[%d]}},
{$1}))})
sdefine({POINT_SET},
{define(format({{point[%d]}},
{$1}),
{$2})})
define({ELEM_START})
define({ELEM_END})
sdefine({S},
{POINT_SET($2,
CONCAT({ <contour>NL},
{format({ <pt x="%d" y="%d" on="1"/>NL},
0,
eval($2 * SCALE))},
{ </contour>NL}))})
Now call our digit macro to fill the array.
DIGIT()
The next step is to compute the number of placeholder points. We again use
a loop to iterate over the index range [0;HEIGHT] and check whether the
`POINT' macro returns a non-empty value. We also use the loop to assign the
placeholder point names `P_<i>' for such cases (with `<i>' the corresponding
index).
define({NUM_PLACEHOLDERS}, 0)
FORLOOP({i_},
0,
HEIGHT,
{ifelse(POINT(i_),
{},
{},
{CONCAT({define(format({{P_%d}},
NUM_PLACEHOLDERS),
NUM_PLACEHOLDERS)},
{define({NUM_PLACEHOLDERS},
incr(NUM_PLACEHOLDERS))})})})
What follows is a macro that emits the placeholder points (collected with
the first call of the digit macro above), to be called later on.
sdefine({PLACEHOLDERS},
{FORLOOP({i_},
0,
HEIGHT,
{ifelse(POINT(i_),
{},
{},
{POINT(i_)})})})
Alignment points
................
We use composite glyphs to construct a number from up to `NUM_SUBGLYPHS'
digit glyphs.
define({NUM_SUBGLYPHS}, 5)
In the composite glyphs below, subglyphs should be horizontally stacked
after the points have been moved to their final positions using bytecode.
The OpenType standard provides two possibilities to position subglyphs,
either using coordinate offsets (to be set up before hinting) or point
indices. Since we heavily adjust the advance width within the bytecode, we
have to use the latter.
The natural way would be to start with subglyphs 0 and 1, aligning a point
of the first subglyph with a point of the second subglyph, iteratively
repeating this to align a point from subglyph <i> with a point from subglyph
<i+1>. After subglyph <i> gets added, the number of total points is
increased by the number of points of subglyph <i>. Due to a bug in FreeType
versions earlier than 2.6.0, however, using point index values in the ranges
[128;255] and [32768;65535] fail. For this very reason we define all
necessary alignment points directly in our digit glyph, aligning subglyphs
1, 2, ..., to subglyph 0 instead, thus circumventing the bug.
Here we set up `TEMP' to incrementally contain the strings `P_00', `P_000',
etc. (`P_0' is already defined). These strings are then used as macro
names, expanding to point indices as expected. We also incrementally set up
the `ALIGNMENTS' macro to emit the corresponding XML code.
define({TEMP},
{P_0})
sdefine({ALIGNMENTS})
FORLOOP({i_},
1,
eval(NUM_SUBGLYPHS),
{CONCAT({define({TEMP},
defn({TEMP})0)},
{define(TEMP,
eval(NUM_PLACEHOLDERS + NUM_ELEM * ELEM_SIZE + i_ - 1))},
{sdefine({ALIGNMENTS},
CONCAT(defn(ALIGNMENTS),
{ <contour>NL},
{ <pt x="}eval(i_ * SCALE * ADV_WIDTH){" y="0" on="1"/>NL},
{ </contour>NL}))})})
undefine({TEMP})
Element points
..............
Within the loop of the `DIGIT' macro, counter variable `i_' is the current
element index. In `ELEM_START', we initialize (or reset) a counter `j_'
that gets incremented in macro `S'. These values are used to define the
names of element points.
sdefine({ELEM_START},
{define({j_}, 0)})
define({ELEM_END})
define({S},
{CONCAT({define(format({{P_%d_%d}},
i_,
j_),
eval(NUM_PLACEHOLDERS + i_ * ELEM_SIZE + j_))},
{define({j_},
incr(j_))})})
DIGIT()
Number of points
................
We are now able to compute the number of points of the digit glyph.
define({NUM_POINTS},
eval(NUM_PLACEHOLDERS + NUM_ELEM * ELEM_SIZE + NUM_SUBGLYPHS))
The outlines
............
By default, we display an `E' shape, indicating `error'; this gets displayed
if there is no bytecode interpreter. To do that we have to redefine
elements 2 and 5, changing its vertical coordinate values to zero.
Similarly, we need different versions of the low-level output macros since
we now emit one six-point contour per element instead of six single points.
sdefine({V_ELEM0},
{CONCAT({ELEM_START()},
{S(eval(THICKNESS + $1), 0)},
{S(eval(THICKNESS / 2 + $1), 0)},
{S(eval( $1), 0)},
{S(eval( $1), 0)},
{S(eval(THICKNESS / 2 + $1), 0)},
{S(eval(THICKNESS + $1), 0)},
{ELEM_END()})})
sdefine({ELEM_2},
{V_ELEM0(eval(LSB + LENGTH), 0)})
sdefine({ELEM_5},
{V_ELEM0(eval(LSB + LENGTH), 0)})
define({S},
{format({ <pt x="%d" y="%d" on="1"/>NL},
eval($1 * SCALE),
eval($2 * SCALE))})
define({ELEM_START},
{ <contour>NL})
define({ELEM_END},
{ </contour>NL})
We call the `DIGIT' macro later on.
Scaled variables
................
Finally, a set of convenience macros that give scaled values. We also need
a heuristic constant `EXTRA_HEIGHT' to increase the ascent so that its value
harmonizes with other fonts.
define({sTHICKNESS}, eval(SCALE * THICKNESS))
define({sLENGTH}, eval(SCALE * LENGTH))
define({sLSB}, eval(SCALE * LSB))
define({sRSB}, eval(SCALE * RSB))
define({sWIDTH}, eval(SCALE * WIDTH))
define({sHEIGHT}, eval(SCALE * HEIGHT))
define({sADV_WIDTH}, eval(SCALE * ADV_WIDTH))
define({sH_COUNTER}, eval(SCALE * H_COUNTER))
define({sV_COUNTER}, eval(SCALE * V_COUNTER))
define({EXTRA_HEIGHT}, 4)
define({sEXTRA_HEIGHT}, eval(SCALE * EXTRA_HEIGHT))
The ZEBRA macros
----------------
The zebra patterns consist of a series of stripes. The macros below call
elements 0, 1, 2, ..., NUM_STRIPES.
define({NUM_STRIPES}, 24)
define({STRIPE_OFFSET}, 12)
define({STRIPE_HEIGHT}, eval(NUM_STRIPES * STRIPE_OFFSET))
define({BLACK_WIDTH_1}, 6)
define({BLACK_WIDTH_2}, 5)
define({BLACK_WIDTH_3}, 7)
sdefine({ZEBRA_1},
{FORLOOP({i_},
0,
eval(NUM_STRIPES - 1),
{STRIPE(BLACK_WIDTH_1, i_)})})
sdefine({ZEBRA_2},
{FORLOOP({i_},
0,
eval(NUM_STRIPES - 1),
{STRIPE(BLACK_WIDTH_2, i_)})})
sdefine({ZEBRA_3},
{FORLOOP({i_},
0,
eval(NUM_STRIPES - 1),
{STRIPE(BLACK_WIDTH_3, i_)})})
We need a small auxiliary macro to convert the coordinate values used in the
sketch above to font units.
define({FU}, {eval(($1 * UPEM + STRIPE_HEIGHT / 2) / STRIPE_HEIGHT)})
Now the definition of the `STRIPE' macro is straightforward.
define({STRIPE},
{CONCAT({ <contour>NL},
{format({ <pt x="%d" y="%d" on="1"/>NL},
FU({eval(STRIPE_OFFSET * $2)}),
eval(-ZEBRA_DESCENDER))},
{format({ <pt x="%d" y="%d" on="1"/>NL},
FU({eval(STRIPE_OFFSET * ($2 + 1))}),
eval(FU({eval(STRIPE_HEIGHT)}) - ZEBRA_DESCENDER))},
{format({ <pt x="%d" y="%d" on="1"/>NL},
FU({eval($1 + STRIPE_OFFSET * ($2 + 1))}),
eval(FU({eval(STRIPE_HEIGHT)}) - ZEBRA_DESCENDER))},
{format({ <pt x="%d" y="%d" on="1"/>NL},
FU({eval($1 + STRIPE_OFFSET * $2)}),
eval(-ZEBRA_DESCENDER))},
{ </contour>NL})})
Bytecode support
----------------
TrueType's bytecode instructions resemble assembler code, not having any
labels, variable, or function names. However, M4 comes to a rescue, so we
add some macros that improve this situation.
Functions
.........
First of all, we need a counter that gets incremented with each call to
`FUNCTION'.
define({NUM_FUNCTIONS}, 0)
To allow forward references, we store bytecode function definitions as
macros in an array. A bytecode function's name also becomes a macro,
holding a sequential number. Later on, a call to the `FUNCTIONS' macro
emits the bytecode functions to the output. Since function numbers never
change, and we also don't access `NUM_FUNCTIONS' within the bytecode, no
special care is needed if an already defined label gets prematurely expanded
in backward references; this allows us to omit the quoting of `FUNCTION's
second argument.
We expect below that calls to `FUNCTION' are indented by 2 spaces, and that
the closing parenthesis is on a line of its own and indented by 2 spaces,
too.
sdefine({FUNCTION},
{CONCAT(define(format({function[%d]},
NUM_FUNCTIONS),
CONCAT({shift($*)})),
define({$1},
NUM_FUNCTIONS),
{define({NUM_FUNCTIONS},
incr(NUM_FUNCTIONS))})})
Contrary to the point array reading macro `POINT' we can't use `defn' this
time: We need another round of macro expansion to resolve forward
references. While outputting the function data, we add four leading spaces
to get nice XML formatting.
define({GET_FUNCTION},
{patsubst(indir(format({{function[%d]}},
{$1})),
{^},
{ })})
Within `FUNCTION', we use the default M4 comment style for comments; a call
to the `patsubst' builtin removes them later on (this is necessary since M4
retains comments within macros). The comments are mainly used to show the
stack after executing the bytecode function on the same line; such comments
start with prefix `s:', with the topmost stack element coming first.
sdefine({FUNCTIONS},
{patsubst(FORLOOP({i_},
0,
eval(NUM_FUNCTIONS - 1),
{CONCAT({ PUSH[ ]NL},
{ i_{}NL},
{ FDEF[ ]NL},
{ GET_FUNCTION(i_)},
{ENDF[ ]NL},
{NL})}),
{\(^ *#.*
\| *#.*$\| *$\)},
{})})
The bytecode storage area
.........................
Similar to functions, we need a counter of storage locations.
define({NUM_STORAGE_LOCATIONS}, 0)
However, we only need a mapping between a storage location name and its
index, making the associated macro quite simple.
sdefine({STORAGE},
{CONCAT(define({$1},
NUM_STORAGE_LOCATIONS),
{define({NUM_STORAGE_LOCATIONS},
incr(NUM_STORAGE_LOCATIONS))})})
And here the list of storage locations.
STORAGE({glyph_offset})
The Control Value Table (CVT)
.............................
The `CVT' macro puts the given value into the `cvt' table; it also defines a
mapping between a CVT name and its index. Because the values are located in
a separate SFNT table, no `maxp' entry is needed, so we define the macro
here and insert it below in the XML part (but before the bytecode tables so
that references are properly resolved to indices).
define({NUM_CVT_ENTRIES}, 0)
sdefine({CVT},
{CONCAT({format({<cv index="%d" value="%d"/>},
NUM_CVT_ENTRIES,
$2)},
{define({$1},
NUM_CVT_ENTRIES)},
{define({NUM_CVT_ENTRIES},
incr(NUM_CVT_ENTRIES))})})
The `fpgm' bytecode
-------------------
Since the `maxp' table needs the number of bytecode functions, it makes
sense to have the code in the M4 section.
Here is a simplified sketch of the digit glyph.
+- 6 -+
| |
4 5
| |
+- 3 -+
| |
1 2
| |
+- 0 -+
Digits are composed from elements, cf. the `DigitX' functions below.
0 012 456
1 2 5
2 01 3 56
3 0 23 56
4 2345
5 0 234 6
6 01234 6
7 2 56
8 0123456
9 0 23456
This table shows the element's outline mapping to placeholder indices (which
are in the range 0 to `NUM_PLACEHOLDERS - 1'), cf. the `ElementX' functions
below.
0 0 1 2 2 1 0
1 3 2 3 4 5 4
2 3 2 3 4 5 4
3 5 6 7 7 6 5
4 8 7 8 9 10 9
5 8 7 8 9 10 9
6 10 11 12 12 11 10
(For example, the first contour point of element 3 should be vertically
aligned to placeholder point 5, the second point to point 6, etc.)