fontisan 0.2.11 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +214 -51
  3. data/README.adoc +160 -0
  4. data/lib/fontisan/cli.rb +177 -6
  5. data/lib/fontisan/commands/convert_command.rb +32 -1
  6. data/lib/fontisan/config/conversion_matrix.yml +132 -4
  7. data/lib/fontisan/constants.rb +12 -0
  8. data/lib/fontisan/conversion_options.rb +378 -0
  9. data/lib/fontisan/converters/collection_converter.rb +45 -10
  10. data/lib/fontisan/converters/format_converter.rb +2 -0
  11. data/lib/fontisan/converters/outline_converter.rb +78 -4
  12. data/lib/fontisan/converters/type1_converter.rb +559 -0
  13. data/lib/fontisan/font_loader.rb +46 -3
  14. data/lib/fontisan/type1/afm_generator.rb +436 -0
  15. data/lib/fontisan/type1/afm_parser.rb +298 -0
  16. data/lib/fontisan/type1/agl.rb +456 -0
  17. data/lib/fontisan/type1/charstring_converter.rb +240 -0
  18. data/lib/fontisan/type1/charstrings.rb +408 -0
  19. data/lib/fontisan/type1/conversion_options.rb +243 -0
  20. data/lib/fontisan/type1/decryptor.rb +183 -0
  21. data/lib/fontisan/type1/encodings.rb +697 -0
  22. data/lib/fontisan/type1/font_dictionary.rb +514 -0
  23. data/lib/fontisan/type1/generator.rb +220 -0
  24. data/lib/fontisan/type1/inf_generator.rb +332 -0
  25. data/lib/fontisan/type1/pfa_generator.rb +343 -0
  26. data/lib/fontisan/type1/pfa_parser.rb +158 -0
  27. data/lib/fontisan/type1/pfb_generator.rb +291 -0
  28. data/lib/fontisan/type1/pfb_parser.rb +166 -0
  29. data/lib/fontisan/type1/pfm_generator.rb +610 -0
  30. data/lib/fontisan/type1/pfm_parser.rb +433 -0
  31. data/lib/fontisan/type1/private_dict.rb +285 -0
  32. data/lib/fontisan/type1/ttf_to_type1_converter.rb +327 -0
  33. data/lib/fontisan/type1/upm_scaler.rb +118 -0
  34. data/lib/fontisan/type1.rb +73 -0
  35. data/lib/fontisan/type1_font.rb +331 -0
  36. data/lib/fontisan/version.rb +1 -1
  37. data/lib/fontisan.rb +2 -0
  38. metadata +26 -2
@@ -0,0 +1,697 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "agl"
4
+
5
+ module Fontisan
6
+ module Type1
7
+ # Font encoding schemes for Type 1 fonts
8
+ #
9
+ # [`Encodings`](lib/fontisan/type1/encodings.rb) provides encoding schemes that map
10
+ # character positions to glyph names, following Adobe's encoding specifications.
11
+ #
12
+ # The Adobe Standard Encoding is the most common encoding for Type 1 fonts,
13
+ # providing a consistent mapping of glyph names to character positions.
14
+ #
15
+ # @example Use Adobe Standard Encoding
16
+ # encoding = Fontisan::Type1::Encodings::AdobeStandard
17
+ # encoding.glyph_name_for_code(65) # => "A"
18
+ # encoding.codepoint_for_glyph("A") # => 65
19
+ #
20
+ # @see https://www.adobe.com/devnet/font/pdfs/5178.Type1.pdf
21
+ module Encodings
22
+ # Adobe Standard Encoding glyph list
23
+ # This is the standard encoding used by most Type 1 fonts
24
+ ADOBE_STANDARD = [
25
+ ".notdef", # 0
26
+ "space", # 1
27
+ "exclam", # 2
28
+ "quotedbl", # 3
29
+ "numbersign", # 4
30
+ "dollar", # 5
31
+ "percent", # 6
32
+ "ampersand", # 7
33
+ "quoteright", # 8
34
+ "parenleft", # 9
35
+ "parenright", # 10
36
+ "asterisk", # 11
37
+ "plus", # 12
38
+ "comma", # 13
39
+ "hyphen", # 14
40
+ "period", # 15
41
+ "slash", # 16
42
+ "zero", # 17
43
+ "one", # 18
44
+ "two", # 19
45
+ "three", # 20
46
+ "four", # 21
47
+ "five", # 22
48
+ "six", # 23
49
+ "seven", # 24
50
+ "eight", # 25
51
+ "nine", # 26
52
+ "colon", # 27
53
+ "semicolon", # 28
54
+ "less", # 29
55
+ "equal", # 30
56
+ "greater", # 31
57
+ "question", # 32
58
+ "at", # 33
59
+ "A", # 34
60
+ "B", # 35
61
+ "C", # 36
62
+ "D", # 37
63
+ "E", # 38
64
+ "F", # 39
65
+ "G", # 40
66
+ "H", # 41
67
+ "I", # 42
68
+ "J", # 43
69
+ "K", # 44
70
+ "L", # 45
71
+ "M", # 46
72
+ "N", # 47
73
+ "O", # 48
74
+ "P", # 49
75
+ "Q", # 50
76
+ "R", # 51
77
+ "S", # 52
78
+ "T", # 53
79
+ "U", # 54
80
+ "V", # 55
81
+ "W", # 56
82
+ "X", # 57
83
+ "Y", # 58
84
+ "Z", # 59
85
+ "bracketleft", # 60
86
+ "backslash", # 61
87
+ "bracketright", # 62
88
+ "asciicircum", # 63
89
+ "underscore", # 64
90
+ "quoteleft", # 65
91
+ "a", # 66
92
+ "b", # 67
93
+ "c", # 68
94
+ "d", # 69
95
+ "e", # 70
96
+ "f", # 71
97
+ "g", # 72
98
+ "h", # 73
99
+ "i", # 74
100
+ "j", # 75
101
+ "k", # 76
102
+ "l", # 77
103
+ "m", # 78
104
+ "n", # 79
105
+ "o", # 80
106
+ "p", # 81
107
+ "q", # 82
108
+ "r", # 83
109
+ "s", # 84
110
+ "t", # 85
111
+ "u", # 86
112
+ "v", # 87
113
+ "w", # 88
114
+ "x", # 89
115
+ "y", # 90
116
+ "z", # 91
117
+ "braceleft", # 92
118
+ "bar", # 93
119
+ "braceright", # 94
120
+ "asciitilde", # 95
121
+ ".notdef", # 96
122
+ ".notdef", # 97
123
+ ".notdef", # 98
124
+ ".notdef", # 99
125
+ ".notdef", # 100
126
+ ".notdef", # 101
127
+ ".notdef", # 102
128
+ ".notdef", # 103
129
+ ".notdef", # 104
130
+ ".notdef", # 105
131
+ ".notdef", # 106
132
+ ".notdef", # 107
133
+ ".notdef", # 108
134
+ ".notdef", # 109
135
+ ".notdef", # 110
136
+ ".notdef", # 111
137
+ ".notdef", # 112
138
+ ".notdef", # 113
139
+ ".notdef", # 114
140
+ ".notdef", # 115
141
+ ".notdef", # 116
142
+ ".notdef", # 117
143
+ ".notdef", # 118
144
+ ".notdef", # 119
145
+ ".notdef", # 120
146
+ ".notdef", # 121
147
+ ".notdef", # 122
148
+ ".notdef", # 123
149
+ ".notdef", # 124
150
+ ".notdef", # 125
151
+ ".notdef", # 126
152
+ ".notdef", # 127
153
+ ".notdef", # 128
154
+ "exclamdown", # 129
155
+ "cent", # 130
156
+ "sterling", # 131
157
+ "fraction", # 132
158
+ "yen", # 133
159
+ "florin", # 134
160
+ "section", # 135
161
+ "currency", # 136
162
+ "quotesingle", # 137
163
+ "quotedblleft", # 138
164
+ "guillemotleft", # 139
165
+ "guilsinglleft", # 140
166
+ "guilsinglright", # 141
167
+ "fi", # 142
168
+ "fl", # 143
169
+ ".notdef", # 144
170
+ "endash", # 145
171
+ "dagger", # 146
172
+ "daggerdbl", # 147
173
+ "periodcentered", # 148
174
+ ".notdef", # 149
175
+ "paragraph", # 150
176
+ "bullet", # 151
177
+ "quotesinglbase", # 152
178
+ "quotedblbase", # 153
179
+ "quotedblright", # 154
180
+ "guillemotright", # 155
181
+ "ellipsis", # 156
182
+ "perthousand", # 157
183
+ ".notdef", # 158
184
+ "questiondown", # 159
185
+ ".notdef", # 160
186
+ "grave", # 161
187
+ "acute", # 162
188
+ "circumflex", # 163
189
+ "tilde", # 164
190
+ "macron", # 165
191
+ "breve", # 166
192
+ "dotaccent", # 167
193
+ "dieresis", # 168
194
+ ".notdef", # 169
195
+ "ring", # 170
196
+ "cedilla", # 171
197
+ ".notdef", # 172
198
+ "hungarumlaut", # 173
199
+ "ogonek", # 174
200
+ "caron", # 175
201
+ "emdash", # 176
202
+ ".notdef", # 177
203
+ ".notdef", # 178
204
+ ".notdef", # 179
205
+ ".notdef", # 180
206
+ ".notdef", # 181
207
+ ".notdef", # 182
208
+ ".notdef", # 183
209
+ ".notdef", # 184
210
+ ".notdef", # 185
211
+ ".notdef", # 186
212
+ ".notdef", # 187
213
+ ".notdef", # 188
214
+ ".notdef", # 189
215
+ ".notdef", # 190
216
+ ".notdef", # 191
217
+ "AE", # 192
218
+ ".notdef", # 193
219
+ "ordfeminine", # 194
220
+ ".notdef", # 195
221
+ ".notdef", # 196
222
+ ".notdef", # 197
223
+ ".notdef", # 198
224
+ "Lslash", # 199
225
+ "Oslash", # 200
226
+ "OE", # 201
227
+ "ordmasculine", # 202
228
+ ".notdef", # 203
229
+ ".notdef", # 204
230
+ ".notdef", # 205
231
+ ".notdef", # 206
232
+ ".notdef", # 207
233
+ ".notdef", # 208
234
+ "ae", # 209
235
+ ".notdef", # 210
236
+ ".notdef", # 211
237
+ ".notdef", # 212
238
+ "dotlessi", # 213
239
+ ".notdef", # 214
240
+ ".notdef", # 215
241
+ "lslash", # 216
242
+ "oslash", # 217
243
+ "oe", # 218
244
+ "germandbls", # 219
245
+ ".notdef", # 220
246
+ ".notdef", # 221
247
+ ".notdef", # 222
248
+ ".notdef", # 223
249
+ ".notdef", # 224
250
+ ".notdef", # 225
251
+ ".notdef", # 226
252
+ ".notdef", # 227
253
+ ".notdef", # 228
254
+ "Agrave", # 229
255
+ "Aacute", # 230
256
+ "Acircumflex", # 231
257
+ "Atilde", # 232
258
+ "Adieresis", # 233
259
+ "Aring", # 234
260
+ "Ccedilla", # 235
261
+ "Egrave", # 236
262
+ "Eacute", # 237
263
+ "Ecircumflex", # 238
264
+ "Edieresis", # 239
265
+ "Igrave", # 240
266
+ "Iacute", # 241
267
+ "Icircumflex", # 242
268
+ "Idieresis", # 243
269
+ "Eth", # 244
270
+ "Ntilde", # 245
271
+ "Ograve", # 246
272
+ "Oacute", # 247
273
+ "Ocircumflex", # 248
274
+ "Otilde", # 249
275
+ "Odieresis", # 250
276
+ "Ugrave", # 251
277
+ "Uacute", # 252
278
+ "Ucircumflex", # 253
279
+ "Udieresis", # 254
280
+ "Yacute", # 255
281
+ ].freeze
282
+
283
+ # ISO-8859-1 (Latin-1) encoding glyph list
284
+ ISO_8859_1 = [
285
+ ".notdef",
286
+ "space",
287
+ "exclam",
288
+ "quotedbl",
289
+ "numbersign",
290
+ "dollar",
291
+ "percent",
292
+ "ampersand",
293
+ "quotesingle",
294
+ "parenleft",
295
+ "parenright",
296
+ "asterisk",
297
+ "plus",
298
+ "comma",
299
+ "hyphen",
300
+ "period",
301
+ "slash",
302
+ "zero",
303
+ "one",
304
+ "two",
305
+ "three",
306
+ "four",
307
+ "five",
308
+ "six",
309
+ "seven",
310
+ "eight",
311
+ "nine",
312
+ "colon",
313
+ "semicolon",
314
+ "less",
315
+ "equal",
316
+ "greater",
317
+ "question",
318
+ "at",
319
+ "A",
320
+ "B",
321
+ "C",
322
+ "D",
323
+ "E",
324
+ "F",
325
+ "G",
326
+ "H",
327
+ "I",
328
+ "J",
329
+ "K",
330
+ "L",
331
+ "M",
332
+ "N",
333
+ "O",
334
+ "P",
335
+ "Q",
336
+ "R",
337
+ "S",
338
+ "T",
339
+ "U",
340
+ "V",
341
+ "W",
342
+ "X",
343
+ "Y",
344
+ "Z",
345
+ "bracketleft",
346
+ "backslash",
347
+ "bracketright",
348
+ "asciicircum",
349
+ "underscore",
350
+ "grave",
351
+ "a",
352
+ "b",
353
+ "c",
354
+ "d",
355
+ "e",
356
+ "f",
357
+ "g",
358
+ "h",
359
+ "i",
360
+ "j",
361
+ "k",
362
+ "l",
363
+ "m",
364
+ "n",
365
+ "o",
366
+ "p",
367
+ "q",
368
+ "r",
369
+ "s",
370
+ "t",
371
+ "u",
372
+ "v",
373
+ "w",
374
+ "x",
375
+ "y",
376
+ "z",
377
+ "braceleft",
378
+ "bar",
379
+ "braceright",
380
+ "asciitilde",
381
+ ".notdef",
382
+ ".notdef",
383
+ ".notdef",
384
+ ".notdef",
385
+ ".notdef",
386
+ ".notdef",
387
+ ".notdef",
388
+ ".notdef",
389
+ ".notdef",
390
+ ".notdef",
391
+ ".notdef",
392
+ ".notdef",
393
+ ".notdef",
394
+ ".notdef",
395
+ ".notdef",
396
+ ".notdef",
397
+ ".notdef",
398
+ ".notdef",
399
+ ".notdef",
400
+ ".notdef",
401
+ ".notdef",
402
+ ".notdef",
403
+ ".notdef",
404
+ ".notdef",
405
+ ".notdef",
406
+ ".notdef",
407
+ ".notdef",
408
+ ".notdef",
409
+ ".notdef",
410
+ ".notdef",
411
+ ".notdef",
412
+ ".notdef",
413
+ "nobreakspace",
414
+ "exclamdown",
415
+ "cent",
416
+ "sterling",
417
+ "currency",
418
+ "yen",
419
+ "brokenbar",
420
+ "section",
421
+ "dieresis",
422
+ "copyright",
423
+ "ordfeminine",
424
+ "guillemotleft",
425
+ "logicalnot",
426
+ "hyphen",
427
+ "registered",
428
+ "macron",
429
+ "degree",
430
+ "plusminus",
431
+ "twosuperior",
432
+ "threesuperior",
433
+ "acute",
434
+ "mu",
435
+ "paragraph",
436
+ "periodcentered",
437
+ "cedilla",
438
+ "onesuperior",
439
+ "ordmasculine",
440
+ "guillemotright",
441
+ "onequarter",
442
+ "onehalf",
443
+ "threequarters",
444
+ "questiondown",
445
+ "Agrave",
446
+ "Aacute",
447
+ "Acircumflex",
448
+ "Atilde",
449
+ "Adieresis",
450
+ "Aring",
451
+ "AE",
452
+ "Ccedilla",
453
+ "Egrave",
454
+ "Eacute",
455
+ "Ecircumflex",
456
+ "Edieresis",
457
+ "Igrave",
458
+ "Iacute",
459
+ "Icircumflex",
460
+ "Idieresis",
461
+ "Eth",
462
+ "Ntilde",
463
+ "Ograve",
464
+ "Oacute",
465
+ "Ocircumflex",
466
+ "Otilde",
467
+ "Odieresis",
468
+ "multiply",
469
+ "Oslash",
470
+ "Ugrave",
471
+ "Uacute",
472
+ "Ucircumflex",
473
+ "Udieresis",
474
+ "Yacute",
475
+ "Thorn",
476
+ "germandbls",
477
+ "agrave",
478
+ "aacute",
479
+ "acircumflex",
480
+ "atilde",
481
+ "adieresis",
482
+ "aring",
483
+ "ae",
484
+ "ccedilla",
485
+ "egrave",
486
+ "eacute",
487
+ "ecircumflex",
488
+ "edieresis",
489
+ "igrave",
490
+ "iacute",
491
+ "icircumflex",
492
+ "idieresis",
493
+ "eth",
494
+ "ntilde",
495
+ "ograve",
496
+ "oacute",
497
+ "ocircumflex",
498
+ "otilde",
499
+ "odieresis",
500
+ "divide",
501
+ "oslash",
502
+ "ugrave",
503
+ "uacute",
504
+ "ucircumflex",
505
+ "udieresis",
506
+ "yacute",
507
+ "thorn",
508
+ "ydieresis",
509
+ ].freeze
510
+
511
+ # Base encoding class
512
+ #
513
+ # All encoding classes should inherit from this and implement
514
+ # the required methods.
515
+ class Encoding
516
+ # Get glyph name for character code
517
+ #
518
+ # @param codepoint [Integer] Character codepoint
519
+ # @return [String, nil] Glyph name or nil if not found
520
+ def self.glyph_name_for_code(codepoint)
521
+ raise NotImplementedError,
522
+ "#{name} must implement glyph_name_for_code"
523
+ end
524
+
525
+ # Get character code for glyph name
526
+ #
527
+ # @param name [String] Glyph name
528
+ # @return [Integer, nil] Character code or nil if not found
529
+ def self.codepoint_for_glyph(name)
530
+ raise NotImplementedError,
531
+ "#{name} must implement codepoint_for_glyph"
532
+ end
533
+
534
+ # Check if glyph name is in encoding
535
+ #
536
+ # @param name [String] Glyph name
537
+ # @return [Boolean] True if glyph is in encoding
538
+ def self.include?(name)
539
+ !codepoint_for_glyph(name).nil?
540
+ end
541
+
542
+ # Get encoding name
543
+ #
544
+ # @return [String] Encoding name
545
+ def self.encoding_name
546
+ raise NotImplementedError, "#{name} must implement encoding_name"
547
+ end
548
+
549
+ # Get all glyph names in encoding
550
+ #
551
+ # @return [Array<String>] All glyph names
552
+ def self.all_glyph_names
553
+ raise NotImplementedError, "#{name} must implement all_glyph_names"
554
+ end
555
+ end
556
+
557
+ # Adobe Standard Encoding
558
+ #
559
+ # The most common encoding for Type 1 fonts, providing a consistent
560
+ # mapping of glyph names to character positions in the range 0-255.
561
+ class AdobeStandard < Encoding
562
+ # Build code to glyph mapping
563
+ code_to_glyph = {}
564
+ glyph_to_code = {}
565
+
566
+ ADOBE_STANDARD.each_with_index do |name, i|
567
+ code_to_glyph[i] = name unless name == ".notdef"
568
+ glyph_to_code[name] = i unless name == ".notdef"
569
+ end
570
+
571
+ CODE_TO_GLYPH = code_to_glyph.freeze
572
+ GLYPH_TO_CODE = glyph_to_code.freeze
573
+
574
+ # Get glyph name for character code
575
+ #
576
+ # @param codepoint [Integer] Character code (0-255)
577
+ # @return [String, nil] Glyph name or nil if not found
578
+ def self.glyph_name_for_code(codepoint)
579
+ CODE_TO_GLYPH[codepoint]
580
+ end
581
+
582
+ # Get character code for glyph name
583
+ #
584
+ # @param name [String] Glyph name
585
+ # @return [Integer, nil] Character code or nil if not found
586
+ def self.codepoint_for_glyph(name)
587
+ GLYPH_TO_CODE[name]
588
+ end
589
+
590
+ # Get encoding name
591
+ #
592
+ # @return [String] "AdobeStandard"
593
+ def self.encoding_name
594
+ "AdobeStandard"
595
+ end
596
+
597
+ # Get all glyph names in encoding
598
+ #
599
+ # @return [Array<String>] All glyph names except .notdef
600
+ def self.all_glyph_names
601
+ ADOBE_STANDARD.reject { |n| n == ".notdef" }
602
+ end
603
+ end
604
+
605
+ # ISO-8859-1 (Latin-1) Encoding
606
+ #
607
+ # Encoding for Western European languages, based on ISO-8859-1 standard.
608
+ class ISOLatin1 < Encoding
609
+ # Build code to glyph mapping
610
+ code_to_glyph = {}
611
+ glyph_to_code = {}
612
+
613
+ ISO_8859_1.each_with_index do |name, i|
614
+ code_to_glyph[i] = name unless name == ".notdef"
615
+ glyph_to_code[name] = i unless name == ".notdef"
616
+ end
617
+
618
+ CODE_TO_GLYPH = code_to_glyph.freeze
619
+ GLYPH_TO_CODE = glyph_to_code.freeze
620
+
621
+ # Get glyph name for character code
622
+ #
623
+ # @param codepoint [Integer] Character code (0-255)
624
+ # @return [String, nil] Glyph name or nil if not found
625
+ def self.glyph_name_for_code(codepoint)
626
+ CODE_TO_GLYPH[codepoint]
627
+ end
628
+
629
+ # Get character code for glyph name
630
+ #
631
+ # @param name [String] Glyph name
632
+ # @return [Integer, nil] Character code or nil if not found
633
+ def self.codepoint_for_glyph(name)
634
+ GLYPH_TO_CODE[name]
635
+ end
636
+
637
+ # Get encoding name
638
+ #
639
+ # @return [String] "ISOLatin1"
640
+ def self.encoding_name
641
+ "ISOLatin1"
642
+ end
643
+
644
+ # Get all glyph names in encoding
645
+ #
646
+ # @return [Array<String>] All glyph names except .notdef
647
+ def self.all_glyph_names
648
+ ISO_8859_1.reject { |n| n == ".notdef" }
649
+ end
650
+ end
651
+
652
+ # Unicode Encoding
653
+ #
654
+ # Uses the Adobe Glyph List to map Unicode codepoints to glyph names.
655
+ # This encoding supports all Unicode characters through the AGL.
656
+ class Unicode < Encoding
657
+ # Get glyph name for Unicode codepoint
658
+ #
659
+ # @param codepoint [Integer] Unicode codepoint
660
+ # @return [String] Glyph name from AGL
661
+ def self.glyph_name_for_code(codepoint)
662
+ AGL.glyph_name_for_unicode(codepoint)
663
+ end
664
+
665
+ # Get Unicode codepoint for glyph name
666
+ #
667
+ # @param name [String] Glyph name
668
+ # @return [Integer, nil] Unicode codepoint or nil if not found
669
+ def self.codepoint_for_glyph(name)
670
+ AGL.unicode_for_glyph_name(name)
671
+ end
672
+
673
+ # Check if glyph name is in encoding (always true for Unicode)
674
+ #
675
+ # @param name [String] Glyph name
676
+ # @return [Boolean] Always true for Unicode
677
+ def self.include?(_name)
678
+ true
679
+ end
680
+
681
+ # Get encoding name
682
+ #
683
+ # @return [String] "Unicode"
684
+ def self.encoding_name
685
+ "Unicode"
686
+ end
687
+
688
+ # Get all glyph names in AGL
689
+ #
690
+ # @return [Array<String>] All glyph names in AGL
691
+ def self.all_glyph_names
692
+ AGL.all_glyph_names
693
+ end
694
+ end
695
+ end
696
+ end
697
+ end