-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathttf2pscid2.ps
More file actions
2103 lines (1969 loc) · 45.2 KB
/
ttf2pscid2.ps
File metadata and controls
2103 lines (1969 loc) · 45.2 KB
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
%!PS-Adobe-3.0
% TTF to CID-keyed font converter by Raymond Luckhurst, Scriptit Ltd, https://scriptit.uk
%
% Converts TTF to a Type 2 CIDFont with 2-byte Unicode CMap encoding, for embedding into PostScript.
%
% See https://github.com/scriptituk/ttf2pscid2/blob/master/README.md for Documentation.
%
% Based on Apple's TrueType Reference Manual
% see https://developer.apple.com/fonts/TrueType-Reference-Manual
% and https://www.microsoft.com/typography/otspec/otff.htm
%
% Conventions:
% r_ reader procs
% w_ writer procs
% d_ directory entry
% t_ table data
% $ variables
% std I/O
/$stdout (%stdout) (w) file def
/$stderr (%stderr) (w) file def
% temp
/cat { exch dup length 2 index length add string dup dup 5 2 roll copy length exch putinterval } def
/error { $stderr exch (ERROR ) exch cat (\n) cat writestring $stderr flushfile quit } def
% options
% NOTE (gs v9): octal-escape double quotes in all command-line string params, i.e. '"' --> '\042'
/psname dup where { pop pop }{ false def } ifelse
/optimise dup where { pop pop }{ false def } ifelse
/subset dup where { pop pop }{ () def } ifelse
/ucs2 dup where { pop pop }{ false def } ifelse
/binary dup where { pop pop }{ false def } ifelse
/compress dup where { pop pop }{ false def } ifelse
/comments dup where { pop pop }{ false def } ifelse
/info dup where { pop pop }{ false def } ifelse
/inc dup where { pop pop }{ () def } ifelse
/json () def
mark /.shellarguments where { pop .shellarguments }{ shellarguments } ifelse pop
counttomark 1 ge { % JSON string follows
token {
dup type /stringtype eq {
/json exch def
} if
} if
} if cleartomark
json ("inc":") search { % has inc
pop pop (") search pop exch pop exch pop % extract inc
{ (\\/) search { exch pop (/) cat exch cat }{ exit } ifelse } loop % \/ to /
/inc exch def
}{ pop } ifelse
inc () ne {
inc dup length 1 sub 1 getinterval (/) ne { /inc inc (/) cat def } if % dir/
} if
% dependencies
/include { inc exch cat dup status { pop pop pop pop run }{ (inc? need ) exch cat error } ifelse } bind def
(string.ps) include
(file.ps) include
(sort.ps) include
(math.ps) include
json jsondecode dup type /dicttype eq { { def } forall }{ pop } ifelse % JSON args
compress { /binary true def } if
subset () ne { /optimise true def } if
% messaging
/notice { $stdout dup dup 4 -1 roll writestring (\n) writestring flushfile } bind def
/warning { [ exch (WARNING ) exch (\n) ] concata $stderr exch writestring $stderr flushfile } bind def
/error { [ exch (ERROR ) exch (\n) ] concata $stderr exch writestring $stderr flushfile stop } bind def
% constants
/MAX_CID 65534 def
/MAX_STRING_SIZE 65535 def
/MAX_ARRAY_SIZE 65535 def
/MAX_DICT_SIZE 65535 def
/ARG_1_AND_2_ARE_WORDS 1 0 bitshift def
/ARGS_ARE_XY_VALUES 1 1 bitshift def
/ROUND_XY_TO_GRID 1 2 bitshift def
/WE_HAVE_A_SCALE 1 3 bitshift def
/MORE_COMPONENTS 1 5 bitshift def
/WE_HAVE_AN_X_AND_Y_SCALE 1 6 bitshift def
/WE_HAVE_A_TWO_BY_TWO 1 7 bitshift def
/WE_HAVE_INSTRUCTIONS 1 8 bitshift def
/USE_MY_METRICS 1 9 bitshift def
/OVERLAP_COMPOUND 1 10 bitshift def
/TABLES [ % tag, required/optional in TTF, emit to Type42
[/head true true]
[/hhea true true]
[/hmtx true true]
[/loca true true]
[/glyf true true]
[/cmap true false]
[/name true false]
[/maxp true true]
[/post true false]
[/cvt_ false true]
[/fpgm false true]
[/prep false true]
% [/cff_ false false]
% [/os_2 false false]
] readonly def
/TAG_NAMES << % TTF tag name conversions to simplify postscript code
(cvt ) (cvt_)
/cvt_ (cvt )
(CFF ) (cff_)
/cff_ (CFF )
(OS/2) (os_2)
/os_2 (OS/2)
//TABLES { 0 get dup dup length string cvs } forall % last
>> readonly def
/UNDERLINE_THICKNESS_CIDS [ (_-.|!liI1) {} forall 16#2013 16#2014 ] readonly def % thinnest cids for ul
% primatives readers
/r_uint8 { $r read pop } bind def
/r_uint16 { r_uint8 8 bitshift r_uint8 or } bind def
/r_int16 { r_uint16 dup 16#8000 and 0 ne { 16#7FFF not or } if } bind def
/r_uint32 { r_uint16 16 bitshift r_uint16 or } bind def
/r_int32 { r_uint32 dup 16#80000000 and 0 ne { 16#7FFFFFFF not or } if } bind def
/r_Fixed { r_int32 65536 div } bind def
/r_uFWord { r_uint16 } bind def
/r_FWord { r_int16 } bind def
/r_longDateTime { 8 r_string } bind def
/r_bytes { [ exch { r_uint8 } repeat ] } bind def
/r_words { [ exch { r_uint16 } repeat ] } bind def
/r_longs { [ exch { r_uint32 } repeat ] } bind def
/r_string { dup 0 gt { $r exch string readstring pop }{ pop () } ifelse } bind def
/r_skip { $r fileposition add $r exch setfileposition } bind def
/r_peek8 { r_uint8 -1 r_skip } bind def
/r_peek16 { r_uint16 -2 r_skip } bind def
/r_peek32 { r_uint32 -4 r_skip } bind def
% primatives writers
/w_uint8 { $w exch write } bind def
/w_uint16 { dup -8 bitshift w_uint8 w_uint8 } bind def
/w_int16 { w_uint16 } bind def
/w_uint32 { dup -16 bitshift w_uint16 w_uint16 } bind def
/w_int32 { w_uint32 } bind def
/w_Fixed { 65536 mul cvi w_uint32 } bind def
/w_uFWord { w_uint16 } bind def
/w_FWord { w_int16 } bind def
/w_longDateTime { dup length dup 8 ne { 8 min 0 exch getinterval 8 string exch 1 index copy } if pop w_string } bind def
/w_bytes { { w_uint8 } forall } bind def
/w_words { { w_uint16 } forall } bind def
/w_string { $w exch writestring } bind def
/w_zero { { 0 w_uint8 } repeat } bind def
/w_poke16 { 2 getinterval /NullEncode filter /$w exch def w_uint16 $w closefile } bind def % value string position
/w_poke32 { 4 getinterval /NullEncode filter /$w exch def w_uint32 $w closefile } bind def % value string position
% stream writer system
/sinit {
userdict /UserObjects known not { 0 null defineuserobject } if % init UOs
1 execuserobject type /booleantype eq dup not {
0 //MAX_STRING_SIZE string defineuserobject % data target
} if
} bind def
/sfile {
sstatus {
(sfile: cannot nest ) warning
} if
1 true defineuserobject % status, false if closed
2 0 defineuserobject % insertion position and data length
3 null defineuserobject % data copy
{ % filter proc
1 exch defineuserobject % status
2 exch length 2 execuserobject add defineuserobject % new length
0 execuserobject 2 execuserobject 1 index length 1 index sub getinterval % remaining space
dup length 0 eq {
(sfile: out of buffer space) error
} if
} /NullEncode filter
} bind def
/sstatus {
sinit { 1 execuserobject }{ false } ifelse
} bind def
/sreset {
sinit { 2 0 defineuserobject } if
} bind def
/slength {
sinit { 2 execuserobject }{ 0 } ifelse
} bind def
/sdata {
sinit { 0 execuserobject 0 2 execuserobject getinterval }{ 0 string } ifelse
} bind def
/scopy {
sinit {
3 execuserobject null eq { 3 sdata dup length string copy defineuserobject } if % cache
3 execuserobject
}{
0 string
} ifelse
} bind def
% on-demand TTF table reader/decoder
/r_decode {
$r fileposition exch % save position
dup
dup
dup 0 (d) putinterval load % directory entry
$r 1 index /offset get setfileposition % set position
1 index 0 (r) putinterval
exch cvx exec % read table
1 index 0 (t) putinterval
exch 1 index def % redefine t_* as the read data
exch $r exch setfileposition % restore position
} bind def
% on-demand TTF table writer/encoder
/w_encode {
dup
dup
cvx exec % load table
exch dup 0 (w) putinterval
/$w sfile def
cvx exec % write table
$w closefile
dup 0 (d) putinterval load % directory entry
dup /data known not { % not array data (glyf hmtx loca)
/data scopy put
}{
pop
} ifelse
} bind def
% TTF directory and table readers/writers
/r_directory {
% parse offset subtable
6 dict dup begin
/type r_uint32 def % 16#00010000 for TTF, 16#4F54544F for OTF
/numTables r_uint16 def
/searchRange r_uint16 def
/entrySelector r_uint16 def
/rangeShift r_uint16 def
% parse table directory
/directory [
numTables {
<<
/tag 4 r_string
/checkSum r_uint32
/offset r_uint32
/Length r_uint32
>>
} repeat
] def
end
/t_directory 1 index def
% define directory dictionaries
/directory get {
dup /tag get
//TAG_NAMES 1 index known {
//TAG_NAMES exch get % ps name
(d_) 1 index concat2 3 -1 roll def % directory entry
(t_) exch concat2 [ 1 index /r_decode cvx ] cvx def % reader fn
}{
pop pop
} ifelse
} forall
% check required TTF tables are present
[ //TABLES {
aload pop
(d_) 3 index concat2
currentdict exch known not {
pop exch
//TAG_NAMES exch get % ttf name
(: table missing) concat2 exch { error }{ pop } ifelse % or pop/warning
}{
exch pop not { pop } if
dup length string cvs
} ifelse
} forall ]
{} insertionsort
/$tables exch def % Type42 tables to emit
} bind def
/w_directory {
% get table data
$tables {
(t_) exch concat2
exec w_encode
} forall
% update directory entries
/$offset 12 $tables length 16 mul add def % running offset
/$checksum 0 def % file checksum
$tables {
(d_) exch concat2 load begin
data type /arraytype eq {
0 0 % length checksum
data {
dup null ne {
dup checksum exch length
4 -1 roll add 3 1 roll iadd32
}{
pop
} ifelse
} forall
/checkSum exch def % for padded data
/Length 1 index def % data length padded
}{
/Length data length def % unpadded
/data data zeropad def % pad
/checkSum data checksum def % for padded data
data length % padded
} ifelse
/offset $offset def
/$offset $offset 3 -1 roll add store
/$checksum $checksum checkSum iadd32 store
end
} forall
% make offset subtable
5 dict
dup /numTables $tables length put
dup searchargs
/$w sfile def
dup begin
16#00010000 w_uint32
numTables w_uint16
searchRange w_uint16
entrySelector w_uint16
rangeShift w_uint16
end
$tables {
(d_) exch concat2 load begin
tag w_string
checkSum w_uint32
offset w_uint32
Length w_uint32
end
} forall
$w closefile
dup /data scopy put
% do checkSumAdjustment
/$checksum 1 index /data get checksum $checksum iadd32 def
16#B1B0AFBA $checksum isub32
d_head /data get 8 w_poke32
currentdict /$offset undef
currentdict /$checksum undef
} bind def
/r_cff_ {
begin
Length //MAX_STRING_SIZE div ceiling cvi array
Length
0 1 3 index length 1 sub {
1 index //MAX_STRING_SIZE min r_string
dup length 3 1 roll
4 index 3 1 roll put
sub
} for
pop
userdict begin
/cff exch def % raw CFF data chunks
% (cff.ps) include % CFF parsing
end
false { % not the CFF solution - install as Type 2 (CFF) base font and add CID layer
$r offset setfileposition
<<
% Header
/major r_CFF_Card8
/minor r_CFF_Card8
/hdrSize r_CFF_Card8
/offSize r_CFF_OffSize
/Name r_CFF_INDEX
/TopDICT r_CFF_INDEX
/String r_CFF_INDEX
/GlobalSubr r_CFF_INDEX
>> begin
% parse TopDICT recursively
TopDICT /data get
0 1 2 index length 1 sub {
2 copy 2 copy get r_CFF_DICT
dup CFF_ESCAPE 6 or known {
dup CFF_ESCAPE 6 or get
}{
CFF_TOP_DICT_DEFAULTS /CharstringType get
} ifelse
/$CharstringType exch def
r_CFF_DICT_sanitise
CFF_TOP_DICT_DEFAULTS r_CFF_DICT_defaults
put pop
} for
pop
currentdict
end
} if
end
cff
} bind def
/r_cmap { % just store the encoding table header and create procs for specific subtable formats
% useful: ttcmap.c from The FreeType Project, https://www.freetype.org/
<<
/version r_uint16
/numberSubtables r_uint16
/encodingSubtables [
2 index {
<<
/platformID r_uint16
/platformSpecificID r_uint16
/offset r_uint32
/format null % peeked
>>
} repeat
]
>>
exch /offset get
1 index /encodingSubtables get {
dup /offset get 2 index add
$r exch setfileposition
r_peek16 % format
dup 8 ge 1 index 13 le and { cvr } if % 32-bit fixed-point format
/format exch put
} forall
pop
} bind def
/r_cmap_0 {
begin
/format r_uint16 def
/Length r_uint16 def
/language r_uint16 def
/glyphIndexArray 256 r_bytes def
currentdict
end
exch pop
} bind def
/r_cmap_2 {
begin
/format r_uint16 def
/Length r_uint16 def
/language r_uint16 def
/subHeaderKeys 256 r_words def
0 subHeaderKeys { 8 idiv max } forall % max subheaders
dup 1 add array % subheaders
exch 0 1 2 index
{
<<
/firstCode r_uint16
/entryCount r_uint16
/idDelta r_int16
/idRangeOffset r_uint16 % from here
>>
dup /idRangeOffset get 2 sub % offset to range
3 index 3 index sub 8 mul % offset to glyphIndexArray
sub 2 idiv % index into glyphIndexArray for firstCode
1 index exch /firstCodeIndex exch put
3 index 3 1 roll put
} for
pop
/subHeaders exch def
Length add $r fileposition sub 2 idiv r_words % # GIDs
/glyphIndexArray exch def
currentdict
end
} bind def
/r_cmap_4 {
begin
/format r_uint16 def
/Length r_uint16 def
/language r_uint16 def
/segCount r_uint16 2 idiv def
/searchRange r_uint16 def
/entrySelector r_uint16 def
/rangeShift r_uint16 def
/endCode segCount r_words def
2 r_skip
/startCode segCount r_words def
/idDelta segCount r_words def
/idRangeOffset segCount r_words def
Length add $r fileposition sub 2 idiv r_words % # GIDs
/glyphIndexArray exch def
currentdict
end
} bind def
/r_cmap_6 {
begin
/format r_uint16 def
/Length r_uint16 def
/language r_uint16 def
/firstCode r_uint16 def
/entryCount r_uint16 def
/glyphIndexArray entryCount r_words def
currentdict
end
exch pop
} bind def
/r_cmap_8.0 {
(cmap: format 8.0 not supported) warning
dup /format r_Fixed put
exch pop
} bind def
/r_cmap_10.0 {
(cmap: format 10.0 not supported) warning
dup /format r_Fixed put
exch pop
} bind def
/r_cmap_12.0 {
begin
/format r_Fixed def
/Length r_uint32 def
/language r_uint32 def
/nGroups r_uint32 def
nGroups [ exch {
<<
/startCharCode r_uint32
/endCharCode r_uint32
/startGlyphCode r_uint32
>>
} repeat ]
/groups exch def
currentdict
end
exch pop
} bind def
/r_cmap_13.0 {
(cmap: format 13.0 not supported) warning
dup /format r_Fixed put
exch pop
} bind def
/r_cmap_14 {
(cmap: format 14 not supported) warning
dup /format r_uint16 put
exch pop
} bind def
/r_cvt_ {
/Length get r_string % controlValues
} bind def
/w_cvt_ {
w_string
} bind def
/r_fpgm {
/Length get r_string % instructions
} bind def
/w_fpgm {
w_string
} bind def
/r_glyf { % just store the unpacked binary and info to re-index component glyphs
/offset get
t_loca
dup length 1 sub % # glyphs
dup array
4 1 roll
0 1 3 -1 roll 1 sub
{
2 copy 1 add get % next glyph position
2 index 2 index get % start
2 copy gt { % not empty
dup 5 index add % offset
$r 1 index setfileposition
3 1 roll sub r_string % description
$r 2 index setfileposition % back
<<
/description 3 -1 roll
/numberOfContours r_int16
/xMin r_FWord
/yMin r_FWord
/xMax r_FWord
/yMax r_FWord
>>
dup /numberOfContours get 0 lt {
[ {
<<
/flags r_uint16 % for testing
/offset $r fileposition % for poking in new GID
/glyphIndex r_uint16 % may need remapping
>>
dup /flags get
dup //ARG_1_AND_2_ARE_WORDS and 0 ne { 4 }{ 2 } ifelse exch
dup //WE_HAVE_A_SCALE and 0 ne {
exch 2 add exch
}{
dup //WE_HAVE_AN_X_AND_Y_SCALE and 0 ne {
exch 4 add exch
}{
dup //WE_HAVE_A_TWO_BY_TWO and 0 ne {
exch 8 add exch
} if
} ifelse
} ifelse
exch r_skip
//MORE_COMPONENTS and 0 eq { exit } if
} loop ]
dup dup length 1 sub get /flags get //WE_HAVE_INSTRUCTIONS and 0 ne {
r_uint16 % # instructions
r_skip
} if
dup { /offset 2 copy get 5 index sub put } forall % GID offsets from description
1 index /components 3 -1 roll put
} if
exch pop
4 index 3 1 roll put
}{
pop pop pop % empty
} ifelse
} for
pop pop
} bind def
/w_glyf {
dup length array % padded descriptions
dup length 1 add array % new loca
3 -1 roll 0 % offset
exch 0 1 2 index length 1 sub
{
2 copy get
dup null ne {
/description get zeropad
dup length exch
6 index exch 3 index exch put
}{
pop 0
} ifelse
exch
4 index exch 4 index put
3 -1 roll add exch
} for
length exch 2 index 3 1 roll put % last loca
/t_loca exch def
d_glyf exch /data exch put
} bind def
/r_head {
pop
<<
/version r_Fixed
/fontRevision r_Fixed
/checkSumAdjustment r_uint32
/magicNumber r_uint32
/flags r_uint16
/unitsPerEm r_uint16
/created r_longDateTime
/modified r_longDateTime
/xMin r_FWord
/yMin r_FWord
/xMax r_FWord
/yMax r_FWord
/macStyle r_uint16
/lowestRecPPEM r_uint16
/fontDirectionHint r_int16
/indexToLocFormat r_int16
/glyphDataFormat r_int16
>>
} bind def
/w_head {
begin
/indexToLocFormat t_loca dup length 1 sub get 16#20000 idiv 1 min def % possibly changed
version w_Fixed
fontRevision w_Fixed
0 w_uint32 % checkSumAdjustment
16#5F0F3CF5 w_uint32 % magicNumber
flags w_uint16
unitsPerEm w_uint16
created w_longDateTime
modified w_longDateTime
xMin w_FWord
yMin w_FWord
xMax w_FWord
yMax w_FWord
macStyle w_uint16
lowestRecPPEM w_uint16
fontDirectionHint w_int16
indexToLocFormat w_int16
glyphDataFormat w_int16
end
} bind def
/r_hhea {
pop
<<
/version r_Fixed
/ascent r_FWord
/descent r_FWord
/lineGap r_FWord
/advanceWidthMax r_uFWord
/minLeftSideBearing r_FWord
/minRightSideBearing r_FWord
/xMaxExtent r_FWord
/caretSlopeRise r_int16
/caretSlopeRun r_int16
/caretOffset r_FWord
8 r_skip
/metricDataFormat r_int16
/numOfLongHorMetrics r_uint16
>>
} bind def
/w_hhea {
begin
/numOfLongHorMetrics t_hmtx /hMetrics get length def % possibly changed
version w_Fixed
ascent w_FWord
descent w_FWord
lineGap w_FWord
advanceWidthMax w_uFWord
minLeftSideBearing w_FWord
minRightSideBearing w_FWord
xMaxExtent w_FWord
caretSlopeRise w_int16
caretSlopeRun w_int16
caretOffset w_FWord
8 w_zero
metricDataFormat w_int16
numOfLongHorMetrics w_uint16
end
} bind def
/r_hmtx {
pop
2 dict dup begin
t_hhea /numOfLongHorMetrics get array
0 1 2 index length 1 sub {
1 index exch << /advanceWidth r_uint16 /leftSideBearing r_int16 >> put % longHorMetric
} for
/hMetrics exch def
t_maxp /numGlyphs get t_hhea /numOfLongHorMetrics get sub array
0 1 2 index length 1 sub {
1 index exch r_FWord put
} for
/leftSideBearing exch def
end
} bind def
/w_hmtx {
begin
hMetrics length
//MAX_STRING_SIZE 4 idiv % metrics per string
2 copy div ceiling cvi 1 add array
0 2 index 4 index 1 sub {
hMetrics 1 index 4 index 6 index 2 index sub min getinterval {
begin
advanceWidth w_uint16
leftSideBearing w_int16
end
} forall
$w closefile
1 index exch 3 index idiv scopy put
/$w sfile def
} for
3 1 roll pop pop
leftSideBearing {
w_FWord
} forall
$w closefile
dup dup length 1 sub scopy zeropad put % last item, padded
end
d_hmtx exch /data exch put
} bind def
/r_loca {
pop
t_maxp /numGlyphs get 1 add array % extra entry for end of last glyph
t_head /indexToLocFormat get 0 eq { {r_uint16 1 bitshift} }{ {r_uint32} } ifelse
0 1 3 index length 1 sub {
2 index exch 2 index exec put
} for
pop
} bind def
/w_loca {
t_head /indexToLocFormat get 0 eq {
{ -1 bitshift w_uint16 } forall
}{
$w closefile
dup length
//MAX_STRING_SIZE 4 idiv % locas per string
2 copy div ceiling cvi array
0 2 index 4 index 1 sub {
/$w sfile def
4 index 1 index 4 index 6 index 2 index sub min getinterval {
w_uint32
} forall
$w closefile
1 index exch 3 index idiv scopy put
} for
$w closefile
4 1 roll pop pop pop
d_loca exch /data exch put
} ifelse
} bind def
/r_maxp {
pop
<<
/version r_Fixed
/numGlyphs r_uint16
/maxPoints r_uint16
/maxContours r_uint16
/maxComponentPoints r_uint16
/maxComponentContours r_uint16
/maxZones r_uint16
/maxTwilightPoints r_uint16
/maxStorage r_uint16
/maxFunctionDefs r_uint16
/maxInstructionDefs r_uint16
/maxStackElements r_uint16
/maxSizeOfInstructions r_uint16
/maxComponentElements r_uint16
/maxComponentDepth r_uint16
>>
} bind def
/w_maxp {
begin
/numGlyphs t_glyf length def % possibly changed
version w_Fixed
numGlyphs w_uint16
maxPoints w_uint16
maxContours w_uint16
maxComponentPoints w_uint16
maxComponentContours w_uint16
maxZones w_uint16
maxTwilightPoints w_uint16
maxStorage w_uint16
maxFunctionDefs w_uint16
maxInstructionDefs w_uint16
maxStackElements w_uint16
maxSizeOfInstructions w_uint16
maxComponentElements w_uint16
maxComponentDepth w_uint16
end
} bind def
/r_name {
<<
/format r_uint16
/count r_uint16
/stringOffset r_uint16
/nameRecord []
>>
[
1 index /count get {
<<
/platformID r_uint16
/platformSpecificID r_uint16
/languageID r_uint16
/nameID r_uint16
/Length r_uint16
/offset r_uint16
/name null
>>
} repeat
]
2 copy /nameRecord exch put
3 -1 roll /offset get
2 index /stringOffset get add
exch {
begin
dup offset add
$r exch setfileposition
Length r_string
/name exch def
end
} forall
pop
} bind def
/r_post {
pop
<<
/format r_Fixed
/italicAngle r_Fixed
/underlinePosition r_FWord
/underlineThickness r_FWord
/isFixedPitch r_uint32
/minMemType42 r_uint32
/maxMemType42 r_uint32
/minMemType1 r_uint32
/maxMemType1 r_uint32
% skip glyph name subtable
>>
} bind def
/r_prep {
/Length get r_string % controlValueProgram
} bind def
/w_prep {
w_string
} bind def
% Microsoft table so use MS names, see https://www.microsoft.com/typography/otspec/os2.htm
/r_os_2 {
begin
<<
/version r_uint16
/xAvgCharWidth r_int16
/usWeightClass r_uint16
/usWidthClass r_uint16
/fsType r_int16
/ySubscriptXSize r_int16
/ySubscriptYSize r_int16
/ySubscriptXOffset r_int16
/ySubscriptYOffset r_int16
/ySuperscriptXSize r_int16
/ySuperscriptYSize r_int16
/ySuperscriptXOffset r_int16
/ySuperscriptYOffset r_int16
/yStrikeoutSize r_int16
/yStrikeoutPosition r_int16
/sFamilyClass r_int16
/panose 10 r_bytes
/ulUnicodeRange 4 r_longs
/achVendID 4 r_string
/fsSelection r_uint16
/usFirstCharIndex r_uint16
/usLastCharIndex r_uint16
/sTypoAscender r_int16
/sTypoDescender r_int16
/sTypoLineGap r_int16
/usWinAscent r_uint16
/usWinDescent r_uint16
>> % version 0 78 bytes
Length 86 ge { <<
/ulCodePageRange 2 r_longs
>> { 3 copy put pop pop } forall
} if % version 1
Length 96 ge { <<
/sxHeight r_int16
/sCapHeight r_int16
/usDefaultChar r_uint16
/usBreakChar r_uint16
/usMaxContext r_uint16
>> { 3 copy put pop pop } forall
} if % version 2,3,4
Length 100 ge { <<
/usLowerPointSize r_uint16
/usUpperPointSize r_uint16
>> { 3 copy put pop pop } forall
} if % version 5
end
} bind def
% helpers
/cid2gid_cmap0 {
begin 10 dict begin % use cmap dict & temporary dict
/$cid2gid 256 array def
0 % max CID
0 1 255 {
glyphIndexArray 1 index get % GID
dup 0 ne {
$cid2gid 2 index 2 index put
pop max
}{
pop pop
} ifelse
} for
1 add $cid2gid 0 3 -1 roll getinterval
end end
} bind def
/cid2gid_cmap2 {
begin 10 dict begin % use cmap dict & temporary dict
/$cid2gid //MAX_ARRAY_SIZE array def
0 % max CID
0 1 255 {
/$hi exch def
/$k subHeaderKeys $hi get 8 idiv def % subHeader index
$hi 0 eq $k 0 ne or {
subHeaders $k get begin
firstCode 1 firstCode entryCount add 1 sub {
/$lo exch def
/$cid $hi 8 bitshift $lo or def
$cid max
$lo firstCode sub firstCodeIndex add % index
dup 0 lt 1 index glyphIndexArray length ge or {
(cmap2: outside of glyphIndexArray) warning
pop 0
}{
glyphIndexArray exch get
dup 0 ne {
idDelta add
} if
} ifelse
/$gid exch 16#FFFF and def
$gid 0 ne {
$cid2gid $cid get null ne {