fontisan 0.2.11 → 0.2.13

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +294 -52
  3. data/Gemfile +5 -0
  4. data/README.adoc +163 -2
  5. data/docs/CONVERSION_GUIDE.adoc +633 -0
  6. data/docs/TYPE1_FONTS.adoc +445 -0
  7. data/lib/fontisan/cli.rb +177 -6
  8. data/lib/fontisan/commands/convert_command.rb +32 -1
  9. data/lib/fontisan/commands/info_command.rb +83 -2
  10. data/lib/fontisan/config/conversion_matrix.yml +132 -4
  11. data/lib/fontisan/constants.rb +12 -0
  12. data/lib/fontisan/conversion_options.rb +378 -0
  13. data/lib/fontisan/converters/collection_converter.rb +45 -10
  14. data/lib/fontisan/converters/format_converter.rb +17 -5
  15. data/lib/fontisan/converters/outline_converter.rb +78 -4
  16. data/lib/fontisan/converters/type1_converter.rb +1234 -0
  17. data/lib/fontisan/font_loader.rb +46 -3
  18. data/lib/fontisan/hints/hint_converter.rb +4 -1
  19. data/lib/fontisan/type1/afm_generator.rb +436 -0
  20. data/lib/fontisan/type1/afm_parser.rb +298 -0
  21. data/lib/fontisan/type1/agl.rb +456 -0
  22. data/lib/fontisan/type1/cff_to_type1_converter.rb +302 -0
  23. data/lib/fontisan/type1/charstring_converter.rb +240 -0
  24. data/lib/fontisan/type1/charstrings.rb +408 -0
  25. data/lib/fontisan/type1/conversion_options.rb +243 -0
  26. data/lib/fontisan/type1/decryptor.rb +183 -0
  27. data/lib/fontisan/type1/encodings.rb +697 -0
  28. data/lib/fontisan/type1/font_dictionary.rb +576 -0
  29. data/lib/fontisan/type1/generator.rb +220 -0
  30. data/lib/fontisan/type1/inf_generator.rb +332 -0
  31. data/lib/fontisan/type1/pfa_generator.rb +369 -0
  32. data/lib/fontisan/type1/pfa_parser.rb +159 -0
  33. data/lib/fontisan/type1/pfb_generator.rb +314 -0
  34. data/lib/fontisan/type1/pfb_parser.rb +166 -0
  35. data/lib/fontisan/type1/pfm_generator.rb +610 -0
  36. data/lib/fontisan/type1/pfm_parser.rb +433 -0
  37. data/lib/fontisan/type1/private_dict.rb +342 -0
  38. data/lib/fontisan/type1/seac_expander.rb +501 -0
  39. data/lib/fontisan/type1/ttf_to_type1_converter.rb +327 -0
  40. data/lib/fontisan/type1/upm_scaler.rb +118 -0
  41. data/lib/fontisan/type1.rb +75 -0
  42. data/lib/fontisan/type1_font.rb +318 -0
  43. data/lib/fontisan/version.rb +1 -1
  44. data/lib/fontisan.rb +2 -0
  45. metadata +30 -3
  46. data/docs/DOCUMENTATION_SUMMARY.md +0 -141
@@ -0,0 +1,456 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Type1
5
+ # Adobe Glyph List (AGL) for Unicode to glyph name mapping
6
+ #
7
+ # [`AGL`](lib/fontisan/type1/agl.rb) provides mapping between Unicode codepoints
8
+ # and glyph names according to the Adobe Glyph List Specification.
9
+ #
10
+ # The AGL defines standard names for glyphs to ensure compatibility across
11
+ # font applications and systems.
12
+ #
13
+ # @see https://github.com/adobe-type-tools/agl-specification
14
+ module AGL
15
+ # Unicode to glyph name mapping (subset of AGL)
16
+ # Includes commonly used glyphs from Latin-1 and Latin Extended-A
17
+ UNICODE_TO_NAME = {
18
+ # ASCII control characters and basic Latin
19
+ 0x0020 => "space",
20
+ 0x0021 => "exclam",
21
+ 0x0022 => "quotedbl",
22
+ 0x0023 => "numbersign",
23
+ 0x0024 => "dollar",
24
+ 0x0025 => "percent",
25
+ 0x0026 => "ampersand",
26
+ 0x0027 => "quotesingle",
27
+ 0x0028 => "parenleft",
28
+ 0x0029 => "parenright",
29
+ 0x002A => "asterisk",
30
+ 0x002B => "plus",
31
+ 0x002C => "comma",
32
+ 0x002D => "hyphen",
33
+ 0x002E => "period",
34
+ 0x002F => "slash",
35
+ 0x0030 => "zero",
36
+ 0x0031 => "one",
37
+ 0x0032 => "two",
38
+ 0x0033 => "three",
39
+ 0x0034 => "four",
40
+ 0x0035 => "five",
41
+ 0x0036 => "six",
42
+ 0x0037 => "seven",
43
+ 0x0038 => "eight",
44
+ 0x0039 => "nine",
45
+ 0x003A => "colon",
46
+ 0x003B => "semicolon",
47
+ 0x003C => "less",
48
+ 0x003D => "equal",
49
+ 0x003E => "greater",
50
+ 0x003F => "question",
51
+ 0x0040 => "at",
52
+ 0x0041 => "A",
53
+ 0x0042 => "B",
54
+ 0x0043 => "C",
55
+ 0x0044 => "D",
56
+ 0x0045 => "E",
57
+ 0x0046 => "F",
58
+ 0x0047 => "G",
59
+ 0x0048 => "H",
60
+ 0x0049 => "I",
61
+ 0x004A => "J",
62
+ 0x004B => "K",
63
+ 0x004C => "L",
64
+ 0x004D => "M",
65
+ 0x004E => "N",
66
+ 0x004F => "O",
67
+ 0x0050 => "P",
68
+ 0x0051 => "Q",
69
+ 0x0052 => "R",
70
+ 0x0053 => "S",
71
+ 0x0054 => "T",
72
+ 0x0055 => "U",
73
+ 0x0056 => "V",
74
+ 0x0057 => "W",
75
+ 0x0058 => "X",
76
+ 0x0059 => "Y",
77
+ 0x005A => "Z",
78
+ 0x005B => "bracketleft",
79
+ 0x005C => "backslash",
80
+ 0x005D => "bracketright",
81
+ 0x005E => "asciicircum",
82
+ 0x005F => "underscore",
83
+ 0x0060 => "grave",
84
+ 0x0061 => "a",
85
+ 0x0062 => "b",
86
+ 0x0063 => "c",
87
+ 0x0064 => "d",
88
+ 0x0065 => "e",
89
+ 0x0066 => "f",
90
+ 0x0067 => "g",
91
+ 0x0068 => "h",
92
+ 0x0069 => "i",
93
+ 0x006A => "j",
94
+ 0x006B => "k",
95
+ 0x006C => "l",
96
+ 0x006D => "m",
97
+ 0x006E => "n",
98
+ 0x006F => "o",
99
+ 0x0070 => "p",
100
+ 0x0071 => "q",
101
+ 0x0072 => "r",
102
+ 0x0073 => "s",
103
+ 0x0074 => "t",
104
+ 0x0075 => "u",
105
+ 0x0076 => "v",
106
+ 0x0077 => "w",
107
+ 0x0078 => "x",
108
+ 0x0079 => "y",
109
+ 0x007A => "z",
110
+ 0x007B => "braceleft",
111
+ 0x007C => "bar",
112
+ 0x007D => "braceright",
113
+ 0x007E => "asciitilde",
114
+ # Latin-1 Supplement
115
+ 0x00A0 => "space",
116
+ 0x00A1 => "exclamdown",
117
+ 0x00A2 => "cent",
118
+ 0x00A3 => "sterling",
119
+ 0x00A4 => "currency",
120
+ 0x00A5 => "yen",
121
+ 0x00A6 => "brokenbar",
122
+ 0x00A7 => "section",
123
+ 0x00A8 => "dieresis",
124
+ 0x00A9 => "copyright",
125
+ 0x00AA => "ordfeminine",
126
+ 0x00AB => "guillemotleft",
127
+ 0x00AC => "logicalnot",
128
+ 0x00AD => "hyphen",
129
+ 0x00AE => "registered",
130
+ 0x00AF => "macron",
131
+ 0x00B0 => "degree",
132
+ 0x00B1 => "plusminus",
133
+ 0x00B2 => "twosuperior",
134
+ 0x00B3 => "threesuperior",
135
+ 0x00B4 => "acute",
136
+ 0x00B5 => "mu",
137
+ 0x00B6 => "paragraph",
138
+ 0x00B7 => "periodcentered",
139
+ 0x00B8 => "cedilla",
140
+ 0x00B9 => "onesuperior",
141
+ 0x00BA => "ordmasculine",
142
+ 0x00BB => "guillemotright",
143
+ 0x00BC => "onequarter",
144
+ 0x00BD => "onehalf",
145
+ 0x00BE => "threequarters",
146
+ 0x00BF => "questiondown",
147
+ 0x00C0 => "Agrave",
148
+ 0x00C1 => "Aacute",
149
+ 0x00C2 => "Acircumflex",
150
+ 0x00C3 => "Atilde",
151
+ 0x00C4 => "Adieresis",
152
+ 0x00C5 => "Aring",
153
+ 0x00C6 => "AE",
154
+ 0x00C7 => "Ccedilla",
155
+ 0x00C8 => "Egrave",
156
+ 0x00C9 => "Eacute",
157
+ 0x00CA => "Ecircumflex",
158
+ 0x00CB => "Edieresis",
159
+ 0x00CC => "Igrave",
160
+ 0x00CD => "Iacute",
161
+ 0x00CE => "Icircumflex",
162
+ 0x00CF => "Idieresis",
163
+ 0x00D0 => "Eth",
164
+ 0x00D1 => "Ntilde",
165
+ 0x00D2 => "Ograve",
166
+ 0x00D3 => "Oacute",
167
+ 0x00D4 => "Ocircumflex",
168
+ 0x00D5 => "Otilde",
169
+ 0x00D6 => "Odieresis",
170
+ 0x00D7 => "multiply",
171
+ 0x00D8 => "Oslash",
172
+ 0x00D9 => "Ugrave",
173
+ 0x00DA => "Uacute",
174
+ 0x00DB => "Ucircumflex",
175
+ 0x00DC => "Udieresis",
176
+ 0x00DD => "Yacute",
177
+ 0x00DE => "Thorn",
178
+ 0x00DF => "germandbls",
179
+ 0x00E0 => "agrave",
180
+ 0x00E1 => "aacute",
181
+ 0x00E2 => "acircumflex",
182
+ 0x00E3 => "atilde",
183
+ 0x00E4 => "adieresis",
184
+ 0x00E5 => "aring",
185
+ 0x00E6 => "ae",
186
+ 0x00E7 => "ccedilla",
187
+ 0x00E8 => "egrave",
188
+ 0x00E9 => "eacute",
189
+ 0x00EA => "ecircumflex",
190
+ 0x00EB => "edieresis",
191
+ 0x00EC => "igrave",
192
+ 0x00ED => "iacute",
193
+ 0x00EE => "icircumflex",
194
+ 0x00EF => "idieresis",
195
+ 0x00F0 => "eth",
196
+ 0x00F1 => "ntilde",
197
+ 0x00F2 => "ograve",
198
+ 0x00F3 => "oacute",
199
+ 0x00F4 => "ocircumflex",
200
+ 0x00F5 => "otilde",
201
+ 0x00F6 => "odieresis",
202
+ 0x00F7 => "divide",
203
+ 0x00F8 => "oslash",
204
+ 0x00F9 => "ugrave",
205
+ 0x00FA => "uacute",
206
+ 0x00FB => "ucircumflex",
207
+ 0x00FC => "udieresis",
208
+ 0x00FD => "yacute",
209
+ 0x00FE => "thorn",
210
+ 0x00FF => "ydieresis",
211
+ # Latin Extended-A
212
+ 0x0100 => "Amacron",
213
+ 0x0101 => "amacron",
214
+ 0x0102 => "Abreve",
215
+ 0x0103 => "abreve",
216
+ 0x0104 => "Aogonek",
217
+ 0x0105 => "aogonek",
218
+ 0x0106 => "Cacute",
219
+ 0x0107 => "cacute",
220
+ 0x0108 => "Ccircumflex",
221
+ 0x0109 => "ccircumflex",
222
+ 0x010A => "Cdotaccent",
223
+ 0x010B => "cdotaccent",
224
+ 0x010C => "Ccaron",
225
+ 0x010D => "ccaron",
226
+ 0x010E => "Dcaron",
227
+ 0x010F => "dcaron",
228
+ 0x0110 => "Dcroat",
229
+ 0x0111 => "dcroat",
230
+ 0x0112 => "Emacron",
231
+ 0x0113 => "emacron",
232
+ 0x0114 => "Ebreve",
233
+ 0x0115 => "ebreve",
234
+ 0x0116 => "Edotaccent",
235
+ 0x0117 => "edotaccent",
236
+ 0x0118 => "Eogonek",
237
+ 0x0119 => "eogonek",
238
+ 0x011A => "Ecaron",
239
+ 0x011B => "ecaron",
240
+ 0x011C => "Gcircumflex",
241
+ 0x011D => "gcircumflex",
242
+ 0x011E => "Gbreve",
243
+ 0x011F => "gbreve",
244
+ 0x0120 => "Gdotaccent",
245
+ 0x0121 => "gdotaccent",
246
+ 0x0122 => "Gcommaaccent",
247
+ 0x0123 => "gcommaaccent",
248
+ 0x0124 => "Hcircumflex",
249
+ 0x0125 => "hcircumflex",
250
+ 0x0126 => "Hbar",
251
+ 0x0127 => "hbar",
252
+ 0x0128 => "Itilde",
253
+ 0x0129 => "itilde",
254
+ 0x012A => "Imacron",
255
+ 0x012B => "imacron",
256
+ 0x012C => "Ibreve",
257
+ 0x012D => "ibreve",
258
+ 0x012E => "Iogonek",
259
+ 0x012F => "iogonek",
260
+ 0x0130 => "Idotaccent",
261
+ 0x0131 => "dotlessi",
262
+ 0x0132 => "Lig",
263
+ 0x0133 => "lig",
264
+ 0x0134 => "Lslash",
265
+ 0x0135 => "lslash",
266
+ 0x0136 => "Nacute",
267
+ 0x0137 => "nacute",
268
+ 0x0138 => "kgreenlandic",
269
+ 0x0139 => "Ncommaaccent",
270
+ 0x013A => "ncommaaccent",
271
+ 0x013B => "Ncaron",
272
+ 0x013C => "ncaron",
273
+ 0x013D => "napostrophe",
274
+ 0x013E => "Eng",
275
+ 0x013F => "eng",
276
+ 0x0140 => "Omacron",
277
+ 0x0141 => "omacron",
278
+ 0x0142 => "Obreve",
279
+ 0x0143 => "obreve",
280
+ 0x0144 => "Ohungarumlaut",
281
+ 0x0145 => "ohungarumlaut",
282
+ 0x0146 => "Oogonek",
283
+ 0x0147 => "oogonek",
284
+ 0x0148 => "Racute",
285
+ 0x0149 => "racute",
286
+ 0x014A => "Rcaron",
287
+ 0x014B => "rcaron",
288
+ 0x014C => "Sacute",
289
+ 0x014D => "sacute",
290
+ 0x014E => "Scircumflex",
291
+ 0x014F => "scircumflex",
292
+ 0x0150 => "Scedilla",
293
+ 0x0151 => "scedilla",
294
+ 0x0152 => "Scaron",
295
+ 0x0153 => "scaron",
296
+ 0x0154 => "Tcommaaccent",
297
+ 0x0155 => "tcommaaccent",
298
+ 0x0156 => "Tcaron",
299
+ 0x0157 => "tcaron",
300
+ 0x0158 => "Tbar",
301
+ 0x0159 => "tbar",
302
+ 0x015A => "Utilde",
303
+ 0x015B => "utilde",
304
+ 0x015C => "Umacron",
305
+ 0x015D => "umacron",
306
+ 0x015E => "Ubreve",
307
+ 0x015F => "ubreve",
308
+ 0x0160 => "Uring",
309
+ 0x0161 => "uring",
310
+ 0x0162 => "Uhungarumlaut",
311
+ 0x0163 => "uhungarumlaut",
312
+ 0x0164 => "Uogonek",
313
+ 0x0165 => "uogonek",
314
+ 0x0166 => "Wcircumflex",
315
+ 0x0167 => "wcircumflex",
316
+ 0x0168 => "Ycircumflex",
317
+ 0x0169 => "ycircumflex",
318
+ 0x016A => "Zacute",
319
+ 0x016B => "zacute",
320
+ 0x016C => "Zdotaccent",
321
+ 0x016D => "zdotaccent",
322
+ 0x016E => "Zcaron",
323
+ 0x016F => "zcaron",
324
+ 0x0170 => "longs",
325
+ 0x0171 => "caron",
326
+ 0x0172 => "breve",
327
+ 0x0173 => "dotaccent",
328
+ 0x0174 => "ring",
329
+ 0x0175 => "ogonek",
330
+ 0x0176 => "tilde",
331
+ 0x0177 => "hungarumlaut",
332
+ 0x0178 => "commaaccent",
333
+ 0x0179 => "slash",
334
+ 0x017A => "hyphen",
335
+ 0x017B => "period",
336
+ 0x017F => "florin",
337
+ # Greek (some common)
338
+ 0x0391 => "Alpha",
339
+ 0x0392 => "Beta",
340
+ 0x0393 => "Gamma",
341
+ 0x0394 => "Delta",
342
+ 0x0395 => "Epsilon",
343
+ 0x0396 => "Zeta",
344
+ 0x0397 => "Eta",
345
+ 0x0398 => "Theta",
346
+ 0x0399 => "Iota",
347
+ 0x039A => "Kappa",
348
+ 0x039B => "Lambda",
349
+ 0x039C => "Mu",
350
+ 0x039D => "Nu",
351
+ 0x039E => "Xi",
352
+ 0x039F => "Omicron",
353
+ 0x03A0 => "Pi",
354
+ 0x03A1 => "Rho",
355
+ 0x03A3 => "Sigma",
356
+ 0x03A4 => "Tau",
357
+ 0x03A5 => "Upsilon",
358
+ 0x03A6 => "Phi",
359
+ 0x03A7 => "Chi",
360
+ 0x03A8 => "Psi",
361
+ 0x03A9 => "Omega",
362
+ # Currency and other symbols
363
+ 0x20AC => "Euro",
364
+ 0x2113 => "literalsign",
365
+ 0x2116 => "numerosign",
366
+ 0x2122 => "trademark",
367
+ 0x2126 => "Omega",
368
+ 0x212E => "estimated",
369
+ 0x2202 => "partialdiff",
370
+ 0x2206 => "Delta",
371
+ 0x220F => "product",
372
+ 0x2211 => "summation",
373
+ 0x221A => "radical",
374
+ 0x221E => "infinity",
375
+ 0x222B => "integral",
376
+ 0x2248 => "approxequal",
377
+ 0x2260 => "notequal",
378
+ 0x2264 => "lessequal",
379
+ 0x2265 => "greaterequal",
380
+ }.freeze
381
+
382
+ # Glyph name to Unicode mapping (inverse of UNICODE_TO_NAME)
383
+ # When duplicates exist, uses the first (lowest) codepoint
384
+ NAME_TO_UNICODE = begin
385
+ result = {}
386
+ UNICODE_TO_NAME.each do |codepoint, name|
387
+ result[name] ||= codepoint # Only set first occurrence
388
+ end
389
+ result.freeze
390
+ end
391
+
392
+ # Get glyph name for Unicode codepoint
393
+ #
394
+ # @param codepoint [Integer] Unicode codepoint
395
+ # @return [String] Glyph name from AGL, or uniXXXX format if not found
396
+ def self.glyph_name_for_unicode(codepoint)
397
+ UNICODE_TO_NAME[codepoint] || generate_uni_name(codepoint)
398
+ end
399
+
400
+ # Get Unicode codepoint for glyph name
401
+ #
402
+ # @param name [String] Glyph name
403
+ # @return [Integer, nil] Unicode codepoint or nil if not found
404
+ def self.unicode_for_glyph_name(name)
405
+ # Try direct lookup
406
+ code = NAME_TO_UNICODE[name]
407
+ return code if code
408
+
409
+ # Try parsing uniXXXX or uXXXXX format
410
+ parse_uni_name(name)
411
+ end
412
+
413
+ # Check if a glyph name is in the AGL
414
+ #
415
+ # @param name [String] Glyph name
416
+ # @return [Boolean] True if name is in AGL
417
+ def self.agl_include?(name)
418
+ NAME_TO_UNICODE.key?(name)
419
+ end
420
+
421
+ # Generate uniXXXX name for codepoint not in AGL
422
+ #
423
+ # @param codepoint [Integer] Unicode codepoint
424
+ # @return [String] uniXXXX name
425
+ def self.generate_uni_name(codepoint)
426
+ format("uni%04X", codepoint)
427
+ end
428
+
429
+ # Parse uniXXXX or uXXXXX glyph name
430
+ #
431
+ # @param name [String] Glyph name in uni/u format
432
+ # @return [Integer, nil] Unicode codepoint or nil if not a uni name
433
+ def self.parse_uni_name(name)
434
+ if name =~ /^uni([0-9A-Fa-f]{4})$/
435
+ $1.to_i(16)
436
+ elsif name =~ /^u([0-9A-Fa-f]+)$/
437
+ $1.to_i(16)
438
+ end
439
+ end
440
+
441
+ # Get all AGL glyph names
442
+ #
443
+ # @return [Array<String>] All glyph names in the AGL subset
444
+ def self.all_glyph_names
445
+ NAME_TO_UNICODE.keys.sort
446
+ end
447
+
448
+ # Get all Unicode codepoints in AGL
449
+ #
450
+ # @return [Array<Integer>] All Unicode codepoints in the AGL subset
451
+ def self.all_codepoints
452
+ UNICODE_TO_NAME.keys.sort
453
+ end
454
+ end
455
+ end
456
+ end