@vaadin-component-factory/vcf-pdf-viewer 0.9.0 → 0.9.4

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 (174) hide show
  1. package/README.md +1 -1
  2. package/package.json +33 -18
  3. package/{src/display → pdfjs/dist}/display_utils.js +344 -139
  4. package/{src/display → pdfjs/dist}/fetch_stream.js +115 -97
  5. package/pdfjs/dist/get.js +1857 -0
  6. package/pdfjs/dist/index.js +767 -0
  7. package/pdfjs/dist/l10n_utils.js +140 -0
  8. package/{src/shared → pdfjs/dist}/message_handler.js +243 -259
  9. package/{src/display → pdfjs/dist}/network.js +149 -87
  10. package/{src/display/content_disposition.js → pdfjs/dist/network_utils.js} +167 -55
  11. package/{src/display → pdfjs/dist}/node_stream.js +133 -98
  12. package/pdfjs/dist/pdf.js +12778 -0
  13. package/pdfjs/dist/pdf_link_service.js +638 -0
  14. package/pdfjs/dist/pdf_rendering_queue.js +199 -0
  15. package/pdfjs/dist/pdf_thumbnail_viewer.js +819 -0
  16. package/pdfjs/dist/pdf_viewer.js +3598 -0
  17. package/pdfjs/dist/typeof.js +100 -0
  18. package/pdfjs/dist/ui_utils.js +1033 -0
  19. package/{src/shared → pdfjs/dist}/util.js +301 -287
  20. package/pdfjs/dist/worker.js +62813 -0
  21. package/src/vcf-pdf-viewer.js +77 -27
  22. package/theme/lumo/vcf-pdf-viewer-styles.js +1 -1
  23. package/theme/material/vcf-pdf-viewer.js +2 -2
  24. package/src/core/.eslintrc +0 -13
  25. package/src/core/annotation.js +0 -2948
  26. package/src/core/arithmetic_decoder.js +0 -182
  27. package/src/core/ascii_85_stream.js +0 -98
  28. package/src/core/ascii_hex_stream.js +0 -79
  29. package/src/core/base_stream.js +0 -110
  30. package/src/core/bidi.js +0 -438
  31. package/src/core/calibri_factors.js +0 -308
  32. package/src/core/catalog.js +0 -1459
  33. package/src/core/ccitt.js +0 -1062
  34. package/src/core/ccitt_stream.js +0 -60
  35. package/src/core/cff_font.js +0 -116
  36. package/src/core/cff_parser.js +0 -1949
  37. package/src/core/charsets.js +0 -119
  38. package/src/core/chunked_stream.js +0 -557
  39. package/src/core/cmap.js +0 -1039
  40. package/src/core/colorspace.js +0 -1533
  41. package/src/core/core_utils.js +0 -464
  42. package/src/core/crypto.js +0 -1900
  43. package/src/core/decode_stream.js +0 -170
  44. package/src/core/decrypt_stream.js +0 -59
  45. package/src/core/default_appearance.js +0 -99
  46. package/src/core/document.js +0 -1456
  47. package/src/core/encodings.js +0 -301
  48. package/src/core/evaluator.js +0 -4601
  49. package/src/core/file_spec.js +0 -108
  50. package/src/core/flate_stream.js +0 -402
  51. package/src/core/font_renderer.js +0 -882
  52. package/src/core/fonts.js +0 -3260
  53. package/src/core/fonts_utils.js +0 -221
  54. package/src/core/function.js +0 -1257
  55. package/src/core/glyf.js +0 -706
  56. package/src/core/glyphlist.js +0 -4558
  57. package/src/core/helvetica_factors.js +0 -353
  58. package/src/core/image.js +0 -802
  59. package/src/core/image_utils.js +0 -291
  60. package/src/core/jbig2.js +0 -2572
  61. package/src/core/jbig2_stream.js +0 -73
  62. package/src/core/jpeg_stream.js +0 -105
  63. package/src/core/jpg.js +0 -1416
  64. package/src/core/jpx.js +0 -2343
  65. package/src/core/jpx_stream.js +0 -87
  66. package/src/core/liberationsans_widths.js +0 -221
  67. package/src/core/lzw_stream.js +0 -150
  68. package/src/core/metadata_parser.js +0 -146
  69. package/src/core/metrics.js +0 -2970
  70. package/src/core/murmurhash3.js +0 -139
  71. package/src/core/myriadpro_factors.js +0 -290
  72. package/src/core/name_number_tree.js +0 -153
  73. package/src/core/object_loader.js +0 -149
  74. package/src/core/opentype_file_builder.js +0 -154
  75. package/src/core/operator_list.js +0 -734
  76. package/src/core/parser.js +0 -1416
  77. package/src/core/pattern.js +0 -985
  78. package/src/core/pdf_manager.js +0 -217
  79. package/src/core/predictor_stream.js +0 -238
  80. package/src/core/primitives.js +0 -402
  81. package/src/core/ps_parser.js +0 -272
  82. package/src/core/run_length_stream.js +0 -61
  83. package/src/core/segoeui_factors.js +0 -308
  84. package/src/core/standard_fonts.js +0 -817
  85. package/src/core/stream.js +0 -103
  86. package/src/core/struct_tree.js +0 -335
  87. package/src/core/to_unicode_map.js +0 -103
  88. package/src/core/type1_font.js +0 -421
  89. package/src/core/type1_parser.js +0 -776
  90. package/src/core/unicode.js +0 -1649
  91. package/src/core/worker.js +0 -848
  92. package/src/core/worker_stream.js +0 -135
  93. package/src/core/writer.js +0 -278
  94. package/src/core/xfa/bind.js +0 -652
  95. package/src/core/xfa/builder.js +0 -207
  96. package/src/core/xfa/config.js +0 -1926
  97. package/src/core/xfa/connection_set.js +0 -202
  98. package/src/core/xfa/data.js +0 -82
  99. package/src/core/xfa/datasets.js +0 -76
  100. package/src/core/xfa/factory.js +0 -111
  101. package/src/core/xfa/fonts.js +0 -181
  102. package/src/core/xfa/formcalc_lexer.js +0 -385
  103. package/src/core/xfa/formcalc_parser.js +0 -1340
  104. package/src/core/xfa/html_utils.js +0 -639
  105. package/src/core/xfa/layout.js +0 -383
  106. package/src/core/xfa/locale_set.js +0 -345
  107. package/src/core/xfa/namespaces.js +0 -81
  108. package/src/core/xfa/parser.js +0 -184
  109. package/src/core/xfa/setup.js +0 -38
  110. package/src/core/xfa/signature.js +0 -40
  111. package/src/core/xfa/som.js +0 -338
  112. package/src/core/xfa/stylesheet.js +0 -40
  113. package/src/core/xfa/template.js +0 -6260
  114. package/src/core/xfa/text.js +0 -290
  115. package/src/core/xfa/unknown.js +0 -29
  116. package/src/core/xfa/utils.js +0 -217
  117. package/src/core/xfa/xdp.js +0 -59
  118. package/src/core/xfa/xfa_object.js +0 -1130
  119. package/src/core/xfa/xhtml.js +0 -543
  120. package/src/core/xfa_fonts.js +0 -208
  121. package/src/core/xml_parser.js +0 -507
  122. package/src/core/xref.js +0 -899
  123. package/src/display/annotation_layer.js +0 -2107
  124. package/src/display/annotation_storage.js +0 -113
  125. package/src/display/api.js +0 -3292
  126. package/src/display/base_factory.js +0 -180
  127. package/src/display/canvas.js +0 -2828
  128. package/src/display/font_loader.js +0 -484
  129. package/src/display/metadata.js +0 -41
  130. package/src/display/network_utils.js +0 -100
  131. package/src/display/node_utils.js +0 -83
  132. package/src/display/optional_content_config.js +0 -189
  133. package/src/display/pattern_helper.js +0 -659
  134. package/src/display/svg.js +0 -1709
  135. package/src/display/text_layer.js +0 -847
  136. package/src/display/transport_stream.js +0 -303
  137. package/src/display/worker_options.js +0 -40
  138. package/src/display/xfa_layer.js +0 -204
  139. package/src/doc_helper.js +0 -25
  140. package/src/images/logo.svg +0 -41
  141. package/src/interfaces.js +0 -169
  142. package/src/license_header.js +0 -14
  143. package/src/license_header_libre.js +0 -21
  144. package/src/pdf.image_decoders.js +0 -46
  145. package/src/pdf.js +0 -146
  146. package/src/pdf.sandbox.external.js +0 -181
  147. package/src/pdf.sandbox.js +0 -151
  148. package/src/pdf.scripting.js +0 -25
  149. package/src/pdf.worker.entry.js +0 -19
  150. package/src/pdf.worker.js +0 -23
  151. package/src/scripting_api/aform.js +0 -608
  152. package/src/scripting_api/app.js +0 -621
  153. package/src/scripting_api/color.js +0 -129
  154. package/src/scripting_api/common.js +0 -58
  155. package/src/scripting_api/console.js +0 -38
  156. package/src/scripting_api/constants.js +0 -208
  157. package/src/scripting_api/doc.js +0 -1195
  158. package/src/scripting_api/error.js +0 -23
  159. package/src/scripting_api/event.js +0 -232
  160. package/src/scripting_api/field.js +0 -620
  161. package/src/scripting_api/fullscreen.js +0 -145
  162. package/src/scripting_api/initialization.js +0 -223
  163. package/src/scripting_api/pdf_object.js +0 -24
  164. package/src/scripting_api/print_params.js +0 -146
  165. package/src/scripting_api/proxy.js +0 -139
  166. package/src/scripting_api/thermometer.js +0 -69
  167. package/src/scripting_api/util.js +0 -581
  168. package/src/shared/.eslintrc +0 -13
  169. package/src/shared/cffStandardStrings.js +0 -311
  170. package/src/shared/compatibility.js +0 -114
  171. package/src/shared/fonts_utils.js +0 -429
  172. package/src/shared/is_node.js +0 -27
  173. package/src/shared/scripting_utils.js +0 -85
  174. package/src/worker_loader.js +0 -32
package/src/core/fonts.js DELETED
@@ -1,3260 +0,0 @@
1
- /* Copyright 2012 Mozilla Foundation
2
- *
3
- * Licensed under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License.
5
- * You may obtain a copy of the License at
6
- *
7
- * http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software
10
- * distributed under the License is distributed on an "AS IS" BASIS,
11
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- * See the License for the specific language governing permissions and
13
- * limitations under the License.
14
- */
15
-
16
- import {
17
- assert,
18
- bytesToString,
19
- FONT_IDENTITY_MATRIX,
20
- FontType,
21
- FormatError,
22
- info,
23
- isNum,
24
- shadow,
25
- string32,
26
- warn,
27
- } from "../shared/util.js";
28
- import { CFFCompiler, CFFParser } from "./cff_parser.js";
29
- import {
30
- FontFlags,
31
- getFontType,
32
- MacStandardGlyphOrdering,
33
- normalizeFontName,
34
- recoverGlyphName,
35
- SEAC_ANALYSIS_ENABLED,
36
- } from "./fonts_utils.js";
37
- import { getDingbatsGlyphsUnicode, getGlyphsUnicode } from "./glyphlist.js";
38
- import {
39
- getEncoding,
40
- MacRomanEncoding,
41
- StandardEncoding,
42
- SymbolSetEncoding,
43
- ZapfDingbatsEncoding,
44
- } from "./encodings.js";
45
- import {
46
- getGlyphMapForStandardFonts,
47
- getNonStdFontMap,
48
- getStdFontMap,
49
- getSupplementalGlyphMapForArialBlack,
50
- getSupplementalGlyphMapForCalibri,
51
- } from "./standard_fonts.js";
52
- import {
53
- getUnicodeForGlyph,
54
- getUnicodeRangeFor,
55
- mapSpecialUnicodeValues,
56
- } from "./unicode.js";
57
- import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js";
58
- import { CFFFont } from "./cff_font.js";
59
- import { FontRendererFactory } from "./font_renderer.js";
60
- import { GlyfTable } from "./glyf.js";
61
- import { IdentityCMap } from "./cmap.js";
62
- import { OpenTypeFileBuilder } from "./opentype_file_builder.js";
63
- import { readUint32 } from "./core_utils.js";
64
- import { Stream } from "./stream.js";
65
- import { Type1Font } from "./type1_font.js";
66
-
67
- // Unicode Private Use Areas:
68
- const PRIVATE_USE_AREAS = [
69
- [0xe000, 0xf8ff], // BMP (0)
70
- [0x100000, 0x10fffd], // PUP (16)
71
- ];
72
-
73
- // PDF Glyph Space Units are one Thousandth of a TextSpace Unit
74
- // except for Type 3 fonts
75
- const PDF_GLYPH_SPACE_UNITS = 1000;
76
-
77
- const EXPORT_DATA_PROPERTIES = [
78
- "ascent",
79
- "bbox",
80
- "black",
81
- "bold",
82
- "charProcOperatorList",
83
- "composite",
84
- "cssFontInfo",
85
- "data",
86
- "defaultVMetrics",
87
- "defaultWidth",
88
- "descent",
89
- "fallbackName",
90
- "fontMatrix",
91
- "fontType",
92
- "isMonospace",
93
- "isSerifFont",
94
- "isType3Font",
95
- "italic",
96
- "loadedName",
97
- "mimetype",
98
- "missingFile",
99
- "name",
100
- "remeasure",
101
- "subtype",
102
- "type",
103
- "vertical",
104
- ];
105
-
106
- const EXPORT_DATA_EXTRA_PROPERTIES = [
107
- "cMap",
108
- "defaultEncoding",
109
- "differences",
110
- "isSymbolicFont",
111
- "seacMap",
112
- "toFontChar",
113
- "toUnicode",
114
- "vmetrics",
115
- "widths",
116
- ];
117
-
118
- function adjustWidths(properties) {
119
- if (!properties.fontMatrix) {
120
- return;
121
- }
122
- if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) {
123
- return;
124
- }
125
- // adjusting width to fontMatrix scale
126
- const scale = 0.001 / properties.fontMatrix[0];
127
- const glyphsWidths = properties.widths;
128
- for (const glyph in glyphsWidths) {
129
- glyphsWidths[glyph] *= scale;
130
- }
131
- properties.defaultWidth *= scale;
132
- }
133
-
134
- function adjustToUnicode(properties, builtInEncoding) {
135
- if (properties.isInternalFont) {
136
- return;
137
- }
138
- if (builtInEncoding === properties.defaultEncoding) {
139
- return; // No point in trying to adjust `toUnicode` if the encodings match.
140
- }
141
- if (properties.toUnicode instanceof IdentityToUnicodeMap) {
142
- return;
143
- }
144
- const toUnicode = [],
145
- glyphsUnicodeMap = getGlyphsUnicode();
146
- for (const charCode in builtInEncoding) {
147
- if (properties.hasIncludedToUnicodeMap) {
148
- if (properties.toUnicode.has(charCode)) {
149
- continue; // The font dictionary has a `ToUnicode` entry.
150
- }
151
- } else {
152
- if (
153
- properties.hasEncoding &&
154
- properties.differences[charCode] !== undefined
155
- ) {
156
- continue; // The font dictionary has an `Encoding`/`Differences` entry.
157
- }
158
- }
159
- const glyphName = builtInEncoding[charCode];
160
- const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
161
- if (unicode !== -1) {
162
- toUnicode[charCode] = String.fromCharCode(unicode);
163
- }
164
- }
165
- if (toUnicode.length > 0) {
166
- properties.toUnicode.amend(toUnicode);
167
- }
168
- }
169
-
170
- /**
171
- * NOTE: This function should only be called at the *end* of font-parsing,
172
- * after e.g. `adjustToUnicode` has run, to prevent any issues.
173
- */
174
- function amendFallbackToUnicode(properties) {
175
- if (!properties.fallbackToUnicode) {
176
- return;
177
- }
178
- if (properties.toUnicode instanceof IdentityToUnicodeMap) {
179
- return;
180
- }
181
- const toUnicode = [];
182
- for (const charCode in properties.fallbackToUnicode) {
183
- if (properties.toUnicode.has(charCode)) {
184
- continue; // The font dictionary has a `ToUnicode` entry.
185
- }
186
- toUnicode[charCode] = properties.fallbackToUnicode[charCode];
187
- }
188
- if (toUnicode.length > 0) {
189
- properties.toUnicode.amend(toUnicode);
190
- }
191
- }
192
-
193
- class Glyph {
194
- constructor(
195
- originalCharCode,
196
- fontChar,
197
- unicode,
198
- accent,
199
- width,
200
- vmetric,
201
- operatorListId,
202
- isSpace,
203
- isInFont
204
- ) {
205
- this.originalCharCode = originalCharCode;
206
- this.fontChar = fontChar;
207
- this.unicode = unicode;
208
- this.accent = accent;
209
- this.width = width;
210
- this.vmetric = vmetric;
211
- this.operatorListId = operatorListId;
212
- this.isSpace = isSpace;
213
- this.isInFont = isInFont;
214
- }
215
-
216
- matchesForCache(
217
- originalCharCode,
218
- fontChar,
219
- unicode,
220
- accent,
221
- width,
222
- vmetric,
223
- operatorListId,
224
- isSpace,
225
- isInFont
226
- ) {
227
- return (
228
- this.originalCharCode === originalCharCode &&
229
- this.fontChar === fontChar &&
230
- this.unicode === unicode &&
231
- this.accent === accent &&
232
- this.width === width &&
233
- this.vmetric === vmetric &&
234
- this.operatorListId === operatorListId &&
235
- this.isSpace === isSpace &&
236
- this.isInFont === isInFont
237
- );
238
- }
239
- }
240
-
241
- function int16(b0, b1) {
242
- return (b0 << 8) + b1;
243
- }
244
-
245
- function writeSignedInt16(bytes, index, value) {
246
- bytes[index + 1] = value;
247
- bytes[index] = value >>> 8;
248
- }
249
-
250
- function signedInt16(b0, b1) {
251
- const value = (b0 << 8) + b1;
252
- return value & (1 << 15) ? value - 0x10000 : value;
253
- }
254
-
255
- function int32(b0, b1, b2, b3) {
256
- return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
257
- }
258
-
259
- function string16(value) {
260
- if (
261
- typeof PDFJSDev === "undefined" ||
262
- PDFJSDev.test("!PRODUCTION || TESTING")
263
- ) {
264
- assert(
265
- typeof value === "number" && Math.abs(value) < 2 ** 16,
266
- `string16: Unexpected input "${value}".`
267
- );
268
- }
269
- return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
270
- }
271
-
272
- function safeString16(value) {
273
- if (
274
- typeof PDFJSDev === "undefined" ||
275
- PDFJSDev.test("!PRODUCTION || TESTING")
276
- ) {
277
- assert(
278
- typeof value === "number" && !Number.isNaN(value),
279
- `safeString16: Unexpected input "${value}".`
280
- );
281
- }
282
- // clamp value to the 16-bit int range
283
- if (value > 0x7fff) {
284
- value = 0x7fff;
285
- } else if (value < -0x8000) {
286
- value = -0x8000;
287
- }
288
- return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
289
- }
290
-
291
- function isTrueTypeFile(file) {
292
- const header = file.peekBytes(4);
293
- return (
294
- readUint32(header, 0) === 0x00010000 || bytesToString(header) === "true"
295
- );
296
- }
297
-
298
- function isTrueTypeCollectionFile(file) {
299
- const header = file.peekBytes(4);
300
- return bytesToString(header) === "ttcf";
301
- }
302
-
303
- function isOpenTypeFile(file) {
304
- const header = file.peekBytes(4);
305
- return bytesToString(header) === "OTTO";
306
- }
307
-
308
- function isType1File(file) {
309
- const header = file.peekBytes(2);
310
- // All Type1 font programs must begin with the comment '%!' (0x25 + 0x21).
311
- if (header[0] === 0x25 && header[1] === 0x21) {
312
- return true;
313
- }
314
- // ... obviously some fonts violate that part of the specification,
315
- // please refer to the comment in |Type1Font| below (pfb file header).
316
- if (header[0] === 0x80 && header[1] === 0x01) {
317
- return true;
318
- }
319
- return false;
320
- }
321
-
322
- /**
323
- * Compared to other font formats, the header in CFF files is not constant
324
- * but contains version numbers. To reduce the possibility of misclassifying
325
- * font files as CFF, it's recommended to check for other font formats first.
326
- */
327
- function isCFFFile(file) {
328
- const header = file.peekBytes(4);
329
- if (
330
- /* major version, [1, 255] */ header[0] >= 1 &&
331
- /* minor version, [0, 255]; header[1] */
332
- /* header size, [0, 255]; header[2] */
333
- /* offset(0) size, [1, 4] */ header[3] >= 1 &&
334
- header[3] <= 4
335
- ) {
336
- return true;
337
- }
338
- return false;
339
- }
340
-
341
- function getFontFileType(file, { type, subtype, composite }) {
342
- let fileType, fileSubtype;
343
-
344
- if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {
345
- if (composite) {
346
- fileType = "CIDFontType2";
347
- } else {
348
- fileType = "TrueType";
349
- }
350
- } else if (isOpenTypeFile(file)) {
351
- if (composite) {
352
- fileType = "CIDFontType2";
353
- } else {
354
- fileType = "OpenType";
355
- }
356
- } else if (isType1File(file)) {
357
- if (composite) {
358
- fileType = "CIDFontType0";
359
- } else {
360
- fileType = type === "MMType1" ? "MMType1" : "Type1";
361
- }
362
- } else if (isCFFFile(file)) {
363
- if (composite) {
364
- fileType = "CIDFontType0";
365
- fileSubtype = "CIDFontType0C";
366
- } else {
367
- fileType = type === "MMType1" ? "MMType1" : "Type1";
368
- fileSubtype = "Type1C";
369
- }
370
- } else {
371
- warn("getFontFileType: Unable to detect correct font file Type/Subtype.");
372
- fileType = type;
373
- fileSubtype = subtype;
374
- }
375
-
376
- return [fileType, fileSubtype];
377
- }
378
-
379
- function buildToFontChar(encoding, glyphsUnicodeMap, differences) {
380
- const toFontChar = [];
381
- let unicode;
382
- for (let i = 0, ii = encoding.length; i < ii; i++) {
383
- unicode = getUnicodeForGlyph(encoding[i], glyphsUnicodeMap);
384
- if (unicode !== -1) {
385
- toFontChar[i] = unicode;
386
- }
387
- }
388
- for (const charCode in differences) {
389
- unicode = getUnicodeForGlyph(differences[charCode], glyphsUnicodeMap);
390
- if (unicode !== -1) {
391
- toFontChar[+charCode] = unicode;
392
- }
393
- }
394
- return toFontChar;
395
- }
396
-
397
- /**
398
- * Rebuilds the char code to glyph ID map by moving all char codes to the
399
- * private use area. This is done to avoid issues with various problematic
400
- * unicode areas where either a glyph won't be drawn or is deformed by a
401
- * shaper.
402
- * @returns {Object} Two properties:
403
- * 'toFontChar' - maps original char codes(the value that will be read
404
- * from commands such as show text) to the char codes that will be used in the
405
- * font that we build
406
- * 'charCodeToGlyphId' - maps the new font char codes to glyph ids
407
- */
408
- function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
409
- const newMap = Object.create(null);
410
- const toFontChar = [];
411
- let privateUseAreaIndex = 0;
412
- let nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
413
- let privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
414
- for (let originalCharCode in charCodeToGlyphId) {
415
- originalCharCode |= 0;
416
- let glyphId = charCodeToGlyphId[originalCharCode];
417
- // For missing glyphs don't create the mappings so the glyph isn't
418
- // drawn.
419
- if (!hasGlyph(glyphId)) {
420
- continue;
421
- }
422
- if (nextAvailableFontCharCode > privateUseOffetEnd) {
423
- privateUseAreaIndex++;
424
- if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {
425
- warn("Ran out of space in font private use area.");
426
- break;
427
- }
428
- nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
429
- privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
430
- }
431
- const fontCharCode = nextAvailableFontCharCode++;
432
- if (glyphId === 0) {
433
- glyphId = newGlyphZeroId;
434
- }
435
-
436
- newMap[fontCharCode] = glyphId;
437
- toFontChar[originalCharCode] = fontCharCode;
438
- }
439
- return {
440
- toFontChar,
441
- charCodeToGlyphId: newMap,
442
- nextAvailableFontCharCode,
443
- };
444
- }
445
-
446
- function getRanges(glyphs, numGlyphs) {
447
- // Array.sort() sorts by characters, not numerically, so convert to an
448
- // array of characters.
449
- const codes = [];
450
- for (const charCode in glyphs) {
451
- // Remove an invalid glyph ID mappings to make OTS happy.
452
- if (glyphs[charCode] >= numGlyphs) {
453
- continue;
454
- }
455
- codes.push({ fontCharCode: charCode | 0, glyphId: glyphs[charCode] });
456
- }
457
- // Some fonts have zero glyphs and are used only for text selection, but
458
- // there needs to be at least one to build a valid cmap table.
459
- if (codes.length === 0) {
460
- codes.push({ fontCharCode: 0, glyphId: 0 });
461
- }
462
- codes.sort(function fontGetRangesSort(a, b) {
463
- return a.fontCharCode - b.fontCharCode;
464
- });
465
-
466
- // Split the sorted codes into ranges.
467
- const ranges = [];
468
- const length = codes.length;
469
- for (let n = 0; n < length; ) {
470
- const start = codes[n].fontCharCode;
471
- const codeIndices = [codes[n].glyphId];
472
- ++n;
473
- let end = start;
474
- while (n < length && end + 1 === codes[n].fontCharCode) {
475
- codeIndices.push(codes[n].glyphId);
476
- ++end;
477
- ++n;
478
- if (end === 0xffff) {
479
- break;
480
- }
481
- }
482
- ranges.push([start, end, codeIndices]);
483
- }
484
-
485
- return ranges;
486
- }
487
-
488
- function createCmapTable(glyphs, numGlyphs) {
489
- const ranges = getRanges(glyphs, numGlyphs);
490
- const numTables = ranges[ranges.length - 1][1] > 0xffff ? 2 : 1;
491
- let cmap =
492
- "\x00\x00" + // version
493
- string16(numTables) + // numTables
494
- "\x00\x03" + // platformID
495
- "\x00\x01" + // encodingID
496
- string32(4 + numTables * 8); // start of the table record
497
-
498
- let i, ii, j, jj;
499
- for (i = ranges.length - 1; i >= 0; --i) {
500
- if (ranges[i][0] <= 0xffff) {
501
- break;
502
- }
503
- }
504
- const bmpLength = i + 1;
505
-
506
- if (ranges[i][0] < 0xffff && ranges[i][1] === 0xffff) {
507
- ranges[i][1] = 0xfffe;
508
- }
509
- const trailingRangesCount = ranges[i][1] < 0xffff ? 1 : 0;
510
- const segCount = bmpLength + trailingRangesCount;
511
- const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);
512
-
513
- // Fill up the 4 parallel arrays describing the segments.
514
- let startCount = "";
515
- let endCount = "";
516
- let idDeltas = "";
517
- let idRangeOffsets = "";
518
- let glyphsIds = "";
519
- let bias = 0;
520
-
521
- let range, start, end, codes;
522
- for (i = 0, ii = bmpLength; i < ii; i++) {
523
- range = ranges[i];
524
- start = range[0];
525
- end = range[1];
526
- startCount += string16(start);
527
- endCount += string16(end);
528
- codes = range[2];
529
- let contiguous = true;
530
- for (j = 1, jj = codes.length; j < jj; ++j) {
531
- if (codes[j] !== codes[j - 1] + 1) {
532
- contiguous = false;
533
- break;
534
- }
535
- }
536
- if (!contiguous) {
537
- const offset = (segCount - i) * 2 + bias * 2;
538
- bias += end - start + 1;
539
-
540
- idDeltas += string16(0);
541
- idRangeOffsets += string16(offset);
542
-
543
- for (j = 0, jj = codes.length; j < jj; ++j) {
544
- glyphsIds += string16(codes[j]);
545
- }
546
- } else {
547
- const startCode = codes[0];
548
-
549
- idDeltas += string16((startCode - start) & 0xffff);
550
- idRangeOffsets += string16(0);
551
- }
552
- }
553
-
554
- if (trailingRangesCount > 0) {
555
- endCount += "\xFF\xFF";
556
- startCount += "\xFF\xFF";
557
- idDeltas += "\x00\x01";
558
- idRangeOffsets += "\x00\x00";
559
- }
560
-
561
- const format314 =
562
- "\x00\x00" + // language
563
- string16(2 * segCount) +
564
- string16(searchParams.range) +
565
- string16(searchParams.entry) +
566
- string16(searchParams.rangeShift) +
567
- endCount +
568
- "\x00\x00" +
569
- startCount +
570
- idDeltas +
571
- idRangeOffsets +
572
- glyphsIds;
573
-
574
- let format31012 = "";
575
- let header31012 = "";
576
- if (numTables > 1) {
577
- cmap +=
578
- "\x00\x03" + // platformID
579
- "\x00\x0A" + // encodingID
580
- string32(4 + numTables * 8 + 4 + format314.length); // start of the table record
581
- format31012 = "";
582
- for (i = 0, ii = ranges.length; i < ii; i++) {
583
- range = ranges[i];
584
- start = range[0];
585
- codes = range[2];
586
- let code = codes[0];
587
- for (j = 1, jj = codes.length; j < jj; ++j) {
588
- if (codes[j] !== codes[j - 1] + 1) {
589
- end = range[0] + j - 1;
590
- format31012 +=
591
- string32(start) + // startCharCode
592
- string32(end) + // endCharCode
593
- string32(code); // startGlyphID
594
- start = end + 1;
595
- code = codes[j];
596
- }
597
- }
598
- format31012 +=
599
- string32(start) + // startCharCode
600
- string32(range[1]) + // endCharCode
601
- string32(code); // startGlyphID
602
- }
603
- header31012 =
604
- "\x00\x0C" + // format
605
- "\x00\x00" + // reserved
606
- string32(format31012.length + 16) + // length
607
- "\x00\x00\x00\x00" + // language
608
- string32(format31012.length / 12); // nGroups
609
- }
610
-
611
- return (
612
- cmap +
613
- "\x00\x04" + // format
614
- string16(format314.length + 4) + // length
615
- format314 +
616
- header31012 +
617
- format31012
618
- );
619
- }
620
-
621
- function validateOS2Table(os2, file) {
622
- file.pos = (file.start || 0) + os2.offset;
623
- const version = file.getUint16();
624
- // TODO verify all OS/2 tables fields, but currently we validate only those
625
- // that give us issues
626
- file.skip(60); // skipping type, misc sizes, panose, unicode ranges
627
- const selection = file.getUint16();
628
- if (version < 4 && selection & 0x0300) {
629
- return false;
630
- }
631
- const firstChar = file.getUint16();
632
- const lastChar = file.getUint16();
633
- if (firstChar > lastChar) {
634
- return false;
635
- }
636
- file.skip(6); // skipping sTypoAscender/Descender/LineGap
637
- const usWinAscent = file.getUint16();
638
- if (usWinAscent === 0) {
639
- // makes font unreadable by windows
640
- return false;
641
- }
642
-
643
- // OS/2 appears to be valid, resetting some fields
644
- os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0
645
- return true;
646
- }
647
-
648
- function createOS2Table(properties, charstrings, override) {
649
- override = override || {
650
- unitsPerEm: 0,
651
- yMax: 0,
652
- yMin: 0,
653
- ascent: 0,
654
- descent: 0,
655
- };
656
-
657
- let ulUnicodeRange1 = 0;
658
- let ulUnicodeRange2 = 0;
659
- let ulUnicodeRange3 = 0;
660
- let ulUnicodeRange4 = 0;
661
-
662
- let firstCharIndex = null;
663
- let lastCharIndex = 0;
664
-
665
- if (charstrings) {
666
- for (let code in charstrings) {
667
- code |= 0;
668
- if (firstCharIndex > code || !firstCharIndex) {
669
- firstCharIndex = code;
670
- }
671
- if (lastCharIndex < code) {
672
- lastCharIndex = code;
673
- }
674
-
675
- const position = getUnicodeRangeFor(code);
676
- if (position < 32) {
677
- ulUnicodeRange1 |= 1 << position;
678
- } else if (position < 64) {
679
- ulUnicodeRange2 |= 1 << (position - 32);
680
- } else if (position < 96) {
681
- ulUnicodeRange3 |= 1 << (position - 64);
682
- } else if (position < 123) {
683
- ulUnicodeRange4 |= 1 << (position - 96);
684
- } else {
685
- throw new FormatError(
686
- "Unicode ranges Bits > 123 are reserved for internal usage"
687
- );
688
- }
689
- }
690
- if (lastCharIndex > 0xffff) {
691
- // OS2 only supports a 16 bit int. The spec says if supplementary
692
- // characters are used the field should just be set to 0xFFFF.
693
- lastCharIndex = 0xffff;
694
- }
695
- } else {
696
- // TODO
697
- firstCharIndex = 0;
698
- lastCharIndex = 255;
699
- }
700
-
701
- const bbox = properties.bbox || [0, 0, 0, 0];
702
- const unitsPerEm =
703
- override.unitsPerEm ||
704
- 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
705
-
706
- // if the font units differ to the PDF glyph space units
707
- // then scale up the values
708
- const scale = properties.ascentScaled
709
- ? 1.0
710
- : unitsPerEm / PDF_GLYPH_SPACE_UNITS;
711
-
712
- const typoAscent =
713
- override.ascent || Math.round(scale * (properties.ascent || bbox[3]));
714
- let typoDescent =
715
- override.descent || Math.round(scale * (properties.descent || bbox[1]));
716
- if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {
717
- typoDescent = -typoDescent; // fixing incorrect descent
718
- }
719
- const winAscent = override.yMax || typoAscent;
720
- const winDescent = -override.yMin || -typoDescent;
721
-
722
- return (
723
- "\x00\x03" + // version
724
- "\x02\x24" + // xAvgCharWidth
725
- "\x01\xF4" + // usWeightClass
726
- "\x00\x05" + // usWidthClass
727
- "\x00\x00" + // fstype (0 to let the font loads via font-face on IE)
728
- "\x02\x8A" + // ySubscriptXSize
729
- "\x02\xBB" + // ySubscriptYSize
730
- "\x00\x00" + // ySubscriptXOffset
731
- "\x00\x8C" + // ySubscriptYOffset
732
- "\x02\x8A" + // ySuperScriptXSize
733
- "\x02\xBB" + // ySuperScriptYSize
734
- "\x00\x00" + // ySuperScriptXOffset
735
- "\x01\xDF" + // ySuperScriptYOffset
736
- "\x00\x31" + // yStrikeOutSize
737
- "\x01\x02" + // yStrikeOutPosition
738
- "\x00\x00" + // sFamilyClass
739
- "\x00\x00\x06" +
740
- String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +
741
- "\x00\x00\x00\x00\x00\x00" + // Panose
742
- string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
743
- string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
744
- string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
745
- string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
746
- "\x2A\x32\x31\x2A" + // achVendID
747
- string16(properties.italicAngle ? 1 : 0) + // fsSelection
748
- string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex
749
- string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
750
- string16(typoAscent) + // sTypoAscender
751
- string16(typoDescent) + // sTypoDescender
752
- "\x00\x64" + // sTypoLineGap (7%-10% of the unitsPerEM value)
753
- string16(winAscent) + // usWinAscent
754
- string16(winDescent) + // usWinDescent
755
- "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
756
- "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
757
- string16(properties.xHeight) + // sxHeight
758
- string16(properties.capHeight) + // sCapHeight
759
- string16(0) + // usDefaultChar
760
- string16(firstCharIndex || properties.firstChar) + // usBreakChar
761
- "\x00\x03"
762
- ); // usMaxContext
763
- }
764
-
765
- function createPostTable(properties) {
766
- const angle = Math.floor(properties.italicAngle * 2 ** 16);
767
- return (
768
- "\x00\x03\x00\x00" + // Version number
769
- string32(angle) + // italicAngle
770
- "\x00\x00" + // underlinePosition
771
- "\x00\x00" + // underlineThickness
772
- string32(properties.fixedPitch ? 1 : 0) + // isFixedPitch
773
- "\x00\x00\x00\x00" + // minMemType42
774
- "\x00\x00\x00\x00" + // maxMemType42
775
- "\x00\x00\x00\x00" + // minMemType1
776
- "\x00\x00\x00\x00"
777
- ); // maxMemType1
778
- }
779
-
780
- function createPostscriptName(name) {
781
- // See https://docs.microsoft.com/en-us/typography/opentype/spec/recom#name.
782
- return name.replace(/[^\x21-\x7E]|[[\](){}<>/%]/g, "").slice(0, 63);
783
- }
784
-
785
- function createNameTable(name, proto) {
786
- if (!proto) {
787
- proto = [[], []]; // no strings and unicode strings
788
- }
789
-
790
- const strings = [
791
- proto[0][0] || "Original licence", // 0.Copyright
792
- proto[0][1] || name, // 1.Font family
793
- proto[0][2] || "Unknown", // 2.Font subfamily (font weight)
794
- proto[0][3] || "uniqueID", // 3.Unique ID
795
- proto[0][4] || name, // 4.Full font name
796
- proto[0][5] || "Version 0.11", // 5.Version
797
- proto[0][6] || createPostscriptName(name), // 6.Postscript name
798
- proto[0][7] || "Unknown", // 7.Trademark
799
- proto[0][8] || "Unknown", // 8.Manufacturer
800
- proto[0][9] || "Unknown", // 9.Designer
801
- ];
802
-
803
- // Mac want 1-byte per character strings while Windows want
804
- // 2-bytes per character, so duplicate the names table
805
- const stringsUnicode = [];
806
- let i, ii, j, jj, str;
807
- for (i = 0, ii = strings.length; i < ii; i++) {
808
- str = proto[1][i] || strings[i];
809
-
810
- const strBufUnicode = [];
811
- for (j = 0, jj = str.length; j < jj; j++) {
812
- strBufUnicode.push(string16(str.charCodeAt(j)));
813
- }
814
- stringsUnicode.push(strBufUnicode.join(""));
815
- }
816
-
817
- const names = [strings, stringsUnicode];
818
- const platforms = ["\x00\x01", "\x00\x03"];
819
- const encodings = ["\x00\x00", "\x00\x01"];
820
- const languages = ["\x00\x00", "\x04\x09"];
821
-
822
- const namesRecordCount = strings.length * platforms.length;
823
- let nameTable =
824
- "\x00\x00" + // format
825
- string16(namesRecordCount) + // Number of names Record
826
- string16(namesRecordCount * 12 + 6); // Storage
827
-
828
- // Build the name records field
829
- let strOffset = 0;
830
- for (i = 0, ii = platforms.length; i < ii; i++) {
831
- const strs = names[i];
832
- for (j = 0, jj = strs.length; j < jj; j++) {
833
- str = strs[j];
834
- const nameRecord =
835
- platforms[i] + // platform ID
836
- encodings[i] + // encoding ID
837
- languages[i] + // language ID
838
- string16(j) + // name ID
839
- string16(str.length) +
840
- string16(strOffset);
841
- nameTable += nameRecord;
842
- strOffset += str.length;
843
- }
844
- }
845
-
846
- nameTable += strings.join("") + stringsUnicode.join("");
847
- return nameTable;
848
- }
849
-
850
- /**
851
- * 'Font' is the class the outside world should use, it encapsulate all the font
852
- * decoding logics whatever type it is (assuming the font type is supported).
853
- */
854
- class Font {
855
- constructor(name, file, properties) {
856
- this.name = name;
857
- this.psName = null;
858
- this.mimetype = null;
859
- this.disableFontFace = false;
860
-
861
- this.loadedName = properties.loadedName;
862
- this.isType3Font = properties.isType3Font;
863
- this.missingFile = false;
864
- this.cssFontInfo = properties.cssFontInfo;
865
-
866
- this._charsCache = Object.create(null);
867
- this._glyphCache = Object.create(null);
868
-
869
- this.isSerifFont = !!(properties.flags & FontFlags.Serif);
870
- this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
871
- this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
872
-
873
- let type = properties.type;
874
- let subtype = properties.subtype;
875
- this.type = type;
876
- this.subtype = subtype;
877
-
878
- let fallbackName = "sans-serif";
879
- if (this.isMonospace) {
880
- fallbackName = "monospace";
881
- } else if (this.isSerifFont) {
882
- fallbackName = "serif";
883
- }
884
- this.fallbackName = fallbackName;
885
-
886
- this.differences = properties.differences;
887
- this.widths = properties.widths;
888
- this.defaultWidth = properties.defaultWidth;
889
- this.composite = properties.composite;
890
- this.cMap = properties.cMap;
891
- this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
892
- this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
893
- this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
894
- this.lineHeight = this.ascent - this.descent;
895
- this.fontMatrix = properties.fontMatrix;
896
- this.bbox = properties.bbox;
897
- this.defaultEncoding = properties.defaultEncoding;
898
-
899
- this.toUnicode = properties.toUnicode;
900
- this.toFontChar = [];
901
-
902
- if (properties.type === "Type3") {
903
- for (let charCode = 0; charCode < 256; charCode++) {
904
- this.toFontChar[charCode] =
905
- this.differences[charCode] || properties.defaultEncoding[charCode];
906
- }
907
- this.fontType = FontType.TYPE3;
908
- return;
909
- }
910
-
911
- this.cidEncoding = properties.cidEncoding;
912
- this.vertical = !!properties.vertical;
913
- if (this.vertical) {
914
- this.vmetrics = properties.vmetrics;
915
- this.defaultVMetrics = properties.defaultVMetrics;
916
- }
917
-
918
- if (!file || file.isEmpty) {
919
- if (file) {
920
- // Some bad PDF generators will include empty font files,
921
- // attempting to recover by assuming that no file exists.
922
- warn('Font file is empty in "' + name + '" (' + this.loadedName + ")");
923
- }
924
- this.fallbackToSystemFont(properties);
925
- return;
926
- }
927
-
928
- // Parse the font file to determine the correct type/subtype, rather than
929
- // relying on the (often incorrect) data in the font dictionary; (see e.g.
930
- // issue6782.pdf, issue7598.pdf, and issue9949.pdf).
931
- [type, subtype] = getFontFileType(file, properties);
932
-
933
- if (type !== this.type || subtype !== this.subtype) {
934
- info(
935
- "Inconsistent font file Type/SubType, expected: " +
936
- `${this.type}/${this.subtype} but found: ${type}/${subtype}.`
937
- );
938
- }
939
-
940
- let data;
941
- try {
942
- switch (type) {
943
- case "MMType1":
944
- info("MMType1 font (" + name + "), falling back to Type1.");
945
- /* falls through */
946
- case "Type1":
947
- case "CIDFontType0":
948
- this.mimetype = "font/opentype";
949
-
950
- const cff =
951
- subtype === "Type1C" || subtype === "CIDFontType0C"
952
- ? new CFFFont(file, properties)
953
- : new Type1Font(name, file, properties);
954
-
955
- adjustWidths(properties);
956
-
957
- // Wrap the CFF data inside an OTF font file
958
- data = this.convert(name, cff, properties);
959
- break;
960
-
961
- case "OpenType":
962
- case "TrueType":
963
- case "CIDFontType2":
964
- this.mimetype = "font/opentype";
965
-
966
- // Repair the TrueType file. It is can be damaged in the point of
967
- // view of the sanitizer
968
- data = this.checkAndRepair(name, file, properties);
969
- if (this.isOpenType) {
970
- adjustWidths(properties);
971
-
972
- type = "OpenType";
973
- }
974
- break;
975
-
976
- default:
977
- throw new FormatError(`Font ${type} is not supported`);
978
- }
979
- } catch (e) {
980
- warn(e);
981
- this.fallbackToSystemFont(properties);
982
- return;
983
- }
984
-
985
- amendFallbackToUnicode(properties);
986
- this.data = data;
987
- this.fontType = getFontType(type, subtype, properties.isStandardFont);
988
-
989
- // Transfer some properties again that could change during font conversion
990
- this.fontMatrix = properties.fontMatrix;
991
- this.widths = properties.widths;
992
- this.defaultWidth = properties.defaultWidth;
993
- this.toUnicode = properties.toUnicode;
994
- this.seacMap = properties.seacMap;
995
- }
996
-
997
- get renderer() {
998
- const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);
999
- return shadow(this, "renderer", renderer);
1000
- }
1001
-
1002
- exportData(extraProperties = false) {
1003
- const exportDataProperties = extraProperties
1004
- ? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES]
1005
- : EXPORT_DATA_PROPERTIES;
1006
-
1007
- const data = Object.create(null);
1008
- let property, value;
1009
- for (property of exportDataProperties) {
1010
- value = this[property];
1011
- // Ignore properties that haven't been explicitly set.
1012
- if (value !== undefined) {
1013
- data[property] = value;
1014
- }
1015
- }
1016
- return data;
1017
- }
1018
-
1019
- fallbackToSystemFont(properties) {
1020
- this.missingFile = true;
1021
- // The file data is not specified. Trying to fix the font name
1022
- // to be used with the canvas.font.
1023
- const name = this.name;
1024
- const type = this.type;
1025
- const subtype = this.subtype;
1026
- let fontName = normalizeFontName(name);
1027
- const stdFontMap = getStdFontMap(),
1028
- nonStdFontMap = getNonStdFontMap();
1029
- const isStandardFont = !!stdFontMap[fontName];
1030
- const isMappedToStandardFont = !!(
1031
- nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]
1032
- );
1033
-
1034
- fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
1035
- this.bold = fontName.search(/bold/gi) !== -1;
1036
- this.italic =
1037
- fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;
1038
-
1039
- // Use 'name' instead of 'fontName' here because the original
1040
- // name ArialBlack for example will be replaced by Helvetica.
1041
- this.black = name.search(/Black/g) !== -1;
1042
-
1043
- // Use 'name' instead of 'fontName' here because the original
1044
- // name ArialNarrow for example will be replaced by Helvetica.
1045
- const isNarrow = name.search(/Narrow/g) !== -1;
1046
-
1047
- // if at least one width is present, remeasure all chars when exists
1048
- this.remeasure =
1049
- (!isStandardFont || isNarrow) && Object.keys(this.widths).length > 0;
1050
- if (
1051
- (isStandardFont || isMappedToStandardFont) &&
1052
- type === "CIDFontType2" &&
1053
- this.cidEncoding.startsWith("Identity-")
1054
- ) {
1055
- const GlyphMapForStandardFonts = getGlyphMapForStandardFonts(),
1056
- cidToGidMap = properties.cidToGidMap;
1057
- // Standard fonts might be embedded as CID font without glyph mapping.
1058
- // Building one based on GlyphMapForStandardFonts.
1059
- const map = [];
1060
- for (const charCode in GlyphMapForStandardFonts) {
1061
- map[+charCode] = GlyphMapForStandardFonts[charCode];
1062
- }
1063
- if (/Arial-?Black/i.test(name)) {
1064
- const SupplementalGlyphMapForArialBlack =
1065
- getSupplementalGlyphMapForArialBlack();
1066
- for (const charCode in SupplementalGlyphMapForArialBlack) {
1067
- map[+charCode] = SupplementalGlyphMapForArialBlack[charCode];
1068
- }
1069
- } else if (/Calibri/i.test(name)) {
1070
- const SupplementalGlyphMapForCalibri =
1071
- getSupplementalGlyphMapForCalibri();
1072
- for (const charCode in SupplementalGlyphMapForCalibri) {
1073
- map[+charCode] = SupplementalGlyphMapForCalibri[charCode];
1074
- }
1075
- }
1076
- // Always update the glyph mapping with the `cidToGidMap` when it exists
1077
- // (fixes issue12418_reduced.pdf).
1078
- if (cidToGidMap) {
1079
- for (const charCode in map) {
1080
- const cid = map[charCode];
1081
- if (cidToGidMap[cid] !== undefined) {
1082
- map[+charCode] = cidToGidMap[cid];
1083
- }
1084
- }
1085
- }
1086
-
1087
- const isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
1088
- if (!isIdentityUnicode) {
1089
- this.toUnicode.forEach(function (charCode, unicodeCharCode) {
1090
- map[+charCode] = unicodeCharCode;
1091
- });
1092
- }
1093
- this.toFontChar = map;
1094
- this.toUnicode = new ToUnicodeMap(map);
1095
- } else if (/Symbol/i.test(fontName)) {
1096
- this.toFontChar = buildToFontChar(
1097
- SymbolSetEncoding,
1098
- getGlyphsUnicode(),
1099
- this.differences
1100
- );
1101
- } else if (/Dingbats/i.test(fontName)) {
1102
- if (/Wingdings/i.test(name)) {
1103
- warn("Non-embedded Wingdings font, falling back to ZapfDingbats.");
1104
- }
1105
- this.toFontChar = buildToFontChar(
1106
- ZapfDingbatsEncoding,
1107
- getDingbatsGlyphsUnicode(),
1108
- this.differences
1109
- );
1110
- } else if (isStandardFont) {
1111
- this.toFontChar = buildToFontChar(
1112
- this.defaultEncoding,
1113
- getGlyphsUnicode(),
1114
- this.differences
1115
- );
1116
- } else {
1117
- const glyphsUnicodeMap = getGlyphsUnicode();
1118
- const map = [];
1119
- this.toUnicode.forEach((charCode, unicodeCharCode) => {
1120
- if (!this.composite) {
1121
- const glyphName =
1122
- this.differences[charCode] || this.defaultEncoding[charCode];
1123
- const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
1124
- if (unicode !== -1) {
1125
- unicodeCharCode = unicode;
1126
- }
1127
- }
1128
- map[+charCode] = unicodeCharCode;
1129
- });
1130
-
1131
- // Attempt to improve the glyph mapping for (some) composite fonts that
1132
- // appear to lack meaningful ToUnicode data.
1133
- if (this.composite && this.toUnicode instanceof IdentityToUnicodeMap) {
1134
- if (/Verdana/i.test(name)) {
1135
- // Fixes issue11242_reduced.pdf
1136
- const GlyphMapForStandardFonts = getGlyphMapForStandardFonts();
1137
- for (const charCode in GlyphMapForStandardFonts) {
1138
- map[+charCode] = GlyphMapForStandardFonts[charCode];
1139
- }
1140
- }
1141
- }
1142
- this.toFontChar = map;
1143
- }
1144
-
1145
- amendFallbackToUnicode(properties);
1146
- this.loadedName = fontName.split("-")[0];
1147
- this.fontType = getFontType(type, subtype, properties.isStandardFont);
1148
- }
1149
-
1150
- checkAndRepair(name, font, properties) {
1151
- const VALID_TABLES = [
1152
- "OS/2",
1153
- "cmap",
1154
- "head",
1155
- "hhea",
1156
- "hmtx",
1157
- "maxp",
1158
- "name",
1159
- "post",
1160
- "loca",
1161
- "glyf",
1162
- "fpgm",
1163
- "prep",
1164
- "cvt ",
1165
- "CFF ",
1166
- ];
1167
-
1168
- function readTables(file, numTables) {
1169
- const tables = Object.create(null);
1170
- tables["OS/2"] = null;
1171
- tables.cmap = null;
1172
- tables.head = null;
1173
- tables.hhea = null;
1174
- tables.hmtx = null;
1175
- tables.maxp = null;
1176
- tables.name = null;
1177
- tables.post = null;
1178
-
1179
- for (let i = 0; i < numTables; i++) {
1180
- const table = readTableEntry(file);
1181
- if (!VALID_TABLES.includes(table.tag)) {
1182
- continue; // skipping table if it's not a required or optional table
1183
- }
1184
- if (table.length === 0) {
1185
- continue; // skipping empty tables
1186
- }
1187
- tables[table.tag] = table;
1188
- }
1189
- return tables;
1190
- }
1191
-
1192
- function readTableEntry(file) {
1193
- const tag = file.getString(4);
1194
-
1195
- const checksum = file.getInt32() >>> 0;
1196
- const offset = file.getInt32() >>> 0;
1197
- const length = file.getInt32() >>> 0;
1198
-
1199
- // Read the table associated data
1200
- const previousPosition = file.pos;
1201
- file.pos = file.start ? file.start : 0;
1202
- file.skip(offset);
1203
- const data = file.getBytes(length);
1204
- file.pos = previousPosition;
1205
-
1206
- if (tag === "head") {
1207
- // clearing checksum adjustment
1208
- data[8] = data[9] = data[10] = data[11] = 0;
1209
- data[17] |= 0x20; // Set font optimized for cleartype flag.
1210
- }
1211
-
1212
- return {
1213
- tag,
1214
- checksum,
1215
- length,
1216
- offset,
1217
- data,
1218
- };
1219
- }
1220
-
1221
- function readOpenTypeHeader(ttf) {
1222
- return {
1223
- version: ttf.getString(4),
1224
- numTables: ttf.getUint16(),
1225
- searchRange: ttf.getUint16(),
1226
- entrySelector: ttf.getUint16(),
1227
- rangeShift: ttf.getUint16(),
1228
- };
1229
- }
1230
-
1231
- function readTrueTypeCollectionHeader(ttc) {
1232
- const ttcTag = ttc.getString(4);
1233
- assert(ttcTag === "ttcf", "Must be a TrueType Collection font.");
1234
-
1235
- const majorVersion = ttc.getUint16();
1236
- const minorVersion = ttc.getUint16();
1237
- const numFonts = ttc.getInt32() >>> 0;
1238
- const offsetTable = [];
1239
- for (let i = 0; i < numFonts; i++) {
1240
- offsetTable.push(ttc.getInt32() >>> 0);
1241
- }
1242
-
1243
- const header = {
1244
- ttcTag,
1245
- majorVersion,
1246
- minorVersion,
1247
- numFonts,
1248
- offsetTable,
1249
- };
1250
- switch (majorVersion) {
1251
- case 1:
1252
- return header;
1253
- case 2:
1254
- header.dsigTag = ttc.getInt32() >>> 0;
1255
- header.dsigLength = ttc.getInt32() >>> 0;
1256
- header.dsigOffset = ttc.getInt32() >>> 0;
1257
- return header;
1258
- }
1259
- throw new FormatError(
1260
- `Invalid TrueType Collection majorVersion: ${majorVersion}.`
1261
- );
1262
- }
1263
-
1264
- function readTrueTypeCollectionData(ttc, fontName) {
1265
- const { numFonts, offsetTable } = readTrueTypeCollectionHeader(ttc);
1266
- const fontNameParts = fontName.split("+");
1267
- let fallbackData;
1268
-
1269
- for (let i = 0; i < numFonts; i++) {
1270
- ttc.pos = (ttc.start || 0) + offsetTable[i];
1271
- const potentialHeader = readOpenTypeHeader(ttc);
1272
- const potentialTables = readTables(ttc, potentialHeader.numTables);
1273
-
1274
- if (!potentialTables.name) {
1275
- throw new FormatError(
1276
- 'TrueType Collection font must contain a "name" table.'
1277
- );
1278
- }
1279
- const nameTable = readNameTable(potentialTables.name);
1280
-
1281
- for (let j = 0, jj = nameTable.length; j < jj; j++) {
1282
- for (let k = 0, kk = nameTable[j].length; k < kk; k++) {
1283
- const nameEntry =
1284
- nameTable[j][k] && nameTable[j][k].replace(/\s/g, "");
1285
- if (!nameEntry) {
1286
- continue;
1287
- }
1288
- if (nameEntry === fontName) {
1289
- return {
1290
- header: potentialHeader,
1291
- tables: potentialTables,
1292
- };
1293
- }
1294
- if (fontNameParts.length < 2) {
1295
- continue;
1296
- }
1297
- for (const part of fontNameParts) {
1298
- if (nameEntry === part) {
1299
- fallbackData = {
1300
- name: part,
1301
- header: potentialHeader,
1302
- tables: potentialTables,
1303
- };
1304
- }
1305
- }
1306
- }
1307
- }
1308
- }
1309
- if (fallbackData) {
1310
- warn(
1311
- `TrueType Collection does not contain "${fontName}" font, ` +
1312
- `falling back to "${fallbackData.name}" font instead.`
1313
- );
1314
- return {
1315
- header: fallbackData.header,
1316
- tables: fallbackData.tables,
1317
- };
1318
- }
1319
- throw new FormatError(
1320
- `TrueType Collection does not contain "${fontName}" font.`
1321
- );
1322
- }
1323
-
1324
- /**
1325
- * Read the appropriate subtable from the cmap according to 9.6.6.4 from
1326
- * PDF spec
1327
- */
1328
- function readCmapTable(cmap, file, isSymbolicFont, hasEncoding) {
1329
- if (!cmap) {
1330
- warn("No cmap table available.");
1331
- return {
1332
- platformId: -1,
1333
- encodingId: -1,
1334
- mappings: [],
1335
- hasShortCmap: false,
1336
- };
1337
- }
1338
- let segment;
1339
- let start = (file.start ? file.start : 0) + cmap.offset;
1340
- file.pos = start;
1341
-
1342
- file.skip(2); // version
1343
- const numTables = file.getUint16();
1344
-
1345
- let potentialTable;
1346
- let canBreak = false;
1347
- // There's an order of preference in terms of which cmap subtable to
1348
- // use:
1349
- // - non-symbolic fonts the preference is a 3,1 table then a 1,0 table
1350
- // - symbolic fonts the preference is a 3,0 table then a 1,0 table
1351
- // The following takes advantage of the fact that the tables are sorted
1352
- // to work.
1353
- for (let i = 0; i < numTables; i++) {
1354
- const platformId = file.getUint16();
1355
- const encodingId = file.getUint16();
1356
- const offset = file.getInt32() >>> 0;
1357
- let useTable = false;
1358
-
1359
- // Sometimes there are multiple of the same type of table. Default
1360
- // to choosing the first table and skip the rest.
1361
- if (
1362
- potentialTable &&
1363
- potentialTable.platformId === platformId &&
1364
- potentialTable.encodingId === encodingId
1365
- ) {
1366
- continue;
1367
- }
1368
-
1369
- if (
1370
- platformId === 0 &&
1371
- (encodingId === /* Unicode Default */ 0 ||
1372
- encodingId === /* Unicode 1.1 */ 1 ||
1373
- encodingId === /* Unicode BMP */ 3)
1374
- ) {
1375
- useTable = true;
1376
- // Continue the loop since there still may be a higher priority
1377
- // table.
1378
- } else if (platformId === 1 && encodingId === 0) {
1379
- useTable = true;
1380
- // Continue the loop since there still may be a higher priority
1381
- // table.
1382
- } else if (
1383
- platformId === 3 &&
1384
- encodingId === 1 &&
1385
- (hasEncoding || !potentialTable)
1386
- ) {
1387
- useTable = true;
1388
- if (!isSymbolicFont) {
1389
- canBreak = true;
1390
- }
1391
- } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
1392
- useTable = true;
1393
-
1394
- let correctlySorted = true;
1395
- if (i < numTables - 1) {
1396
- const nextBytes = file.peekBytes(2),
1397
- nextPlatformId = int16(nextBytes[0], nextBytes[1]);
1398
- if (nextPlatformId < platformId) {
1399
- correctlySorted = false;
1400
- }
1401
- }
1402
- if (correctlySorted) {
1403
- canBreak = true;
1404
- }
1405
- }
1406
-
1407
- if (useTable) {
1408
- potentialTable = {
1409
- platformId,
1410
- encodingId,
1411
- offset,
1412
- };
1413
- }
1414
- if (canBreak) {
1415
- break;
1416
- }
1417
- }
1418
-
1419
- if (potentialTable) {
1420
- file.pos = start + potentialTable.offset;
1421
- }
1422
- if (!potentialTable || file.peekByte() === -1) {
1423
- warn("Could not find a preferred cmap table.");
1424
- return {
1425
- platformId: -1,
1426
- encodingId: -1,
1427
- mappings: [],
1428
- hasShortCmap: false,
1429
- };
1430
- }
1431
-
1432
- const format = file.getUint16();
1433
- file.skip(2 + 2); // length + language
1434
-
1435
- let hasShortCmap = false;
1436
- const mappings = [];
1437
- let j, glyphId;
1438
-
1439
- // TODO(mack): refactor this cmap subtable reading logic out
1440
- if (format === 0) {
1441
- for (j = 0; j < 256; j++) {
1442
- const index = file.getByte();
1443
- if (!index) {
1444
- continue;
1445
- }
1446
- mappings.push({
1447
- charCode: j,
1448
- glyphId: index,
1449
- });
1450
- }
1451
- hasShortCmap = true;
1452
- } else if (format === 4) {
1453
- // re-creating the table in format 4 since the encoding
1454
- // might be changed
1455
- const segCount = file.getUint16() >> 1;
1456
- file.skip(6); // skipping range fields
1457
- const segments = [];
1458
- let segIndex;
1459
- for (segIndex = 0; segIndex < segCount; segIndex++) {
1460
- segments.push({ end: file.getUint16() });
1461
- }
1462
- file.skip(2);
1463
- for (segIndex = 0; segIndex < segCount; segIndex++) {
1464
- segments[segIndex].start = file.getUint16();
1465
- }
1466
-
1467
- for (segIndex = 0; segIndex < segCount; segIndex++) {
1468
- segments[segIndex].delta = file.getUint16();
1469
- }
1470
-
1471
- let offsetsCount = 0,
1472
- offsetIndex;
1473
- for (segIndex = 0; segIndex < segCount; segIndex++) {
1474
- segment = segments[segIndex];
1475
- const rangeOffset = file.getUint16();
1476
- if (!rangeOffset) {
1477
- segment.offsetIndex = -1;
1478
- continue;
1479
- }
1480
-
1481
- offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
1482
- segment.offsetIndex = offsetIndex;
1483
- offsetsCount = Math.max(
1484
- offsetsCount,
1485
- offsetIndex + segment.end - segment.start + 1
1486
- );
1487
- }
1488
-
1489
- const offsets = [];
1490
- for (j = 0; j < offsetsCount; j++) {
1491
- offsets.push(file.getUint16());
1492
- }
1493
-
1494
- for (segIndex = 0; segIndex < segCount; segIndex++) {
1495
- segment = segments[segIndex];
1496
- start = segment.start;
1497
- const end = segment.end;
1498
- const delta = segment.delta;
1499
- offsetIndex = segment.offsetIndex;
1500
-
1501
- for (j = start; j <= end; j++) {
1502
- if (j === 0xffff) {
1503
- continue;
1504
- }
1505
-
1506
- glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
1507
- glyphId = (glyphId + delta) & 0xffff;
1508
- mappings.push({
1509
- charCode: j,
1510
- glyphId,
1511
- });
1512
- }
1513
- }
1514
- } else if (format === 6) {
1515
- // Format 6 is a 2-bytes dense mapping, which means the font data
1516
- // lives glue together even if they are pretty far in the unicode
1517
- // table. (This looks weird, so I can have missed something), this
1518
- // works on Linux but seems to fails on Mac so let's rewrite the
1519
- // cmap table to a 3-1-4 style
1520
- const firstCode = file.getUint16();
1521
- const entryCount = file.getUint16();
1522
-
1523
- for (j = 0; j < entryCount; j++) {
1524
- glyphId = file.getUint16();
1525
- const charCode = firstCode + j;
1526
-
1527
- mappings.push({
1528
- charCode,
1529
- glyphId,
1530
- });
1531
- }
1532
- } else {
1533
- warn("cmap table has unsupported format: " + format);
1534
- return {
1535
- platformId: -1,
1536
- encodingId: -1,
1537
- mappings: [],
1538
- hasShortCmap: false,
1539
- };
1540
- }
1541
-
1542
- // removing duplicate entries
1543
- mappings.sort(function (a, b) {
1544
- return a.charCode - b.charCode;
1545
- });
1546
- for (let i = 1; i < mappings.length; i++) {
1547
- if (mappings[i - 1].charCode === mappings[i].charCode) {
1548
- mappings.splice(i, 1);
1549
- i--;
1550
- }
1551
- }
1552
-
1553
- return {
1554
- platformId: potentialTable.platformId,
1555
- encodingId: potentialTable.encodingId,
1556
- mappings,
1557
- hasShortCmap,
1558
- };
1559
- }
1560
-
1561
- function sanitizeMetrics(
1562
- file,
1563
- header,
1564
- metrics,
1565
- headTable,
1566
- numGlyphs,
1567
- dupFirstEntry
1568
- ) {
1569
- if (!header) {
1570
- if (metrics) {
1571
- metrics.data = null;
1572
- }
1573
- return;
1574
- }
1575
-
1576
- file.pos = (file.start ? file.start : 0) + header.offset;
1577
- file.pos += 4; // version
1578
- file.pos += 2; // ascent
1579
- file.pos += 2; // descent
1580
- file.pos += 2; // linegap
1581
- file.pos += 2; // adv_width_max
1582
- file.pos += 2; // min_sb1
1583
- file.pos += 2; // min_sb2
1584
- file.pos += 2; // max_extent
1585
- file.pos += 2; // caret_slope_rise
1586
- file.pos += 2; // caret_slope_run
1587
- const caretOffset = file.getUint16();
1588
- file.pos += 8; // reserved
1589
- file.pos += 2; // format
1590
- let numOfMetrics = file.getUint16();
1591
-
1592
- if (caretOffset !== 0) {
1593
- const macStyle = int16(headTable.data[44], headTable.data[45]);
1594
- if (!(macStyle & 2)) {
1595
- // Suppress OTS warnings about the `caretOffset` in the hhea-table.
1596
- header.data[22] = 0;
1597
- header.data[23] = 0;
1598
- }
1599
- }
1600
-
1601
- if (numOfMetrics > numGlyphs) {
1602
- info(
1603
- `The numOfMetrics (${numOfMetrics}) should not be ` +
1604
- `greater than the numGlyphs (${numGlyphs}).`
1605
- );
1606
- // Reduce numOfMetrics if it is greater than numGlyphs
1607
- numOfMetrics = numGlyphs;
1608
- header.data[34] = (numOfMetrics & 0xff00) >> 8;
1609
- header.data[35] = numOfMetrics & 0x00ff;
1610
- }
1611
-
1612
- const numOfSidebearings = numGlyphs - numOfMetrics;
1613
- const numMissing =
1614
- numOfSidebearings - ((metrics.length - numOfMetrics * 4) >> 1);
1615
-
1616
- if (numMissing > 0) {
1617
- // For each missing glyph, we set both the width and lsb to 0 (zero).
1618
- // Since we need to add two properties for each glyph, this explains
1619
- // the use of |numMissing * 2| when initializing the typed array.
1620
- const entries = new Uint8Array(metrics.length + numMissing * 2);
1621
- entries.set(metrics.data);
1622
- if (dupFirstEntry) {
1623
- // Set the sidebearing value of the duplicated glyph.
1624
- entries[metrics.length] = metrics.data[2];
1625
- entries[metrics.length + 1] = metrics.data[3];
1626
- }
1627
- metrics.data = entries;
1628
- }
1629
- }
1630
-
1631
- function sanitizeGlyph(
1632
- source,
1633
- sourceStart,
1634
- sourceEnd,
1635
- dest,
1636
- destStart,
1637
- hintsValid
1638
- ) {
1639
- const glyphProfile = {
1640
- length: 0,
1641
- sizeOfInstructions: 0,
1642
- };
1643
- if (sourceEnd - sourceStart <= 12) {
1644
- // glyph with data less than 12 is invalid one
1645
- return glyphProfile;
1646
- }
1647
- const glyf = source.subarray(sourceStart, sourceEnd);
1648
- let contoursCount = signedInt16(glyf[0], glyf[1]);
1649
- if (contoursCount < 0) {
1650
- // OTS doesn't like contour count to be less than -1.
1651
- contoursCount = -1;
1652
- writeSignedInt16(glyf, 0, contoursCount);
1653
- // complex glyph, writing as is
1654
- dest.set(glyf, destStart);
1655
- glyphProfile.length = glyf.length;
1656
- return glyphProfile;
1657
- }
1658
-
1659
- let i,
1660
- j = 10,
1661
- flagsCount = 0;
1662
- for (i = 0; i < contoursCount; i++) {
1663
- const endPoint = (glyf[j] << 8) | glyf[j + 1];
1664
- flagsCount = endPoint + 1;
1665
- j += 2;
1666
- }
1667
- // skipping instructions
1668
- const instructionsStart = j;
1669
- const instructionsLength = (glyf[j] << 8) | glyf[j + 1];
1670
- glyphProfile.sizeOfInstructions = instructionsLength;
1671
- j += 2 + instructionsLength;
1672
- const instructionsEnd = j;
1673
- // validating flags
1674
- let coordinatesLength = 0;
1675
- for (i = 0; i < flagsCount; i++) {
1676
- const flag = glyf[j++];
1677
- if (flag & 0xc0) {
1678
- // reserved flags must be zero, cleaning up
1679
- glyf[j - 1] = flag & 0x3f;
1680
- }
1681
- let xLength = 2;
1682
- if (flag & 2) {
1683
- xLength = 1;
1684
- } else if (flag & 16) {
1685
- xLength = 0;
1686
- }
1687
- let yLength = 2;
1688
- if (flag & 4) {
1689
- yLength = 1;
1690
- } else if (flag & 32) {
1691
- yLength = 0;
1692
- }
1693
- const xyLength = xLength + yLength;
1694
- coordinatesLength += xyLength;
1695
- if (flag & 8) {
1696
- const repeat = glyf[j++];
1697
- i += repeat;
1698
- coordinatesLength += repeat * xyLength;
1699
- }
1700
- }
1701
- // glyph without coordinates will be rejected
1702
- if (coordinatesLength === 0) {
1703
- return glyphProfile;
1704
- }
1705
- let glyphDataLength = j + coordinatesLength;
1706
- if (glyphDataLength > glyf.length) {
1707
- // not enough data for coordinates
1708
- return glyphProfile;
1709
- }
1710
- if (!hintsValid && instructionsLength > 0) {
1711
- dest.set(glyf.subarray(0, instructionsStart), destStart);
1712
- dest.set([0, 0], destStart + instructionsStart);
1713
- dest.set(
1714
- glyf.subarray(instructionsEnd, glyphDataLength),
1715
- destStart + instructionsStart + 2
1716
- );
1717
- glyphDataLength -= instructionsLength;
1718
- if (glyf.length - glyphDataLength > 3) {
1719
- glyphDataLength = (glyphDataLength + 3) & ~3;
1720
- }
1721
- glyphProfile.length = glyphDataLength;
1722
- return glyphProfile;
1723
- }
1724
- if (glyf.length - glyphDataLength > 3) {
1725
- // truncating and aligning to 4 bytes the long glyph data
1726
- glyphDataLength = (glyphDataLength + 3) & ~3;
1727
- dest.set(glyf.subarray(0, glyphDataLength), destStart);
1728
- glyphProfile.length = glyphDataLength;
1729
- return glyphProfile;
1730
- }
1731
- // glyph data is fine
1732
- dest.set(glyf, destStart);
1733
- glyphProfile.length = glyf.length;
1734
- return glyphProfile;
1735
- }
1736
-
1737
- function sanitizeHead(head, numGlyphs, locaLength) {
1738
- const data = head.data;
1739
-
1740
- // Validate version:
1741
- // Should always be 0x00010000
1742
- const version = int32(data[0], data[1], data[2], data[3]);
1743
- if (version >> 16 !== 1) {
1744
- info("Attempting to fix invalid version in head table: " + version);
1745
- data[0] = 0;
1746
- data[1] = 1;
1747
- data[2] = 0;
1748
- data[3] = 0;
1749
- }
1750
-
1751
- const indexToLocFormat = int16(data[50], data[51]);
1752
- if (indexToLocFormat < 0 || indexToLocFormat > 1) {
1753
- info(
1754
- "Attempting to fix invalid indexToLocFormat in head table: " +
1755
- indexToLocFormat
1756
- );
1757
-
1758
- // The value of indexToLocFormat should be 0 if the loca table
1759
- // consists of short offsets, and should be 1 if the loca table
1760
- // consists of long offsets.
1761
- //
1762
- // The number of entries in the loca table should be numGlyphs + 1.
1763
- //
1764
- // Using this information, we can work backwards to deduce if the
1765
- // size of each offset in the loca table, and thus figure out the
1766
- // appropriate value for indexToLocFormat.
1767
-
1768
- const numGlyphsPlusOne = numGlyphs + 1;
1769
- if (locaLength === numGlyphsPlusOne << 1) {
1770
- // 0x0000 indicates the loca table consists of short offsets
1771
- data[50] = 0;
1772
- data[51] = 0;
1773
- } else if (locaLength === numGlyphsPlusOne << 2) {
1774
- // 0x0001 indicates the loca table consists of long offsets
1775
- data[50] = 0;
1776
- data[51] = 1;
1777
- } else {
1778
- throw new FormatError(
1779
- "Could not fix indexToLocFormat: " + indexToLocFormat
1780
- );
1781
- }
1782
- }
1783
- }
1784
-
1785
- function sanitizeGlyphLocations(
1786
- loca,
1787
- glyf,
1788
- numGlyphs,
1789
- isGlyphLocationsLong,
1790
- hintsValid,
1791
- dupFirstEntry,
1792
- maxSizeOfInstructions
1793
- ) {
1794
- let itemSize, itemDecode, itemEncode;
1795
- if (isGlyphLocationsLong) {
1796
- itemSize = 4;
1797
- itemDecode = function fontItemDecodeLong(data, offset) {
1798
- return (
1799
- (data[offset] << 24) |
1800
- (data[offset + 1] << 16) |
1801
- (data[offset + 2] << 8) |
1802
- data[offset + 3]
1803
- );
1804
- };
1805
- itemEncode = function fontItemEncodeLong(data, offset, value) {
1806
- data[offset] = (value >>> 24) & 0xff;
1807
- data[offset + 1] = (value >> 16) & 0xff;
1808
- data[offset + 2] = (value >> 8) & 0xff;
1809
- data[offset + 3] = value & 0xff;
1810
- };
1811
- } else {
1812
- itemSize = 2;
1813
- itemDecode = function fontItemDecode(data, offset) {
1814
- return (data[offset] << 9) | (data[offset + 1] << 1);
1815
- };
1816
- itemEncode = function fontItemEncode(data, offset, value) {
1817
- data[offset] = (value >> 9) & 0xff;
1818
- data[offset + 1] = (value >> 1) & 0xff;
1819
- };
1820
- }
1821
- // The first glyph is duplicated.
1822
- const numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;
1823
- const locaDataSize = itemSize * (1 + numGlyphsOut);
1824
- // Resize loca table to account for duplicated glyph.
1825
- const locaData = new Uint8Array(locaDataSize);
1826
- locaData.set(loca.data.subarray(0, locaDataSize));
1827
- loca.data = locaData;
1828
- // removing the invalid glyphs
1829
- const oldGlyfData = glyf.data;
1830
- const oldGlyfDataLength = oldGlyfData.length;
1831
- const newGlyfData = new Uint8Array(oldGlyfDataLength);
1832
-
1833
- // The spec says the offsets should be in ascending order, however
1834
- // this is not true for some fonts or they use the offset of 0 to mark a
1835
- // glyph as missing. OTS requires the offsets to be in order and not to
1836
- // be zero, so we must sort and rebuild the loca table and potentially
1837
- // re-arrange the glyf data.
1838
- let i, j;
1839
- const locaEntries = [];
1840
- // There are numGlyphs + 1 loca table entries.
1841
- for (i = 0, j = 0; i < numGlyphs + 1; i++, j += itemSize) {
1842
- let offset = itemDecode(locaData, j);
1843
- if (offset > oldGlyfDataLength) {
1844
- offset = oldGlyfDataLength;
1845
- }
1846
- locaEntries.push({
1847
- index: i,
1848
- offset,
1849
- endOffset: 0,
1850
- });
1851
- }
1852
- locaEntries.sort((a, b) => {
1853
- return a.offset - b.offset;
1854
- });
1855
- // Now the offsets are sorted, calculate the end offset of each glyph.
1856
- // The last loca entry's endOffset is not calculated since it's the end
1857
- // of the data and will be stored on the previous entry's endOffset.
1858
- for (i = 0; i < numGlyphs; i++) {
1859
- locaEntries[i].endOffset = locaEntries[i + 1].offset;
1860
- }
1861
- // Re-sort so glyphs aren't out of order.
1862
- locaEntries.sort((a, b) => {
1863
- return a.index - b.index;
1864
- });
1865
-
1866
- const missingGlyphs = Object.create(null);
1867
- let writeOffset = 0;
1868
- itemEncode(locaData, 0, writeOffset);
1869
- for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
1870
- const glyphProfile = sanitizeGlyph(
1871
- oldGlyfData,
1872
- locaEntries[i].offset,
1873
- locaEntries[i].endOffset,
1874
- newGlyfData,
1875
- writeOffset,
1876
- hintsValid
1877
- );
1878
- const newLength = glyphProfile.length;
1879
- if (newLength === 0) {
1880
- missingGlyphs[i] = true;
1881
- }
1882
- if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) {
1883
- maxSizeOfInstructions = glyphProfile.sizeOfInstructions;
1884
- }
1885
- writeOffset += newLength;
1886
- itemEncode(locaData, j, writeOffset);
1887
- }
1888
-
1889
- if (writeOffset === 0) {
1890
- // glyf table cannot be empty -- redoing the glyf and loca tables
1891
- // to have single glyph with one point
1892
- const simpleGlyph = new Uint8Array([
1893
- 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0,
1894
- ]);
1895
- for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {
1896
- itemEncode(locaData, j, simpleGlyph.length);
1897
- }
1898
- glyf.data = simpleGlyph;
1899
- } else if (dupFirstEntry) {
1900
- // Browsers will not display a glyph at position 0. Typically glyph 0
1901
- // is notdef, but a number of fonts put a valid glyph there so it must
1902
- // be duplicated and appended.
1903
- const firstEntryLength = itemDecode(locaData, itemSize);
1904
- if (newGlyfData.length > firstEntryLength + writeOffset) {
1905
- glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
1906
- } else {
1907
- glyf.data = new Uint8Array(firstEntryLength + writeOffset);
1908
- glyf.data.set(newGlyfData.subarray(0, writeOffset));
1909
- }
1910
- glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);
1911
- itemEncode(
1912
- loca.data,
1913
- locaData.length - itemSize,
1914
- writeOffset + firstEntryLength
1915
- );
1916
- } else {
1917
- glyf.data = newGlyfData.subarray(0, writeOffset);
1918
- }
1919
- return {
1920
- missingGlyphs,
1921
- maxSizeOfInstructions,
1922
- };
1923
- }
1924
-
1925
- function readPostScriptTable(post, propertiesObj, maxpNumGlyphs) {
1926
- const start = (font.start ? font.start : 0) + post.offset;
1927
- font.pos = start;
1928
-
1929
- const length = post.length,
1930
- end = start + length;
1931
- const version = font.getInt32();
1932
- // skip rest to the tables
1933
- font.skip(28);
1934
-
1935
- let glyphNames;
1936
- let valid = true;
1937
- let i;
1938
-
1939
- switch (version) {
1940
- case 0x00010000:
1941
- glyphNames = MacStandardGlyphOrdering;
1942
- break;
1943
- case 0x00020000:
1944
- const numGlyphs = font.getUint16();
1945
- if (numGlyphs !== maxpNumGlyphs) {
1946
- valid = false;
1947
- break;
1948
- }
1949
- const glyphNameIndexes = [];
1950
- for (i = 0; i < numGlyphs; ++i) {
1951
- const index = font.getUint16();
1952
- if (index >= 32768) {
1953
- valid = false;
1954
- break;
1955
- }
1956
- glyphNameIndexes.push(index);
1957
- }
1958
- if (!valid) {
1959
- break;
1960
- }
1961
- const customNames = [],
1962
- strBuf = [];
1963
- while (font.pos < end) {
1964
- const stringLength = font.getByte();
1965
- strBuf.length = stringLength;
1966
- for (i = 0; i < stringLength; ++i) {
1967
- strBuf[i] = String.fromCharCode(font.getByte());
1968
- }
1969
- customNames.push(strBuf.join(""));
1970
- }
1971
- glyphNames = [];
1972
- for (i = 0; i < numGlyphs; ++i) {
1973
- const j = glyphNameIndexes[i];
1974
- if (j < 258) {
1975
- glyphNames.push(MacStandardGlyphOrdering[j]);
1976
- continue;
1977
- }
1978
- glyphNames.push(customNames[j - 258]);
1979
- }
1980
- break;
1981
- case 0x00030000:
1982
- break;
1983
- default:
1984
- warn("Unknown/unsupported post table version " + version);
1985
- valid = false;
1986
- if (propertiesObj.defaultEncoding) {
1987
- glyphNames = propertiesObj.defaultEncoding;
1988
- }
1989
- break;
1990
- }
1991
- propertiesObj.glyphNames = glyphNames;
1992
- return valid;
1993
- }
1994
-
1995
- function readNameTable(nameTable) {
1996
- const start = (font.start ? font.start : 0) + nameTable.offset;
1997
- font.pos = start;
1998
-
1999
- const names = [[], []];
2000
- const length = nameTable.length,
2001
- end = start + length;
2002
- const format = font.getUint16();
2003
- const FORMAT_0_HEADER_LENGTH = 6;
2004
- if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
2005
- // unsupported name table format or table "too" small
2006
- return names;
2007
- }
2008
- const numRecords = font.getUint16();
2009
- const stringsStart = font.getUint16();
2010
- const records = [];
2011
- const NAME_RECORD_LENGTH = 12;
2012
- let i, ii;
2013
-
2014
- for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {
2015
- const r = {
2016
- platform: font.getUint16(),
2017
- encoding: font.getUint16(),
2018
- language: font.getUint16(),
2019
- name: font.getUint16(),
2020
- length: font.getUint16(),
2021
- offset: font.getUint16(),
2022
- };
2023
- // using only Macintosh and Windows platform/encoding names
2024
- if (
2025
- (r.platform === 1 && r.encoding === 0 && r.language === 0) ||
2026
- (r.platform === 3 && r.encoding === 1 && r.language === 0x409)
2027
- ) {
2028
- records.push(r);
2029
- }
2030
- }
2031
- for (i = 0, ii = records.length; i < ii; i++) {
2032
- const record = records[i];
2033
- if (record.length <= 0) {
2034
- continue; // Nothing to process, ignoring.
2035
- }
2036
- const pos = start + stringsStart + record.offset;
2037
- if (pos + record.length > end) {
2038
- continue; // outside of name table, ignoring
2039
- }
2040
- font.pos = pos;
2041
- const nameIndex = record.name;
2042
- if (record.encoding) {
2043
- // unicode
2044
- let str = "";
2045
- for (let j = 0, jj = record.length; j < jj; j += 2) {
2046
- str += String.fromCharCode(font.getUint16());
2047
- }
2048
- names[1][nameIndex] = str;
2049
- } else {
2050
- names[0][nameIndex] = font.getString(record.length);
2051
- }
2052
- }
2053
- return names;
2054
- }
2055
-
2056
- // prettier-ignore
2057
- const TTOpsStackDeltas = [
2058
- 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
2059
- -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
2060
- 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1,
2061
- 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2,
2062
- 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1,
2063
- -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1,
2064
- -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2065
- -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1,
2066
- -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];
2067
- // 0xC0-DF == -1 and 0xE0-FF == -2
2068
-
2069
- function sanitizeTTProgram(table, ttContext) {
2070
- let data = table.data;
2071
- let i = 0,
2072
- j,
2073
- n,
2074
- b,
2075
- funcId,
2076
- pc,
2077
- lastEndf = 0,
2078
- lastDeff = 0;
2079
- const stack = [];
2080
- const callstack = [];
2081
- const functionsCalled = [];
2082
- let tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions;
2083
- let inFDEF = false,
2084
- ifLevel = 0,
2085
- inELSE = 0;
2086
- for (let ii = data.length; i < ii; ) {
2087
- const op = data[i++];
2088
- // The TrueType instruction set docs can be found at
2089
- // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
2090
- if (op === 0x40) {
2091
- // NPUSHB - pushes n bytes
2092
- n = data[i++];
2093
- if (inFDEF || inELSE) {
2094
- i += n;
2095
- } else {
2096
- for (j = 0; j < n; j++) {
2097
- stack.push(data[i++]);
2098
- }
2099
- }
2100
- } else if (op === 0x41) {
2101
- // NPUSHW - pushes n words
2102
- n = data[i++];
2103
- if (inFDEF || inELSE) {
2104
- i += n * 2;
2105
- } else {
2106
- for (j = 0; j < n; j++) {
2107
- b = data[i++];
2108
- stack.push((b << 8) | data[i++]);
2109
- }
2110
- }
2111
- } else if ((op & 0xf8) === 0xb0) {
2112
- // PUSHB - pushes bytes
2113
- n = op - 0xb0 + 1;
2114
- if (inFDEF || inELSE) {
2115
- i += n;
2116
- } else {
2117
- for (j = 0; j < n; j++) {
2118
- stack.push(data[i++]);
2119
- }
2120
- }
2121
- } else if ((op & 0xf8) === 0xb8) {
2122
- // PUSHW - pushes words
2123
- n = op - 0xb8 + 1;
2124
- if (inFDEF || inELSE) {
2125
- i += n * 2;
2126
- } else {
2127
- for (j = 0; j < n; j++) {
2128
- b = data[i++];
2129
- stack.push((b << 8) | data[i++]);
2130
- }
2131
- }
2132
- } else if (op === 0x2b && !tooComplexToFollowFunctions) {
2133
- // CALL
2134
- if (!inFDEF && !inELSE) {
2135
- // collecting information about which functions are used
2136
- funcId = stack[stack.length - 1];
2137
- if (isNaN(funcId)) {
2138
- info("TT: CALL empty stack (or invalid entry).");
2139
- } else {
2140
- ttContext.functionsUsed[funcId] = true;
2141
- if (funcId in ttContext.functionsStackDeltas) {
2142
- const newStackLength =
2143
- stack.length + ttContext.functionsStackDeltas[funcId];
2144
- if (newStackLength < 0) {
2145
- warn("TT: CALL invalid functions stack delta.");
2146
- ttContext.hintsValid = false;
2147
- return;
2148
- }
2149
- stack.length = newStackLength;
2150
- } else if (
2151
- funcId in ttContext.functionsDefined &&
2152
- !functionsCalled.includes(funcId)
2153
- ) {
2154
- callstack.push({ data, i, stackTop: stack.length - 1 });
2155
- functionsCalled.push(funcId);
2156
- pc = ttContext.functionsDefined[funcId];
2157
- if (!pc) {
2158
- warn("TT: CALL non-existent function");
2159
- ttContext.hintsValid = false;
2160
- return;
2161
- }
2162
- data = pc.data;
2163
- i = pc.i;
2164
- }
2165
- }
2166
- }
2167
- } else if (op === 0x2c && !tooComplexToFollowFunctions) {
2168
- // FDEF
2169
- if (inFDEF || inELSE) {
2170
- warn("TT: nested FDEFs not allowed");
2171
- tooComplexToFollowFunctions = true;
2172
- }
2173
- inFDEF = true;
2174
- // collecting information about which functions are defined
2175
- lastDeff = i;
2176
- funcId = stack.pop();
2177
- ttContext.functionsDefined[funcId] = { data, i };
2178
- } else if (op === 0x2d) {
2179
- // ENDF - end of function
2180
- if (inFDEF) {
2181
- inFDEF = false;
2182
- lastEndf = i;
2183
- } else {
2184
- pc = callstack.pop();
2185
- if (!pc) {
2186
- warn("TT: ENDF bad stack");
2187
- ttContext.hintsValid = false;
2188
- return;
2189
- }
2190
- funcId = functionsCalled.pop();
2191
- data = pc.data;
2192
- i = pc.i;
2193
- ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;
2194
- }
2195
- } else if (op === 0x89) {
2196
- // IDEF - instruction definition
2197
- if (inFDEF || inELSE) {
2198
- warn("TT: nested IDEFs not allowed");
2199
- tooComplexToFollowFunctions = true;
2200
- }
2201
- inFDEF = true;
2202
- // recording it as a function to track ENDF
2203
- lastDeff = i;
2204
- } else if (op === 0x58) {
2205
- // IF
2206
- ++ifLevel;
2207
- } else if (op === 0x1b) {
2208
- // ELSE
2209
- inELSE = ifLevel;
2210
- } else if (op === 0x59) {
2211
- // EIF
2212
- if (inELSE === ifLevel) {
2213
- inELSE = 0;
2214
- }
2215
- --ifLevel;
2216
- } else if (op === 0x1c) {
2217
- // JMPR
2218
- if (!inFDEF && !inELSE) {
2219
- const offset = stack[stack.length - 1];
2220
- // only jumping forward to prevent infinite loop
2221
- if (offset > 0) {
2222
- i += offset - 1;
2223
- }
2224
- }
2225
- }
2226
- // Adjusting stack not extactly, but just enough to get function id
2227
- if (!inFDEF && !inELSE) {
2228
- let stackDelta = 0;
2229
- if (op <= 0x8e) {
2230
- stackDelta = TTOpsStackDeltas[op];
2231
- } else if (op >= 0xc0 && op <= 0xdf) {
2232
- stackDelta = -1;
2233
- } else if (op >= 0xe0) {
2234
- stackDelta = -2;
2235
- }
2236
- if (op >= 0x71 && op <= 0x75) {
2237
- n = stack.pop();
2238
- if (!isNaN(n)) {
2239
- stackDelta = -n * 2;
2240
- }
2241
- }
2242
- while (stackDelta < 0 && stack.length > 0) {
2243
- stack.pop();
2244
- stackDelta++;
2245
- }
2246
- while (stackDelta > 0) {
2247
- stack.push(NaN); // pushing any number into stack
2248
- stackDelta--;
2249
- }
2250
- }
2251
- }
2252
- ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
2253
- const content = [data];
2254
- if (i > data.length) {
2255
- content.push(new Uint8Array(i - data.length));
2256
- }
2257
- if (lastDeff > lastEndf) {
2258
- warn("TT: complementing a missing function tail");
2259
- // new function definition started, but not finished
2260
- // complete function by [CLEAR, ENDF]
2261
- content.push(new Uint8Array([0x22, 0x2d]));
2262
- }
2263
- foldTTTable(table, content);
2264
- }
2265
-
2266
- function checkInvalidFunctions(ttContext, maxFunctionDefs) {
2267
- if (ttContext.tooComplexToFollowFunctions) {
2268
- return;
2269
- }
2270
- if (ttContext.functionsDefined.length > maxFunctionDefs) {
2271
- warn("TT: more functions defined than expected");
2272
- ttContext.hintsValid = false;
2273
- return;
2274
- }
2275
- for (let j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
2276
- if (j > maxFunctionDefs) {
2277
- warn("TT: invalid function id: " + j);
2278
- ttContext.hintsValid = false;
2279
- return;
2280
- }
2281
- if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
2282
- warn("TT: undefined function: " + j);
2283
- ttContext.hintsValid = false;
2284
- return;
2285
- }
2286
- }
2287
- }
2288
-
2289
- function foldTTTable(table, content) {
2290
- if (content.length > 1) {
2291
- // concatenating the content items
2292
- let newLength = 0;
2293
- let j, jj;
2294
- for (j = 0, jj = content.length; j < jj; j++) {
2295
- newLength += content[j].length;
2296
- }
2297
- newLength = (newLength + 3) & ~3;
2298
- const result = new Uint8Array(newLength);
2299
- let pos = 0;
2300
- for (j = 0, jj = content.length; j < jj; j++) {
2301
- result.set(content[j], pos);
2302
- pos += content[j].length;
2303
- }
2304
- table.data = result;
2305
- table.length = newLength;
2306
- }
2307
- }
2308
-
2309
- function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) {
2310
- const ttContext = {
2311
- functionsDefined: [],
2312
- functionsUsed: [],
2313
- functionsStackDeltas: [],
2314
- tooComplexToFollowFunctions: false,
2315
- hintsValid: true,
2316
- };
2317
- if (fpgm) {
2318
- sanitizeTTProgram(fpgm, ttContext);
2319
- }
2320
- if (prep) {
2321
- sanitizeTTProgram(prep, ttContext);
2322
- }
2323
- if (fpgm) {
2324
- checkInvalidFunctions(ttContext, maxFunctionDefs);
2325
- }
2326
- if (cvt && cvt.length & 1) {
2327
- const cvtData = new Uint8Array(cvt.length + 1);
2328
- cvtData.set(cvt.data);
2329
- cvt.data = cvtData;
2330
- }
2331
- return ttContext.hintsValid;
2332
- }
2333
-
2334
- // The following steps modify the original font data, making copy
2335
- font = new Stream(new Uint8Array(font.getBytes()));
2336
-
2337
- let header, tables;
2338
- if (isTrueTypeCollectionFile(font)) {
2339
- const ttcData = readTrueTypeCollectionData(font, this.name);
2340
- header = ttcData.header;
2341
- tables = ttcData.tables;
2342
- } else {
2343
- header = readOpenTypeHeader(font);
2344
- tables = readTables(font, header.numTables);
2345
- }
2346
- let cff, cffFile;
2347
-
2348
- const isTrueType = !tables["CFF "];
2349
- if (!isTrueType) {
2350
- const isComposite =
2351
- properties.composite &&
2352
- ((properties.cidToGidMap || []).length > 0 ||
2353
- !(properties.cMap instanceof IdentityCMap));
2354
- // OpenType font (skip composite fonts with non-default glyph mapping).
2355
- if (
2356
- (header.version === "OTTO" && !isComposite) ||
2357
- !tables.head ||
2358
- !tables.hhea ||
2359
- !tables.maxp ||
2360
- !tables.post
2361
- ) {
2362
- // No major tables: throwing everything at `CFFFont`.
2363
- cffFile = new Stream(tables["CFF "].data);
2364
- cff = new CFFFont(cffFile, properties);
2365
-
2366
- adjustWidths(properties);
2367
-
2368
- return this.convert(name, cff, properties);
2369
- }
2370
-
2371
- delete tables.glyf;
2372
- delete tables.loca;
2373
- delete tables.fpgm;
2374
- delete tables.prep;
2375
- delete tables["cvt "];
2376
- this.isOpenType = true;
2377
- } else {
2378
- if (!tables.loca) {
2379
- throw new FormatError('Required "loca" table is not found');
2380
- }
2381
- if (!tables.glyf) {
2382
- warn('Required "glyf" table is not found -- trying to recover.');
2383
- // Note: We use `sanitizeGlyphLocations` to add dummy glyf data below.
2384
- tables.glyf = {
2385
- tag: "glyf",
2386
- data: new Uint8Array(0),
2387
- };
2388
- }
2389
- this.isOpenType = false;
2390
- }
2391
-
2392
- if (!tables.maxp) {
2393
- throw new FormatError('Required "maxp" table is not found');
2394
- }
2395
-
2396
- font.pos = (font.start || 0) + tables.maxp.offset;
2397
- const version = font.getInt32();
2398
- const numGlyphs = font.getUint16();
2399
-
2400
- if (
2401
- properties.scaleFactors &&
2402
- properties.scaleFactors.length === numGlyphs &&
2403
- isTrueType
2404
- ) {
2405
- const { scaleFactors } = properties;
2406
- const isGlyphLocationsLong = int16(
2407
- tables.head.data[50],
2408
- tables.head.data[51]
2409
- );
2410
-
2411
- const glyphs = new GlyfTable({
2412
- glyfTable: tables.glyf.data,
2413
- isGlyphLocationsLong,
2414
- locaTable: tables.loca.data,
2415
- numGlyphs,
2416
- });
2417
- glyphs.scale(scaleFactors);
2418
-
2419
- const { glyf, loca, isLocationLong } = glyphs.write();
2420
- tables.glyf.data = glyf;
2421
- tables.loca.data = loca;
2422
-
2423
- if (isLocationLong !== !!isGlyphLocationsLong) {
2424
- tables.head.data[50] = 0;
2425
- tables.head.data[51] = isLocationLong ? 1 : 0;
2426
- }
2427
-
2428
- const metrics = tables.hmtx.data;
2429
-
2430
- for (let i = 0; i < numGlyphs; i++) {
2431
- const j = 4 * i;
2432
- const advanceWidth = Math.round(
2433
- scaleFactors[i] * int16(metrics[j], metrics[j + 1])
2434
- );
2435
- metrics[j] = (advanceWidth >> 8) & 0xff;
2436
- metrics[j + 1] = advanceWidth & 0xff;
2437
- const lsb = Math.round(
2438
- scaleFactors[i] * signedInt16(metrics[j + 2], metrics[j + 3])
2439
- );
2440
- writeSignedInt16(metrics, j + 2, lsb);
2441
- }
2442
- }
2443
-
2444
- // Glyph 0 is duplicated and appended.
2445
- let numGlyphsOut = numGlyphs + 1;
2446
- let dupFirstEntry = true;
2447
- if (numGlyphsOut > 0xffff) {
2448
- dupFirstEntry = false;
2449
- numGlyphsOut = numGlyphs;
2450
- warn("Not enough space in glyfs to duplicate first glyph.");
2451
- }
2452
- let maxFunctionDefs = 0;
2453
- let maxSizeOfInstructions = 0;
2454
- if (version >= 0x00010000 && tables.maxp.length >= 22) {
2455
- // maxZones can be invalid
2456
- font.pos += 8;
2457
- const maxZones = font.getUint16();
2458
- if (maxZones > 2) {
2459
- // reset to 2 if font has invalid maxZones
2460
- tables.maxp.data[14] = 0;
2461
- tables.maxp.data[15] = 2;
2462
- }
2463
- font.pos += 4;
2464
- maxFunctionDefs = font.getUint16();
2465
- font.pos += 4;
2466
- maxSizeOfInstructions = font.getUint16();
2467
- }
2468
-
2469
- tables.maxp.data[4] = numGlyphsOut >> 8;
2470
- tables.maxp.data[5] = numGlyphsOut & 255;
2471
-
2472
- const hintsValid = sanitizeTTPrograms(
2473
- tables.fpgm,
2474
- tables.prep,
2475
- tables["cvt "],
2476
- maxFunctionDefs
2477
- );
2478
- if (!hintsValid) {
2479
- delete tables.fpgm;
2480
- delete tables.prep;
2481
- delete tables["cvt "];
2482
- }
2483
-
2484
- // Ensure the hmtx table contains the advance width and
2485
- // sidebearings information for numGlyphs in the maxp table
2486
- sanitizeMetrics(
2487
- font,
2488
- tables.hhea,
2489
- tables.hmtx,
2490
- tables.head,
2491
- numGlyphsOut,
2492
- dupFirstEntry
2493
- );
2494
-
2495
- if (!tables.head) {
2496
- throw new FormatError('Required "head" table is not found');
2497
- }
2498
-
2499
- sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0);
2500
-
2501
- let missingGlyphs = Object.create(null);
2502
- if (isTrueType) {
2503
- const isGlyphLocationsLong = int16(
2504
- tables.head.data[50],
2505
- tables.head.data[51]
2506
- );
2507
- const glyphsInfo = sanitizeGlyphLocations(
2508
- tables.loca,
2509
- tables.glyf,
2510
- numGlyphs,
2511
- isGlyphLocationsLong,
2512
- hintsValid,
2513
- dupFirstEntry,
2514
- maxSizeOfInstructions
2515
- );
2516
- missingGlyphs = glyphsInfo.missingGlyphs;
2517
-
2518
- // Some fonts have incorrect maxSizeOfInstructions values, so we use
2519
- // the computed value instead.
2520
- if (version >= 0x00010000 && tables.maxp.length >= 22) {
2521
- tables.maxp.data[26] = glyphsInfo.maxSizeOfInstructions >> 8;
2522
- tables.maxp.data[27] = glyphsInfo.maxSizeOfInstructions & 255;
2523
- }
2524
- }
2525
- if (!tables.hhea) {
2526
- throw new FormatError('Required "hhea" table is not found');
2527
- }
2528
-
2529
- // Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
2530
- // Sometimes it's 0. That needs to be fixed
2531
- if (tables.hhea.data[10] === 0 && tables.hhea.data[11] === 0) {
2532
- tables.hhea.data[10] = 0xff;
2533
- tables.hhea.data[11] = 0xff;
2534
- }
2535
-
2536
- // Extract some more font properties from the OpenType head and
2537
- // hhea tables; yMin and descent value are always negative.
2538
- const metricsOverride = {
2539
- unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),
2540
- yMax: int16(tables.head.data[42], tables.head.data[43]),
2541
- yMin: signedInt16(tables.head.data[38], tables.head.data[39]),
2542
- ascent: signedInt16(tables.hhea.data[4], tables.hhea.data[5]),
2543
- descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7]),
2544
- lineGap: signedInt16(tables.hhea.data[8], tables.hhea.data[9]),
2545
- };
2546
-
2547
- // PDF FontDescriptor metrics lie -- using data from actual font.
2548
- this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;
2549
- this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;
2550
- this.lineGap = metricsOverride.lineGap / metricsOverride.unitsPerEm;
2551
-
2552
- if (this.cssFontInfo && this.cssFontInfo.lineHeight) {
2553
- this.lineHeight = this.cssFontInfo.lineHeight;
2554
- } else {
2555
- this.lineHeight = this.ascent - this.descent + this.lineGap;
2556
- }
2557
-
2558
- // The 'post' table has glyphs names.
2559
- if (tables.post) {
2560
- readPostScriptTable(tables.post, properties, numGlyphs);
2561
- }
2562
-
2563
- // The original 'post' table is not needed, replace it.
2564
- tables.post = {
2565
- tag: "post",
2566
- data: createPostTable(properties),
2567
- };
2568
-
2569
- const charCodeToGlyphId = [];
2570
-
2571
- // Helper function to try to skip mapping of empty glyphs.
2572
- function hasGlyph(glyphId) {
2573
- return !missingGlyphs[glyphId];
2574
- }
2575
-
2576
- if (properties.composite) {
2577
- const cidToGidMap = properties.cidToGidMap || [];
2578
- const isCidToGidMapEmpty = cidToGidMap.length === 0;
2579
-
2580
- properties.cMap.forEach(function (charCode, cid) {
2581
- if (cid > 0xffff) {
2582
- throw new FormatError("Max size of CID is 65,535");
2583
- }
2584
- let glyphId = -1;
2585
- if (isCidToGidMapEmpty) {
2586
- glyphId = cid;
2587
- } else if (cidToGidMap[cid] !== undefined) {
2588
- glyphId = cidToGidMap[cid];
2589
- }
2590
-
2591
- if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
2592
- charCodeToGlyphId[charCode] = glyphId;
2593
- }
2594
- });
2595
- } else {
2596
- // Most of the following logic in this code branch is based on the
2597
- // 9.6.6.4 of the PDF spec.
2598
- const cmapTable = readCmapTable(
2599
- tables.cmap,
2600
- font,
2601
- this.isSymbolicFont,
2602
- properties.hasEncoding
2603
- );
2604
- const cmapPlatformId = cmapTable.platformId;
2605
- const cmapEncodingId = cmapTable.encodingId;
2606
- const cmapMappings = cmapTable.mappings;
2607
- const cmapMappingsLength = cmapMappings.length;
2608
- let baseEncoding = [];
2609
- if (
2610
- properties.hasEncoding &&
2611
- (properties.baseEncodingName === "MacRomanEncoding" ||
2612
- properties.baseEncodingName === "WinAnsiEncoding")
2613
- ) {
2614
- baseEncoding = getEncoding(properties.baseEncodingName);
2615
- }
2616
-
2617
- // If the font has an encoding and is not symbolic then follow the
2618
- // rules in section 9.6.6.4 of the spec on how to map 3,1 and 1,0
2619
- // cmaps.
2620
- if (
2621
- properties.hasEncoding &&
2622
- !this.isSymbolicFont &&
2623
- ((cmapPlatformId === 3 && cmapEncodingId === 1) ||
2624
- (cmapPlatformId === 1 && cmapEncodingId === 0))
2625
- ) {
2626
- const glyphsUnicodeMap = getGlyphsUnicode();
2627
- for (let charCode = 0; charCode < 256; charCode++) {
2628
- let glyphName;
2629
- if (this.differences[charCode] !== undefined) {
2630
- glyphName = this.differences[charCode];
2631
- } else if (baseEncoding[charCode] !== "") {
2632
- glyphName = baseEncoding[charCode];
2633
- } else {
2634
- glyphName = StandardEncoding[charCode];
2635
- }
2636
- if (!glyphName) {
2637
- continue;
2638
- }
2639
- // Ensure that non-standard glyph names are resolved to valid ones.
2640
- const standardGlyphName = recoverGlyphName(
2641
- glyphName,
2642
- glyphsUnicodeMap
2643
- );
2644
-
2645
- let unicodeOrCharCode;
2646
- if (cmapPlatformId === 3 && cmapEncodingId === 1) {
2647
- unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName];
2648
- } else if (cmapPlatformId === 1 && cmapEncodingId === 0) {
2649
- // TODO: the encoding needs to be updated with mac os table.
2650
- unicodeOrCharCode = MacRomanEncoding.indexOf(standardGlyphName);
2651
- }
2652
-
2653
- for (let i = 0; i < cmapMappingsLength; ++i) {
2654
- if (cmapMappings[i].charCode !== unicodeOrCharCode) {
2655
- continue;
2656
- }
2657
- charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
2658
- break;
2659
- }
2660
- }
2661
- } else if (cmapPlatformId === 0) {
2662
- // Default Unicode semantics, use the charcodes as is.
2663
- for (let i = 0; i < cmapMappingsLength; ++i) {
2664
- charCodeToGlyphId[cmapMappings[i].charCode] = cmapMappings[i].glyphId;
2665
- }
2666
- } else {
2667
- // When there is only a (1, 0) cmap table, the char code is a single
2668
- // byte and it is used directly as the char code.
2669
-
2670
- // When a (3, 0) cmap table is present, it is used instead but the
2671
- // spec has special rules for char codes in the range of 0xF000 to
2672
- // 0xF0FF and it says the (3, 0) table should map the values from
2673
- // the (1, 0) table by prepending 0xF0 to the char codes. To reverse
2674
- // this, the upper bits of the char code are cleared, but only for the
2675
- // special range since some PDFs have char codes outside of this range
2676
- // (e.g. 0x2013) which when masked would overwrite other values in the
2677
- // cmap.
2678
- for (let i = 0; i < cmapMappingsLength; ++i) {
2679
- let charCode = cmapMappings[i].charCode;
2680
- if (
2681
- cmapPlatformId === 3 &&
2682
- charCode >= 0xf000 &&
2683
- charCode <= 0xf0ff
2684
- ) {
2685
- charCode &= 0xff;
2686
- }
2687
- charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
2688
- }
2689
- }
2690
-
2691
- // Last, try to map any missing charcodes using the post table.
2692
- if (
2693
- properties.glyphNames &&
2694
- (baseEncoding.length || this.differences.length)
2695
- ) {
2696
- for (let i = 0; i < 256; ++i) {
2697
- if (charCodeToGlyphId[i] !== undefined) {
2698
- continue;
2699
- }
2700
- const glyphName = this.differences[i] || baseEncoding[i];
2701
- if (!glyphName) {
2702
- continue;
2703
- }
2704
- const glyphId = properties.glyphNames.indexOf(glyphName);
2705
- if (glyphId > 0 && hasGlyph(glyphId)) {
2706
- charCodeToGlyphId[i] = glyphId;
2707
- }
2708
- }
2709
- }
2710
- }
2711
-
2712
- if (charCodeToGlyphId.length === 0) {
2713
- // defines at least one glyph
2714
- charCodeToGlyphId[0] = 0;
2715
- }
2716
-
2717
- // Typically glyph 0 is duplicated and the mapping must be updated, but if
2718
- // there isn't enough room to duplicate, the glyph id is left the same. In
2719
- // this case, glyph 0 may not work correctly, but that is better than
2720
- // having the whole font fail.
2721
- let glyphZeroId = numGlyphsOut - 1;
2722
- if (!dupFirstEntry) {
2723
- glyphZeroId = 0;
2724
- }
2725
-
2726
- // When `cssFontInfo` is set, the font is used to render text in the HTML
2727
- // view (e.g. with Xfa) so nothing must be moved in the private use area.
2728
- if (!properties.cssFontInfo) {
2729
- // Converting glyphs and ids into font's cmap table
2730
- const newMapping = adjustMapping(
2731
- charCodeToGlyphId,
2732
- hasGlyph,
2733
- glyphZeroId
2734
- );
2735
- this.toFontChar = newMapping.toFontChar;
2736
- tables.cmap = {
2737
- tag: "cmap",
2738
- data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut),
2739
- };
2740
-
2741
- if (!tables["OS/2"] || !validateOS2Table(tables["OS/2"], font)) {
2742
- tables["OS/2"] = {
2743
- tag: "OS/2",
2744
- data: createOS2Table(
2745
- properties,
2746
- newMapping.charCodeToGlyphId,
2747
- metricsOverride
2748
- ),
2749
- };
2750
- }
2751
- }
2752
-
2753
- if (!isTrueType) {
2754
- try {
2755
- // Trying to repair CFF file
2756
- cffFile = new Stream(tables["CFF "].data);
2757
- const parser = new CFFParser(
2758
- cffFile,
2759
- properties,
2760
- SEAC_ANALYSIS_ENABLED
2761
- );
2762
- cff = parser.parse();
2763
- cff.duplicateFirstGlyph();
2764
- const compiler = new CFFCompiler(cff);
2765
- tables["CFF "].data = compiler.compile();
2766
- } catch (e) {
2767
- warn("Failed to compile font " + properties.loadedName);
2768
- }
2769
- }
2770
-
2771
- // Re-creating 'name' table
2772
- if (!tables.name) {
2773
- tables.name = {
2774
- tag: "name",
2775
- data: createNameTable(this.name),
2776
- };
2777
- } else {
2778
- // ... using existing 'name' table as prototype
2779
- const namePrototype = readNameTable(tables.name);
2780
- tables.name.data = createNameTable(name, namePrototype);
2781
- this.psName = namePrototype[0][6] || null;
2782
- }
2783
-
2784
- const builder = new OpenTypeFileBuilder(header.version);
2785
- for (const tableTag in tables) {
2786
- builder.addTable(tableTag, tables[tableTag].data);
2787
- }
2788
- return builder.toArray();
2789
- }
2790
-
2791
- convert(fontName, font, properties) {
2792
- // TODO: Check the charstring widths to determine this.
2793
- properties.fixedPitch = false;
2794
-
2795
- if (properties.builtInEncoding) {
2796
- // For Type1 fonts that do not include either `ToUnicode` or `Encoding`
2797
- // data, attempt to use the `builtInEncoding` to improve text selection.
2798
- adjustToUnicode(properties, properties.builtInEncoding);
2799
- }
2800
-
2801
- // Type 1 fonts have a notdef inserted at the beginning, so glyph 0
2802
- // becomes glyph 1. In a CFF font glyph 0 is appended to the end of the
2803
- // char strings.
2804
- let glyphZeroId = 1;
2805
- if (font instanceof CFFFont) {
2806
- glyphZeroId = font.numGlyphs - 1;
2807
- }
2808
- const mapping = font.getGlyphMapping(properties);
2809
- let newMapping = null;
2810
- let newCharCodeToGlyphId = mapping;
2811
-
2812
- // When `cssFontInfo` is set, the font is used to render text in the HTML
2813
- // view (e.g. with Xfa) so nothing must be moved in the private use area.
2814
- if (!properties.cssFontInfo) {
2815
- newMapping = adjustMapping(
2816
- mapping,
2817
- font.hasGlyphId.bind(font),
2818
- glyphZeroId
2819
- );
2820
- this.toFontChar = newMapping.toFontChar;
2821
- newCharCodeToGlyphId = newMapping.charCodeToGlyphId;
2822
- }
2823
- const numGlyphs = font.numGlyphs;
2824
-
2825
- function getCharCodes(charCodeToGlyphId, glyphId) {
2826
- let charCodes = null;
2827
- for (const charCode in charCodeToGlyphId) {
2828
- if (glyphId === charCodeToGlyphId[charCode]) {
2829
- if (!charCodes) {
2830
- charCodes = [];
2831
- }
2832
- charCodes.push(charCode | 0);
2833
- }
2834
- }
2835
- return charCodes;
2836
- }
2837
-
2838
- function createCharCode(charCodeToGlyphId, glyphId) {
2839
- for (const charCode in charCodeToGlyphId) {
2840
- if (glyphId === charCodeToGlyphId[charCode]) {
2841
- return charCode | 0;
2842
- }
2843
- }
2844
- newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] =
2845
- glyphId;
2846
- return newMapping.nextAvailableFontCharCode++;
2847
- }
2848
-
2849
- const seacs = font.seacs;
2850
- if (newMapping && SEAC_ANALYSIS_ENABLED && seacs && seacs.length) {
2851
- const matrix = properties.fontMatrix || FONT_IDENTITY_MATRIX;
2852
- const charset = font.getCharset();
2853
- const seacMap = Object.create(null);
2854
- for (let glyphId in seacs) {
2855
- glyphId |= 0;
2856
- const seac = seacs[glyphId];
2857
- const baseGlyphName = StandardEncoding[seac[2]];
2858
- const accentGlyphName = StandardEncoding[seac[3]];
2859
- const baseGlyphId = charset.indexOf(baseGlyphName);
2860
- const accentGlyphId = charset.indexOf(accentGlyphName);
2861
- if (baseGlyphId < 0 || accentGlyphId < 0) {
2862
- continue;
2863
- }
2864
- const accentOffset = {
2865
- x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4],
2866
- y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5],
2867
- };
2868
-
2869
- const charCodes = getCharCodes(mapping, glyphId);
2870
- if (!charCodes) {
2871
- // There's no point in mapping it if the char code was never mapped
2872
- // to begin with.
2873
- continue;
2874
- }
2875
- for (let i = 0, ii = charCodes.length; i < ii; i++) {
2876
- const charCode = charCodes[i];
2877
- // Find a fontCharCode that maps to the base and accent glyphs.
2878
- // If one doesn't exists, create it.
2879
- const charCodeToGlyphId = newMapping.charCodeToGlyphId;
2880
- const baseFontCharCode = createCharCode(
2881
- charCodeToGlyphId,
2882
- baseGlyphId
2883
- );
2884
- const accentFontCharCode = createCharCode(
2885
- charCodeToGlyphId,
2886
- accentGlyphId
2887
- );
2888
- seacMap[charCode] = {
2889
- baseFontCharCode,
2890
- accentFontCharCode,
2891
- accentOffset,
2892
- };
2893
- }
2894
- }
2895
- properties.seacMap = seacMap;
2896
- }
2897
-
2898
- const unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
2899
-
2900
- const builder = new OpenTypeFileBuilder("\x4F\x54\x54\x4F");
2901
- // PostScript Font Program
2902
- builder.addTable("CFF ", font.data);
2903
- // OS/2 and Windows Specific metrics
2904
- builder.addTable("OS/2", createOS2Table(properties, newCharCodeToGlyphId));
2905
- // Character to glyphs mapping
2906
- builder.addTable("cmap", createCmapTable(newCharCodeToGlyphId, numGlyphs));
2907
- // Font header
2908
- builder.addTable(
2909
- "head",
2910
- "\x00\x01\x00\x00" + // Version number
2911
- "\x00\x00\x10\x00" + // fontRevision
2912
- "\x00\x00\x00\x00" + // checksumAdjustement
2913
- "\x5F\x0F\x3C\xF5" + // magicNumber
2914
- "\x00\x00" + // Flags
2915
- safeString16(unitsPerEm) + // unitsPerEM
2916
- "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // creation date
2917
- "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // modifification date
2918
- "\x00\x00" + // xMin
2919
- safeString16(properties.descent) + // yMin
2920
- "\x0F\xFF" + // xMax
2921
- safeString16(properties.ascent) + // yMax
2922
- string16(properties.italicAngle ? 2 : 0) + // macStyle
2923
- "\x00\x11" + // lowestRecPPEM
2924
- "\x00\x00" + // fontDirectionHint
2925
- "\x00\x00" + // indexToLocFormat
2926
- "\x00\x00"
2927
- ); // glyphDataFormat
2928
-
2929
- // Horizontal header
2930
- builder.addTable(
2931
- "hhea",
2932
- "\x00\x01\x00\x00" + // Version number
2933
- safeString16(properties.ascent) + // Typographic Ascent
2934
- safeString16(properties.descent) + // Typographic Descent
2935
- "\x00\x00" + // Line Gap
2936
- "\xFF\xFF" + // advanceWidthMax
2937
- "\x00\x00" + // minLeftSidebearing
2938
- "\x00\x00" + // minRightSidebearing
2939
- "\x00\x00" + // xMaxExtent
2940
- safeString16(properties.capHeight) + // caretSlopeRise
2941
- safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + // caretSlopeRun
2942
- "\x00\x00" + // caretOffset
2943
- "\x00\x00" + // -reserved-
2944
- "\x00\x00" + // -reserved-
2945
- "\x00\x00" + // -reserved-
2946
- "\x00\x00" + // -reserved-
2947
- "\x00\x00" + // metricDataFormat
2948
- string16(numGlyphs)
2949
- ); // Number of HMetrics
2950
-
2951
- // Horizontal metrics
2952
- builder.addTable(
2953
- "hmtx",
2954
- (function fontFieldsHmtx() {
2955
- const charstrings = font.charstrings;
2956
- const cffWidths = font.cff ? font.cff.widths : null;
2957
- let hmtx = "\x00\x00\x00\x00"; // Fake .notdef
2958
- for (let i = 1, ii = numGlyphs; i < ii; i++) {
2959
- let width = 0;
2960
- if (charstrings) {
2961
- const charstring = charstrings[i - 1];
2962
- width = "width" in charstring ? charstring.width : 0;
2963
- } else if (cffWidths) {
2964
- width = Math.ceil(cffWidths[i] || 0);
2965
- }
2966
- hmtx += string16(width) + string16(0);
2967
- }
2968
- return hmtx;
2969
- })()
2970
- );
2971
-
2972
- // Maximum profile
2973
- builder.addTable(
2974
- "maxp",
2975
- "\x00\x00\x50\x00" + string16(numGlyphs) // Version number
2976
- ); // Num of glyphs
2977
-
2978
- // Naming tables
2979
- builder.addTable("name", createNameTable(fontName));
2980
-
2981
- // PostScript information
2982
- builder.addTable("post", createPostTable(properties));
2983
-
2984
- return builder.toArray();
2985
- }
2986
-
2987
- get spaceWidth() {
2988
- // trying to estimate space character width
2989
- const possibleSpaceReplacements = ["space", "minus", "one", "i", "I"];
2990
- let width;
2991
- for (let i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
2992
- const glyphName = possibleSpaceReplacements[i];
2993
- // if possible, getting width by glyph name
2994
- if (glyphName in this.widths) {
2995
- width = this.widths[glyphName];
2996
- break;
2997
- }
2998
- const glyphsUnicodeMap = getGlyphsUnicode();
2999
- const glyphUnicode = glyphsUnicodeMap[glyphName];
3000
- // finding the charcode via unicodeToCID map
3001
- let charcode = 0;
3002
- if (this.composite && this.cMap.contains(glyphUnicode)) {
3003
- charcode = this.cMap.lookup(glyphUnicode);
3004
- }
3005
- // ... via toUnicode map
3006
- if (!charcode && this.toUnicode) {
3007
- charcode = this.toUnicode.charCodeOf(glyphUnicode);
3008
- }
3009
- // setting it to unicode if negative or undefined
3010
- if (charcode <= 0) {
3011
- charcode = glyphUnicode;
3012
- }
3013
- // trying to get width via charcode
3014
- width = this.widths[charcode];
3015
- if (width) {
3016
- break; // the non-zero width found
3017
- }
3018
- }
3019
- width = width || this.defaultWidth;
3020
- return shadow(this, "spaceWidth", width);
3021
- }
3022
-
3023
- /**
3024
- * @private
3025
- */
3026
- _charToGlyph(charcode, isSpace = false) {
3027
- let fontCharCode, width, operatorListId;
3028
-
3029
- let widthCode = charcode;
3030
- if (this.cMap && this.cMap.contains(charcode)) {
3031
- widthCode = this.cMap.lookup(charcode);
3032
- }
3033
- width = this.widths[widthCode];
3034
- width = isNum(width) ? width : this.defaultWidth;
3035
- const vmetric = this.vmetrics && this.vmetrics[widthCode];
3036
-
3037
- let unicode = this.toUnicode.get(charcode) || charcode;
3038
- if (typeof unicode === "number") {
3039
- unicode = String.fromCharCode(unicode);
3040
- }
3041
-
3042
- let isInFont = this.toFontChar[charcode] !== undefined;
3043
- // First try the toFontChar map, if it's not there then try falling
3044
- // back to the char code.
3045
- fontCharCode = this.toFontChar[charcode] || charcode;
3046
- if (this.missingFile) {
3047
- const glyphName =
3048
- this.differences[charcode] || this.defaultEncoding[charcode];
3049
- if (
3050
- (glyphName === ".notdef" || glyphName === "") &&
3051
- this.type === "Type1"
3052
- ) {
3053
- // .notdef glyphs should be invisible in non-embedded Type1 fonts, so
3054
- // replace them with spaces.
3055
- fontCharCode = 0x20;
3056
- }
3057
- fontCharCode = mapSpecialUnicodeValues(fontCharCode);
3058
- }
3059
-
3060
- if (this.isType3Font) {
3061
- // Font char code in this case is actually a glyph name.
3062
- operatorListId = fontCharCode;
3063
- }
3064
-
3065
- let accent = null;
3066
- if (this.seacMap && this.seacMap[charcode]) {
3067
- isInFont = true;
3068
- const seac = this.seacMap[charcode];
3069
- fontCharCode = seac.baseFontCharCode;
3070
- accent = {
3071
- fontChar: String.fromCodePoint(seac.accentFontCharCode),
3072
- offset: seac.accentOffset,
3073
- };
3074
- }
3075
-
3076
- let fontChar = "";
3077
- if (typeof fontCharCode === "number") {
3078
- if (fontCharCode <= 0x10ffff) {
3079
- fontChar = String.fromCodePoint(fontCharCode);
3080
- } else {
3081
- warn(`charToGlyph - invalid fontCharCode: ${fontCharCode}`);
3082
- }
3083
- }
3084
-
3085
- let glyph = this._glyphCache[charcode];
3086
- if (
3087
- !glyph ||
3088
- !glyph.matchesForCache(
3089
- charcode,
3090
- fontChar,
3091
- unicode,
3092
- accent,
3093
- width,
3094
- vmetric,
3095
- operatorListId,
3096
- isSpace,
3097
- isInFont
3098
- )
3099
- ) {
3100
- glyph = new Glyph(
3101
- charcode,
3102
- fontChar,
3103
- unicode,
3104
- accent,
3105
- width,
3106
- vmetric,
3107
- operatorListId,
3108
- isSpace,
3109
- isInFont
3110
- );
3111
- this._glyphCache[charcode] = glyph;
3112
- }
3113
- return glyph;
3114
- }
3115
-
3116
- charsToGlyphs(chars) {
3117
- // If we translated this string before, just grab it from the cache.
3118
- let glyphs = this._charsCache[chars];
3119
- if (glyphs) {
3120
- return glyphs;
3121
- }
3122
- glyphs = [];
3123
-
3124
- if (this.cMap) {
3125
- // Composite fonts have multi-byte strings, convert the string from
3126
- // single-byte to multi-byte.
3127
- const c = Object.create(null),
3128
- ii = chars.length;
3129
- let i = 0;
3130
- while (i < ii) {
3131
- this.cMap.readCharCode(chars, i, c);
3132
- const { charcode, length } = c;
3133
- i += length;
3134
- // Space is char with code 0x20 and length 1 in multiple-byte codes.
3135
- const glyph = this._charToGlyph(
3136
- charcode,
3137
- length === 1 && chars.charCodeAt(i - 1) === 0x20
3138
- );
3139
- glyphs.push(glyph);
3140
- }
3141
- } else {
3142
- for (let i = 0, ii = chars.length; i < ii; ++i) {
3143
- const charcode = chars.charCodeAt(i);
3144
- const glyph = this._charToGlyph(charcode, charcode === 0x20);
3145
- glyphs.push(glyph);
3146
- }
3147
- }
3148
-
3149
- // Enter the translated string into the cache.
3150
- return (this._charsCache[chars] = glyphs);
3151
- }
3152
-
3153
- /**
3154
- * Chars can have different sizes (depends on the encoding).
3155
- * @param {String} a string encoded with font encoding.
3156
- * @returns {Array<Array<number>>} the positions of each char in the string.
3157
- */
3158
- getCharPositions(chars) {
3159
- // This function doesn't use a cache because
3160
- // it's called only when saving or printing.
3161
- const positions = [];
3162
-
3163
- if (this.cMap) {
3164
- const c = Object.create(null);
3165
- let i = 0;
3166
- while (i < chars.length) {
3167
- this.cMap.readCharCode(chars, i, c);
3168
- const length = c.length;
3169
- positions.push([i, i + length]);
3170
- i += length;
3171
- }
3172
- } else {
3173
- for (let i = 0, ii = chars.length; i < ii; ++i) {
3174
- positions.push([i, i + 1]);
3175
- }
3176
- }
3177
-
3178
- return positions;
3179
- }
3180
-
3181
- get glyphCacheValues() {
3182
- return Object.values(this._glyphCache);
3183
- }
3184
-
3185
- /**
3186
- * Encode a js string using font encoding.
3187
- * The resulting array contains an encoded string at even positions
3188
- * (can be empty) and a non-encoded one at odd positions.
3189
- * @param {String} a js string.
3190
- * @returns {Array<String>} an array of encoded strings or non-encoded ones.
3191
- */
3192
- encodeString(str) {
3193
- const buffers = [];
3194
- const currentBuf = [];
3195
-
3196
- // buffers will contain: encoded, non-encoded, encoded, ...
3197
- // currentBuf is pushed in buffers each time there is a change.
3198
- // So when buffers.length is odd then the last string is an encoded one
3199
- // and currentBuf contains non-encoded chars.
3200
- const hasCurrentBufErrors = () => buffers.length % 2 === 1;
3201
-
3202
- for (let i = 0, ii = str.length; i < ii; i++) {
3203
- const unicode = str.codePointAt(i);
3204
- if (unicode > 0xd7ff && (unicode < 0xe000 || unicode > 0xfffd)) {
3205
- // unicode is represented by two uint16
3206
- i++;
3207
- }
3208
- if (this.toUnicode) {
3209
- const char = String.fromCodePoint(unicode);
3210
- const charCode = this.toUnicode.charCodeOf(char);
3211
- if (charCode !== -1) {
3212
- if (hasCurrentBufErrors()) {
3213
- buffers.push(currentBuf.join(""));
3214
- currentBuf.length = 0;
3215
- }
3216
- const charCodeLength = this.cMap
3217
- ? this.cMap.getCharCodeLength(charCode)
3218
- : 1;
3219
- for (let j = charCodeLength - 1; j >= 0; j--) {
3220
- currentBuf.push(String.fromCharCode((charCode >> (8 * j)) & 0xff));
3221
- }
3222
- continue;
3223
- }
3224
- }
3225
-
3226
- // unicode can't be encoded
3227
- if (!hasCurrentBufErrors()) {
3228
- buffers.push(currentBuf.join(""));
3229
- currentBuf.length = 0;
3230
- }
3231
- currentBuf.push(String.fromCodePoint(unicode));
3232
- }
3233
-
3234
- buffers.push(currentBuf.join(""));
3235
-
3236
- return buffers;
3237
- }
3238
- }
3239
-
3240
- class ErrorFont {
3241
- constructor(error) {
3242
- this.error = error;
3243
- this.loadedName = "g_font_error";
3244
- this.missingFile = true;
3245
- }
3246
-
3247
- charsToGlyphs() {
3248
- return [];
3249
- }
3250
-
3251
- encodeString(chars) {
3252
- return [chars];
3253
- }
3254
-
3255
- exportData(extraProperties = false) {
3256
- return { error: this.error };
3257
- }
3258
- }
3259
-
3260
- export { ErrorFont, Font };