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

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 (172) hide show
  1. package/README.md +1 -1
  2. package/package.json +42 -26
  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/l10n_utils.js +140 -0
  6. package/{src/shared → pdfjs/dist}/message_handler.js +243 -259
  7. package/{src/display → pdfjs/dist}/network.js +149 -87
  8. package/{src/display/content_disposition.js → pdfjs/dist/network_utils.js} +167 -55
  9. package/{src/display → pdfjs/dist}/node_stream.js +133 -98
  10. package/pdfjs/dist/pdf.js +12778 -0
  11. package/pdfjs/dist/pdf_link_service.js +638 -0
  12. package/pdfjs/dist/pdf_rendering_queue.js +199 -0
  13. package/pdfjs/dist/pdf_thumbnail_viewer.js +819 -0
  14. package/pdfjs/dist/pdf_viewer.js +3598 -0
  15. package/pdfjs/dist/ui_utils.js +1033 -0
  16. package/{src/shared → pdfjs/dist}/util.js +301 -287
  17. package/pdfjs/dist/worker.js +62813 -0
  18. package/src/vcf-pdf-viewer.js +98 -46
  19. package/theme/lumo/vcf-pdf-viewer-styles.js +4 -4
  20. package/theme/material/vcf-pdf-viewer-styles.js +4 -4
  21. package/theme/material/vcf-pdf-viewer.js +2 -2
  22. package/src/core/.eslintrc +0 -13
  23. package/src/core/annotation.js +0 -2948
  24. package/src/core/arithmetic_decoder.js +0 -182
  25. package/src/core/ascii_85_stream.js +0 -98
  26. package/src/core/ascii_hex_stream.js +0 -79
  27. package/src/core/base_stream.js +0 -110
  28. package/src/core/bidi.js +0 -438
  29. package/src/core/calibri_factors.js +0 -308
  30. package/src/core/catalog.js +0 -1459
  31. package/src/core/ccitt.js +0 -1062
  32. package/src/core/ccitt_stream.js +0 -60
  33. package/src/core/cff_font.js +0 -116
  34. package/src/core/cff_parser.js +0 -1949
  35. package/src/core/charsets.js +0 -119
  36. package/src/core/chunked_stream.js +0 -557
  37. package/src/core/cmap.js +0 -1039
  38. package/src/core/colorspace.js +0 -1533
  39. package/src/core/core_utils.js +0 -464
  40. package/src/core/crypto.js +0 -1900
  41. package/src/core/decode_stream.js +0 -170
  42. package/src/core/decrypt_stream.js +0 -59
  43. package/src/core/default_appearance.js +0 -99
  44. package/src/core/document.js +0 -1456
  45. package/src/core/encodings.js +0 -301
  46. package/src/core/evaluator.js +0 -4601
  47. package/src/core/file_spec.js +0 -108
  48. package/src/core/flate_stream.js +0 -402
  49. package/src/core/font_renderer.js +0 -882
  50. package/src/core/fonts.js +0 -3260
  51. package/src/core/fonts_utils.js +0 -221
  52. package/src/core/function.js +0 -1257
  53. package/src/core/glyf.js +0 -706
  54. package/src/core/glyphlist.js +0 -4558
  55. package/src/core/helvetica_factors.js +0 -353
  56. package/src/core/image.js +0 -802
  57. package/src/core/image_utils.js +0 -291
  58. package/src/core/jbig2.js +0 -2572
  59. package/src/core/jbig2_stream.js +0 -73
  60. package/src/core/jpeg_stream.js +0 -105
  61. package/src/core/jpg.js +0 -1416
  62. package/src/core/jpx.js +0 -2343
  63. package/src/core/jpx_stream.js +0 -87
  64. package/src/core/liberationsans_widths.js +0 -221
  65. package/src/core/lzw_stream.js +0 -150
  66. package/src/core/metadata_parser.js +0 -146
  67. package/src/core/metrics.js +0 -2970
  68. package/src/core/murmurhash3.js +0 -139
  69. package/src/core/myriadpro_factors.js +0 -290
  70. package/src/core/name_number_tree.js +0 -153
  71. package/src/core/object_loader.js +0 -149
  72. package/src/core/opentype_file_builder.js +0 -154
  73. package/src/core/operator_list.js +0 -734
  74. package/src/core/parser.js +0 -1416
  75. package/src/core/pattern.js +0 -985
  76. package/src/core/pdf_manager.js +0 -217
  77. package/src/core/predictor_stream.js +0 -238
  78. package/src/core/primitives.js +0 -402
  79. package/src/core/ps_parser.js +0 -272
  80. package/src/core/run_length_stream.js +0 -61
  81. package/src/core/segoeui_factors.js +0 -308
  82. package/src/core/standard_fonts.js +0 -817
  83. package/src/core/stream.js +0 -103
  84. package/src/core/struct_tree.js +0 -335
  85. package/src/core/to_unicode_map.js +0 -103
  86. package/src/core/type1_font.js +0 -421
  87. package/src/core/type1_parser.js +0 -776
  88. package/src/core/unicode.js +0 -1649
  89. package/src/core/worker.js +0 -848
  90. package/src/core/worker_stream.js +0 -135
  91. package/src/core/writer.js +0 -278
  92. package/src/core/xfa/bind.js +0 -652
  93. package/src/core/xfa/builder.js +0 -207
  94. package/src/core/xfa/config.js +0 -1926
  95. package/src/core/xfa/connection_set.js +0 -202
  96. package/src/core/xfa/data.js +0 -82
  97. package/src/core/xfa/datasets.js +0 -76
  98. package/src/core/xfa/factory.js +0 -111
  99. package/src/core/xfa/fonts.js +0 -181
  100. package/src/core/xfa/formcalc_lexer.js +0 -385
  101. package/src/core/xfa/formcalc_parser.js +0 -1340
  102. package/src/core/xfa/html_utils.js +0 -639
  103. package/src/core/xfa/layout.js +0 -383
  104. package/src/core/xfa/locale_set.js +0 -345
  105. package/src/core/xfa/namespaces.js +0 -81
  106. package/src/core/xfa/parser.js +0 -184
  107. package/src/core/xfa/setup.js +0 -38
  108. package/src/core/xfa/signature.js +0 -40
  109. package/src/core/xfa/som.js +0 -338
  110. package/src/core/xfa/stylesheet.js +0 -40
  111. package/src/core/xfa/template.js +0 -6260
  112. package/src/core/xfa/text.js +0 -290
  113. package/src/core/xfa/unknown.js +0 -29
  114. package/src/core/xfa/utils.js +0 -217
  115. package/src/core/xfa/xdp.js +0 -59
  116. package/src/core/xfa/xfa_object.js +0 -1130
  117. package/src/core/xfa/xhtml.js +0 -543
  118. package/src/core/xfa_fonts.js +0 -208
  119. package/src/core/xml_parser.js +0 -507
  120. package/src/core/xref.js +0 -899
  121. package/src/display/annotation_layer.js +0 -2107
  122. package/src/display/annotation_storage.js +0 -113
  123. package/src/display/api.js +0 -3292
  124. package/src/display/base_factory.js +0 -180
  125. package/src/display/canvas.js +0 -2828
  126. package/src/display/font_loader.js +0 -484
  127. package/src/display/metadata.js +0 -41
  128. package/src/display/network_utils.js +0 -100
  129. package/src/display/node_utils.js +0 -83
  130. package/src/display/optional_content_config.js +0 -189
  131. package/src/display/pattern_helper.js +0 -659
  132. package/src/display/svg.js +0 -1709
  133. package/src/display/text_layer.js +0 -847
  134. package/src/display/transport_stream.js +0 -303
  135. package/src/display/worker_options.js +0 -40
  136. package/src/display/xfa_layer.js +0 -204
  137. package/src/doc_helper.js +0 -25
  138. package/src/images/logo.svg +0 -41
  139. package/src/interfaces.js +0 -169
  140. package/src/license_header.js +0 -14
  141. package/src/license_header_libre.js +0 -21
  142. package/src/pdf.image_decoders.js +0 -46
  143. package/src/pdf.js +0 -146
  144. package/src/pdf.sandbox.external.js +0 -181
  145. package/src/pdf.sandbox.js +0 -151
  146. package/src/pdf.scripting.js +0 -25
  147. package/src/pdf.worker.entry.js +0 -19
  148. package/src/pdf.worker.js +0 -23
  149. package/src/scripting_api/aform.js +0 -608
  150. package/src/scripting_api/app.js +0 -621
  151. package/src/scripting_api/color.js +0 -129
  152. package/src/scripting_api/common.js +0 -58
  153. package/src/scripting_api/console.js +0 -38
  154. package/src/scripting_api/constants.js +0 -208
  155. package/src/scripting_api/doc.js +0 -1195
  156. package/src/scripting_api/error.js +0 -23
  157. package/src/scripting_api/event.js +0 -232
  158. package/src/scripting_api/field.js +0 -620
  159. package/src/scripting_api/fullscreen.js +0 -145
  160. package/src/scripting_api/initialization.js +0 -223
  161. package/src/scripting_api/pdf_object.js +0 -24
  162. package/src/scripting_api/print_params.js +0 -146
  163. package/src/scripting_api/proxy.js +0 -139
  164. package/src/scripting_api/thermometer.js +0 -69
  165. package/src/scripting_api/util.js +0 -581
  166. package/src/shared/.eslintrc +0 -13
  167. package/src/shared/cffStandardStrings.js +0 -311
  168. package/src/shared/compatibility.js +0 -114
  169. package/src/shared/fonts_utils.js +0 -429
  170. package/src/shared/is_node.js +0 -27
  171. package/src/shared/scripting_utils.js +0 -85
  172. package/src/worker_loader.js +0 -32
@@ -1,1949 +0,0 @@
1
- /* Copyright 2016 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
- bytesToString,
18
- FormatError,
19
- info,
20
- shadow,
21
- stringToBytes,
22
- Util,
23
- warn,
24
- } from "../shared/util.js";
25
- import {
26
- ExpertCharset,
27
- ExpertSubsetCharset,
28
- ISOAdobeCharset,
29
- } from "./charsets.js";
30
- import { ExpertEncoding, StandardEncoding } from "./encodings.js";
31
-
32
- // Maximum subroutine call depth of type 2 chartrings. Matches OTS.
33
- const MAX_SUBR_NESTING = 10;
34
-
35
- /**
36
- * The CFF class takes a Type1 file and wrap it into a
37
- * 'Compact Font Format' which itself embed Type2 charstrings.
38
- */
39
- // prettier-ignore
40
- const CFFStandardStrings = [
41
- ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent",
42
- "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus",
43
- "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four",
44
- "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less",
45
- "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H",
46
- "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
47
- "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
48
- "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
49
- "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
50
- "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
51
- "sterling", "fraction", "yen", "florin", "section", "currency",
52
- "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft",
53
- "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl",
54
- "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase",
55
- "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown",
56
- "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent",
57
- "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash",
58
- "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae",
59
- "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior",
60
- "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn",
61
- "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters",
62
- "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior",
63
- "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring",
64
- "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
65
- "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
66
- "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
67
- "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
68
- "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde",
69
- "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute",
70
- "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex",
71
- "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex",
72
- "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
73
- "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall",
74
- "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
75
- "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle",
76
- "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
77
- "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior",
78
- "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
79
- "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
80
- "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
81
- "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior",
82
- "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall",
83
- "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall",
84
- "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
85
- "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall",
86
- "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah",
87
- "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall",
88
- "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall",
89
- "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior",
90
- "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth",
91
- "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
92
- "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior",
93
- "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior",
94
- "oneinferior", "twoinferior", "threeinferior", "fourinferior",
95
- "fiveinferior", "sixinferior", "seveninferior", "eightinferior",
96
- "nineinferior", "centinferior", "dollarinferior", "periodinferior",
97
- "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall",
98
- "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
99
- "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall",
100
- "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall",
101
- "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall",
102
- "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall",
103
- "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall",
104
- "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003",
105
- "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold"
106
- ];
107
-
108
- const NUM_STANDARD_CFF_STRINGS = 391;
109
-
110
- const CFFParser = (function CFFParserClosure() {
111
- const CharstringValidationData = [
112
- null,
113
- { id: "hstem", min: 2, stackClearing: true, stem: true },
114
- null,
115
- { id: "vstem", min: 2, stackClearing: true, stem: true },
116
- { id: "vmoveto", min: 1, stackClearing: true },
117
- { id: "rlineto", min: 2, resetStack: true },
118
- { id: "hlineto", min: 1, resetStack: true },
119
- { id: "vlineto", min: 1, resetStack: true },
120
- { id: "rrcurveto", min: 6, resetStack: true },
121
- null,
122
- { id: "callsubr", min: 1, undefStack: true },
123
- { id: "return", min: 0, undefStack: true },
124
- null, // 12
125
- null,
126
- { id: "endchar", min: 0, stackClearing: true },
127
- null,
128
- null,
129
- null,
130
- { id: "hstemhm", min: 2, stackClearing: true, stem: true },
131
- { id: "hintmask", min: 0, stackClearing: true },
132
- { id: "cntrmask", min: 0, stackClearing: true },
133
- { id: "rmoveto", min: 2, stackClearing: true },
134
- { id: "hmoveto", min: 1, stackClearing: true },
135
- { id: "vstemhm", min: 2, stackClearing: true, stem: true },
136
- { id: "rcurveline", min: 8, resetStack: true },
137
- { id: "rlinecurve", min: 8, resetStack: true },
138
- { id: "vvcurveto", min: 4, resetStack: true },
139
- { id: "hhcurveto", min: 4, resetStack: true },
140
- null, // shortint
141
- { id: "callgsubr", min: 1, undefStack: true },
142
- { id: "vhcurveto", min: 4, resetStack: true },
143
- { id: "hvcurveto", min: 4, resetStack: true },
144
- ];
145
- const CharstringValidationData12 = [
146
- null,
147
- null,
148
- null,
149
- { id: "and", min: 2, stackDelta: -1 },
150
- { id: "or", min: 2, stackDelta: -1 },
151
- { id: "not", min: 1, stackDelta: 0 },
152
- null,
153
- null,
154
- null,
155
- { id: "abs", min: 1, stackDelta: 0 },
156
- {
157
- id: "add",
158
- min: 2,
159
- stackDelta: -1,
160
- stackFn: function stack_div(stack, index) {
161
- stack[index - 2] = stack[index - 2] + stack[index - 1];
162
- },
163
- },
164
- {
165
- id: "sub",
166
- min: 2,
167
- stackDelta: -1,
168
- stackFn: function stack_div(stack, index) {
169
- stack[index - 2] = stack[index - 2] - stack[index - 1];
170
- },
171
- },
172
- {
173
- id: "div",
174
- min: 2,
175
- stackDelta: -1,
176
- stackFn: function stack_div(stack, index) {
177
- stack[index - 2] = stack[index - 2] / stack[index - 1];
178
- },
179
- },
180
- null,
181
- {
182
- id: "neg",
183
- min: 1,
184
- stackDelta: 0,
185
- stackFn: function stack_div(stack, index) {
186
- stack[index - 1] = -stack[index - 1];
187
- },
188
- },
189
- { id: "eq", min: 2, stackDelta: -1 },
190
- null,
191
- null,
192
- { id: "drop", min: 1, stackDelta: -1 },
193
- null,
194
- { id: "put", min: 2, stackDelta: -2 },
195
- { id: "get", min: 1, stackDelta: 0 },
196
- { id: "ifelse", min: 4, stackDelta: -3 },
197
- { id: "random", min: 0, stackDelta: 1 },
198
- {
199
- id: "mul",
200
- min: 2,
201
- stackDelta: -1,
202
- stackFn: function stack_div(stack, index) {
203
- stack[index - 2] = stack[index - 2] * stack[index - 1];
204
- },
205
- },
206
- null,
207
- { id: "sqrt", min: 1, stackDelta: 0 },
208
- { id: "dup", min: 1, stackDelta: 1 },
209
- { id: "exch", min: 2, stackDelta: 0 },
210
- { id: "index", min: 2, stackDelta: 0 },
211
- { id: "roll", min: 3, stackDelta: -2 },
212
- null,
213
- null,
214
- null,
215
- { id: "hflex", min: 7, resetStack: true },
216
- { id: "flex", min: 13, resetStack: true },
217
- { id: "hflex1", min: 9, resetStack: true },
218
- { id: "flex1", min: 11, resetStack: true },
219
- ];
220
-
221
- // eslint-disable-next-line no-shadow
222
- class CFFParser {
223
- constructor(file, properties, seacAnalysisEnabled) {
224
- this.bytes = file.getBytes();
225
- this.properties = properties;
226
- this.seacAnalysisEnabled = !!seacAnalysisEnabled;
227
- }
228
-
229
- parse() {
230
- const properties = this.properties;
231
- const cff = new CFF();
232
- this.cff = cff;
233
-
234
- // The first five sections must be in order, all the others are reached
235
- // via offsets contained in one of the below.
236
- const header = this.parseHeader();
237
- const nameIndex = this.parseIndex(header.endPos);
238
- const topDictIndex = this.parseIndex(nameIndex.endPos);
239
- const stringIndex = this.parseIndex(topDictIndex.endPos);
240
- const globalSubrIndex = this.parseIndex(stringIndex.endPos);
241
-
242
- const topDictParsed = this.parseDict(topDictIndex.obj.get(0));
243
- const topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings);
244
-
245
- cff.header = header.obj;
246
- cff.names = this.parseNameIndex(nameIndex.obj);
247
- cff.strings = this.parseStringIndex(stringIndex.obj);
248
- cff.topDict = topDict;
249
- cff.globalSubrIndex = globalSubrIndex.obj;
250
-
251
- this.parsePrivateDict(cff.topDict);
252
-
253
- cff.isCIDFont = topDict.hasName("ROS");
254
-
255
- const charStringOffset = topDict.getByName("CharStrings");
256
- const charStringIndex = this.parseIndex(charStringOffset).obj;
257
-
258
- const fontMatrix = topDict.getByName("FontMatrix");
259
- if (fontMatrix) {
260
- properties.fontMatrix = fontMatrix;
261
- }
262
-
263
- const fontBBox = topDict.getByName("FontBBox");
264
- if (fontBBox) {
265
- // adjusting ascent/descent
266
- properties.ascent = Math.max(fontBBox[3], fontBBox[1]);
267
- properties.descent = Math.min(fontBBox[1], fontBBox[3]);
268
- properties.ascentScaled = true;
269
- }
270
-
271
- let charset, encoding;
272
- if (cff.isCIDFont) {
273
- const fdArrayIndex = this.parseIndex(topDict.getByName("FDArray")).obj;
274
- for (let i = 0, ii = fdArrayIndex.count; i < ii; ++i) {
275
- const dictRaw = fdArrayIndex.get(i);
276
- const fontDict = this.createDict(
277
- CFFTopDict,
278
- this.parseDict(dictRaw),
279
- cff.strings
280
- );
281
- this.parsePrivateDict(fontDict);
282
- cff.fdArray.push(fontDict);
283
- }
284
- // cid fonts don't have an encoding
285
- encoding = null;
286
- charset = this.parseCharsets(
287
- topDict.getByName("charset"),
288
- charStringIndex.count,
289
- cff.strings,
290
- true
291
- );
292
- cff.fdSelect = this.parseFDSelect(
293
- topDict.getByName("FDSelect"),
294
- charStringIndex.count
295
- );
296
- } else {
297
- charset = this.parseCharsets(
298
- topDict.getByName("charset"),
299
- charStringIndex.count,
300
- cff.strings,
301
- false
302
- );
303
- encoding = this.parseEncoding(
304
- topDict.getByName("Encoding"),
305
- properties,
306
- cff.strings,
307
- charset.charset
308
- );
309
- }
310
-
311
- cff.charset = charset;
312
- cff.encoding = encoding;
313
-
314
- const charStringsAndSeacs = this.parseCharStrings({
315
- charStrings: charStringIndex,
316
- localSubrIndex: topDict.privateDict.subrsIndex,
317
- globalSubrIndex: globalSubrIndex.obj,
318
- fdSelect: cff.fdSelect,
319
- fdArray: cff.fdArray,
320
- privateDict: topDict.privateDict,
321
- });
322
- cff.charStrings = charStringsAndSeacs.charStrings;
323
- cff.seacs = charStringsAndSeacs.seacs;
324
- cff.widths = charStringsAndSeacs.widths;
325
-
326
- return cff;
327
- }
328
-
329
- parseHeader() {
330
- let bytes = this.bytes;
331
- const bytesLength = bytes.length;
332
- let offset = 0;
333
-
334
- // Prevent an infinite loop, by checking that the offset is within the
335
- // bounds of the bytes array. Necessary in empty, or invalid, font files.
336
- while (offset < bytesLength && bytes[offset] !== 1) {
337
- ++offset;
338
- }
339
- if (offset >= bytesLength) {
340
- throw new FormatError("Invalid CFF header");
341
- }
342
- if (offset !== 0) {
343
- info("cff data is shifted");
344
- bytes = bytes.subarray(offset);
345
- this.bytes = bytes;
346
- }
347
- const major = bytes[0];
348
- const minor = bytes[1];
349
- const hdrSize = bytes[2];
350
- const offSize = bytes[3];
351
- const header = new CFFHeader(major, minor, hdrSize, offSize);
352
- return { obj: header, endPos: hdrSize };
353
- }
354
-
355
- parseDict(dict) {
356
- let pos = 0;
357
-
358
- function parseOperand() {
359
- let value = dict[pos++];
360
- if (value === 30) {
361
- return parseFloatOperand();
362
- } else if (value === 28) {
363
- value = dict[pos++];
364
- value = ((value << 24) | (dict[pos++] << 16)) >> 16;
365
- return value;
366
- } else if (value === 29) {
367
- value = dict[pos++];
368
- value = (value << 8) | dict[pos++];
369
- value = (value << 8) | dict[pos++];
370
- value = (value << 8) | dict[pos++];
371
- return value;
372
- } else if (value >= 32 && value <= 246) {
373
- return value - 139;
374
- } else if (value >= 247 && value <= 250) {
375
- return (value - 247) * 256 + dict[pos++] + 108;
376
- } else if (value >= 251 && value <= 254) {
377
- return -((value - 251) * 256) - dict[pos++] - 108;
378
- }
379
- warn('CFFParser_parseDict: "' + value + '" is a reserved command.');
380
- return NaN;
381
- }
382
-
383
- function parseFloatOperand() {
384
- let str = "";
385
- const eof = 15;
386
- // prettier-ignore
387
- const lookup = ["0", "1", "2", "3", "4", "5", "6", "7", "8",
388
- "9", ".", "E", "E-", null, "-"];
389
- const length = dict.length;
390
- while (pos < length) {
391
- const b = dict[pos++];
392
- const b1 = b >> 4;
393
- const b2 = b & 15;
394
-
395
- if (b1 === eof) {
396
- break;
397
- }
398
- str += lookup[b1];
399
-
400
- if (b2 === eof) {
401
- break;
402
- }
403
- str += lookup[b2];
404
- }
405
- return parseFloat(str);
406
- }
407
-
408
- let operands = [];
409
- const entries = [];
410
-
411
- pos = 0;
412
- const end = dict.length;
413
- while (pos < end) {
414
- let b = dict[pos];
415
- if (b <= 21) {
416
- if (b === 12) {
417
- b = (b << 8) | dict[++pos];
418
- }
419
- entries.push([b, operands]);
420
- operands = [];
421
- ++pos;
422
- } else {
423
- operands.push(parseOperand());
424
- }
425
- }
426
- return entries;
427
- }
428
-
429
- parseIndex(pos) {
430
- const cffIndex = new CFFIndex();
431
- const bytes = this.bytes;
432
- const count = (bytes[pos++] << 8) | bytes[pos++];
433
- const offsets = [];
434
- let end = pos;
435
- let i, ii;
436
-
437
- if (count !== 0) {
438
- const offsetSize = bytes[pos++];
439
- // add 1 for offset to determine size of last object
440
- const startPos = pos + (count + 1) * offsetSize - 1;
441
-
442
- for (i = 0, ii = count + 1; i < ii; ++i) {
443
- let offset = 0;
444
- for (let j = 0; j < offsetSize; ++j) {
445
- offset <<= 8;
446
- offset += bytes[pos++];
447
- }
448
- offsets.push(startPos + offset);
449
- }
450
- end = offsets[count];
451
- }
452
- for (i = 0, ii = offsets.length - 1; i < ii; ++i) {
453
- const offsetStart = offsets[i];
454
- const offsetEnd = offsets[i + 1];
455
- cffIndex.add(bytes.subarray(offsetStart, offsetEnd));
456
- }
457
- return { obj: cffIndex, endPos: end };
458
- }
459
-
460
- parseNameIndex(index) {
461
- const names = [];
462
- for (let i = 0, ii = index.count; i < ii; ++i) {
463
- const name = index.get(i);
464
- names.push(bytesToString(name));
465
- }
466
- return names;
467
- }
468
-
469
- parseStringIndex(index) {
470
- const strings = new CFFStrings();
471
- for (let i = 0, ii = index.count; i < ii; ++i) {
472
- const data = index.get(i);
473
- strings.add(bytesToString(data));
474
- }
475
- return strings;
476
- }
477
-
478
- createDict(Type, dict, strings) {
479
- const cffDict = new Type(strings);
480
- for (let i = 0, ii = dict.length; i < ii; ++i) {
481
- const pair = dict[i];
482
- const key = pair[0];
483
- const value = pair[1];
484
- cffDict.setByKey(key, value);
485
- }
486
- return cffDict;
487
- }
488
-
489
- parseCharString(state, data, localSubrIndex, globalSubrIndex) {
490
- if (!data || state.callDepth > MAX_SUBR_NESTING) {
491
- return false;
492
- }
493
- let stackSize = state.stackSize;
494
- const stack = state.stack;
495
-
496
- const length = data.length;
497
-
498
- for (let j = 0; j < length; ) {
499
- const value = data[j++];
500
- let validationCommand = null;
501
- if (value === 12) {
502
- const q = data[j++];
503
- if (q === 0) {
504
- // The CFF specification state that the 'dotsection' command
505
- // (12, 0) is deprecated and treated as a no-op, but all Type2
506
- // charstrings processors should support them. Unfortunately
507
- // the font sanitizer don't. As a workaround the sequence (12, 0)
508
- // is replaced by a useless (0, hmoveto).
509
- data[j - 2] = 139;
510
- data[j - 1] = 22;
511
- stackSize = 0;
512
- } else {
513
- validationCommand = CharstringValidationData12[q];
514
- }
515
- } else if (value === 28) {
516
- // number (16 bit)
517
- stack[stackSize] = ((data[j] << 24) | (data[j + 1] << 16)) >> 16;
518
- j += 2;
519
- stackSize++;
520
- } else if (value === 14) {
521
- if (stackSize >= 4) {
522
- stackSize -= 4;
523
- if (this.seacAnalysisEnabled) {
524
- state.seac = stack.slice(stackSize, stackSize + 4);
525
- return false;
526
- }
527
- }
528
- validationCommand = CharstringValidationData[value];
529
- } else if (value >= 32 && value <= 246) {
530
- // number
531
- stack[stackSize] = value - 139;
532
- stackSize++;
533
- } else if (value >= 247 && value <= 254) {
534
- // number (+1 bytes)
535
- stack[stackSize] =
536
- value < 251
537
- ? ((value - 247) << 8) + data[j] + 108
538
- : -((value - 251) << 8) - data[j] - 108;
539
- j++;
540
- stackSize++;
541
- } else if (value === 255) {
542
- // number (32 bit)
543
- stack[stackSize] =
544
- ((data[j] << 24) |
545
- (data[j + 1] << 16) |
546
- (data[j + 2] << 8) |
547
- data[j + 3]) /
548
- 65536;
549
- j += 4;
550
- stackSize++;
551
- } else if (value === 19 || value === 20) {
552
- state.hints += stackSize >> 1;
553
- // skipping right amount of hints flag data
554
- j += (state.hints + 7) >> 3;
555
- stackSize %= 2;
556
- validationCommand = CharstringValidationData[value];
557
- } else if (value === 10 || value === 29) {
558
- let subrsIndex;
559
- if (value === 10) {
560
- subrsIndex = localSubrIndex;
561
- } else {
562
- subrsIndex = globalSubrIndex;
563
- }
564
- if (!subrsIndex) {
565
- validationCommand = CharstringValidationData[value];
566
- warn("Missing subrsIndex for " + validationCommand.id);
567
- return false;
568
- }
569
- let bias = 32768;
570
- if (subrsIndex.count < 1240) {
571
- bias = 107;
572
- } else if (subrsIndex.count < 33900) {
573
- bias = 1131;
574
- }
575
- const subrNumber = stack[--stackSize] + bias;
576
- if (
577
- subrNumber < 0 ||
578
- subrNumber >= subrsIndex.count ||
579
- isNaN(subrNumber)
580
- ) {
581
- validationCommand = CharstringValidationData[value];
582
- warn("Out of bounds subrIndex for " + validationCommand.id);
583
- return false;
584
- }
585
- state.stackSize = stackSize;
586
- state.callDepth++;
587
- const valid = this.parseCharString(
588
- state,
589
- subrsIndex.get(subrNumber),
590
- localSubrIndex,
591
- globalSubrIndex
592
- );
593
- if (!valid) {
594
- return false;
595
- }
596
- state.callDepth--;
597
- stackSize = state.stackSize;
598
- continue;
599
- } else if (value === 11) {
600
- state.stackSize = stackSize;
601
- return true;
602
- } else if (value === 0 && j === data.length) {
603
- // Operator 0 is not used according to the current spec and
604
- // it's the last char and consequently it's likely a terminator.
605
- // So just replace it by endchar command to make OTS happy.
606
- data[j - 1] = 14;
607
- validationCommand = CharstringValidationData[14];
608
- } else {
609
- validationCommand = CharstringValidationData[value];
610
- }
611
- if (validationCommand) {
612
- if (validationCommand.stem) {
613
- state.hints += stackSize >> 1;
614
- if (value === 3 || value === 23) {
615
- // vstem or vstemhm.
616
- state.hasVStems = true;
617
- } else if (state.hasVStems && (value === 1 || value === 18)) {
618
- // Some browsers don't draw glyphs that specify vstems before
619
- // hstems. As a workaround, replace hstem (1) and hstemhm (18)
620
- // with a pointless vstem (3) or vstemhm (23).
621
- warn("CFF stem hints are in wrong order");
622
- data[j - 1] = value === 1 ? 3 : 23;
623
- }
624
- }
625
- if ("min" in validationCommand) {
626
- if (!state.undefStack && stackSize < validationCommand.min) {
627
- warn(
628
- "Not enough parameters for " +
629
- validationCommand.id +
630
- "; actual: " +
631
- stackSize +
632
- ", expected: " +
633
- validationCommand.min
634
- );
635
-
636
- if (stackSize === 0) {
637
- // Just "fix" the outline in replacing command by a endchar:
638
- // it could lead to wrong rendering of some glyphs or not.
639
- // For example, the pdf in #6132 is well-rendered.
640
- data[j - 1] = 14;
641
- return true;
642
- }
643
- return false;
644
- }
645
- }
646
- if (state.firstStackClearing && validationCommand.stackClearing) {
647
- state.firstStackClearing = false;
648
- // the optional character width can be found before the first
649
- // stack-clearing command arguments
650
- stackSize -= validationCommand.min;
651
- if (stackSize >= 2 && validationCommand.stem) {
652
- // there are even amount of arguments for stem commands
653
- stackSize %= 2;
654
- } else if (stackSize > 1) {
655
- warn("Found too many parameters for stack-clearing command");
656
- }
657
- if (stackSize > 0) {
658
- // Width can be any number since its the difference
659
- // from nominalWidthX.
660
- state.width = stack[stackSize - 1];
661
- }
662
- }
663
- if ("stackDelta" in validationCommand) {
664
- if ("stackFn" in validationCommand) {
665
- validationCommand.stackFn(stack, stackSize);
666
- }
667
- stackSize += validationCommand.stackDelta;
668
- } else if (validationCommand.stackClearing) {
669
- stackSize = 0;
670
- } else if (validationCommand.resetStack) {
671
- stackSize = 0;
672
- state.undefStack = false;
673
- } else if (validationCommand.undefStack) {
674
- stackSize = 0;
675
- state.undefStack = true;
676
- state.firstStackClearing = false;
677
- }
678
- }
679
- }
680
- state.stackSize = stackSize;
681
- return true;
682
- }
683
-
684
- parseCharStrings({
685
- charStrings,
686
- localSubrIndex,
687
- globalSubrIndex,
688
- fdSelect,
689
- fdArray,
690
- privateDict,
691
- }) {
692
- const seacs = [];
693
- const widths = [];
694
- const count = charStrings.count;
695
- for (let i = 0; i < count; i++) {
696
- const charstring = charStrings.get(i);
697
- const state = {
698
- callDepth: 0,
699
- stackSize: 0,
700
- stack: [],
701
- undefStack: true,
702
- hints: 0,
703
- firstStackClearing: true,
704
- seac: null,
705
- width: null,
706
- hasVStems: false,
707
- };
708
- let valid = true;
709
- let localSubrToUse = null;
710
- let privateDictToUse = privateDict;
711
- if (fdSelect && fdArray.length) {
712
- const fdIndex = fdSelect.getFDIndex(i);
713
- if (fdIndex === -1) {
714
- warn("Glyph index is not in fd select.");
715
- valid = false;
716
- }
717
- if (fdIndex >= fdArray.length) {
718
- warn("Invalid fd index for glyph index.");
719
- valid = false;
720
- }
721
- if (valid) {
722
- privateDictToUse = fdArray[fdIndex].privateDict;
723
- localSubrToUse = privateDictToUse.subrsIndex;
724
- }
725
- } else if (localSubrIndex) {
726
- localSubrToUse = localSubrIndex;
727
- }
728
- if (valid) {
729
- valid = this.parseCharString(
730
- state,
731
- charstring,
732
- localSubrToUse,
733
- globalSubrIndex
734
- );
735
- }
736
- if (state.width !== null) {
737
- const nominalWidth = privateDictToUse.getByName("nominalWidthX");
738
- widths[i] = nominalWidth + state.width;
739
- } else {
740
- const defaultWidth = privateDictToUse.getByName("defaultWidthX");
741
- widths[i] = defaultWidth;
742
- }
743
- if (state.seac !== null) {
744
- seacs[i] = state.seac;
745
- }
746
- if (!valid) {
747
- // resetting invalid charstring to single 'endchar'
748
- charStrings.set(i, new Uint8Array([14]));
749
- }
750
- }
751
- return { charStrings, seacs, widths };
752
- }
753
-
754
- emptyPrivateDictionary(parentDict) {
755
- const privateDict = this.createDict(
756
- CFFPrivateDict,
757
- [],
758
- parentDict.strings
759
- );
760
- parentDict.setByKey(18, [0, 0]);
761
- parentDict.privateDict = privateDict;
762
- }
763
-
764
- parsePrivateDict(parentDict) {
765
- // no private dict, do nothing
766
- if (!parentDict.hasName("Private")) {
767
- this.emptyPrivateDictionary(parentDict);
768
- return;
769
- }
770
- const privateOffset = parentDict.getByName("Private");
771
- // make sure the params are formatted correctly
772
- if (!Array.isArray(privateOffset) || privateOffset.length !== 2) {
773
- parentDict.removeByName("Private");
774
- return;
775
- }
776
- const size = privateOffset[0];
777
- const offset = privateOffset[1];
778
- // remove empty dicts or ones that refer to invalid location
779
- if (size === 0 || offset >= this.bytes.length) {
780
- this.emptyPrivateDictionary(parentDict);
781
- return;
782
- }
783
-
784
- const privateDictEnd = offset + size;
785
- const dictData = this.bytes.subarray(offset, privateDictEnd);
786
- const dict = this.parseDict(dictData);
787
- const privateDict = this.createDict(
788
- CFFPrivateDict,
789
- dict,
790
- parentDict.strings
791
- );
792
- parentDict.privateDict = privateDict;
793
-
794
- // Parse the Subrs index also since it's relative to the private dict.
795
- if (!privateDict.getByName("Subrs")) {
796
- return;
797
- }
798
- const subrsOffset = privateDict.getByName("Subrs");
799
- const relativeOffset = offset + subrsOffset;
800
- // Validate the offset.
801
- if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
802
- this.emptyPrivateDictionary(parentDict);
803
- return;
804
- }
805
- const subrsIndex = this.parseIndex(relativeOffset);
806
- privateDict.subrsIndex = subrsIndex.obj;
807
- }
808
-
809
- parseCharsets(pos, length, strings, cid) {
810
- if (pos === 0) {
811
- return new CFFCharset(
812
- true,
813
- CFFCharsetPredefinedTypes.ISO_ADOBE,
814
- ISOAdobeCharset
815
- );
816
- } else if (pos === 1) {
817
- return new CFFCharset(
818
- true,
819
- CFFCharsetPredefinedTypes.EXPERT,
820
- ExpertCharset
821
- );
822
- } else if (pos === 2) {
823
- return new CFFCharset(
824
- true,
825
- CFFCharsetPredefinedTypes.EXPERT_SUBSET,
826
- ExpertSubsetCharset
827
- );
828
- }
829
-
830
- const bytes = this.bytes;
831
- const start = pos;
832
- const format = bytes[pos++];
833
- const charset = [cid ? 0 : ".notdef"];
834
- let id, count, i;
835
-
836
- // subtract 1 for the .notdef glyph
837
- length -= 1;
838
-
839
- switch (format) {
840
- case 0:
841
- for (i = 0; i < length; i++) {
842
- id = (bytes[pos++] << 8) | bytes[pos++];
843
- charset.push(cid ? id : strings.get(id));
844
- }
845
- break;
846
- case 1:
847
- while (charset.length <= length) {
848
- id = (bytes[pos++] << 8) | bytes[pos++];
849
- count = bytes[pos++];
850
- for (i = 0; i <= count; i++) {
851
- charset.push(cid ? id++ : strings.get(id++));
852
- }
853
- }
854
- break;
855
- case 2:
856
- while (charset.length <= length) {
857
- id = (bytes[pos++] << 8) | bytes[pos++];
858
- count = (bytes[pos++] << 8) | bytes[pos++];
859
- for (i = 0; i <= count; i++) {
860
- charset.push(cid ? id++ : strings.get(id++));
861
- }
862
- }
863
- break;
864
- default:
865
- throw new FormatError("Unknown charset format");
866
- }
867
- // Raw won't be needed if we actually compile the charset.
868
- const end = pos;
869
- const raw = bytes.subarray(start, end);
870
-
871
- return new CFFCharset(false, format, charset, raw);
872
- }
873
-
874
- parseEncoding(pos, properties, strings, charset) {
875
- const encoding = Object.create(null);
876
- const bytes = this.bytes;
877
- let predefined = false;
878
- let format, i, ii;
879
- let raw = null;
880
-
881
- function readSupplement() {
882
- const supplementsCount = bytes[pos++];
883
- for (i = 0; i < supplementsCount; i++) {
884
- const code = bytes[pos++];
885
- const sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
886
- encoding[code] = charset.indexOf(strings.get(sid));
887
- }
888
- }
889
-
890
- if (pos === 0 || pos === 1) {
891
- predefined = true;
892
- format = pos;
893
- const baseEncoding = pos ? ExpertEncoding : StandardEncoding;
894
- for (i = 0, ii = charset.length; i < ii; i++) {
895
- const index = baseEncoding.indexOf(charset[i]);
896
- if (index !== -1) {
897
- encoding[index] = i;
898
- }
899
- }
900
- } else {
901
- const dataStart = pos;
902
- format = bytes[pos++];
903
- switch (format & 0x7f) {
904
- case 0:
905
- const glyphsCount = bytes[pos++];
906
- for (i = 1; i <= glyphsCount; i++) {
907
- encoding[bytes[pos++]] = i;
908
- }
909
- break;
910
-
911
- case 1:
912
- const rangesCount = bytes[pos++];
913
- let gid = 1;
914
- for (i = 0; i < rangesCount; i++) {
915
- const start = bytes[pos++];
916
- const left = bytes[pos++];
917
- for (let j = start; j <= start + left; j++) {
918
- encoding[j] = gid++;
919
- }
920
- }
921
- break;
922
-
923
- default:
924
- throw new FormatError(`Unknown encoding format: ${format} in CFF`);
925
- }
926
- const dataEnd = pos;
927
- if (format & 0x80) {
928
- // hasSupplement
929
- // The font sanitizer does not support CFF encoding with a
930
- // supplement, since the encoding is not really used to map
931
- // between gid to glyph, let's overwrite what is declared in
932
- // the top dictionary to let the sanitizer think the font use
933
- // StandardEncoding, that's a lie but that's ok.
934
- bytes[dataStart] &= 0x7f;
935
- readSupplement();
936
- }
937
- raw = bytes.subarray(dataStart, dataEnd);
938
- }
939
- format &= 0x7f;
940
- return new CFFEncoding(predefined, format, encoding, raw);
941
- }
942
-
943
- parseFDSelect(pos, length) {
944
- const bytes = this.bytes;
945
- const format = bytes[pos++];
946
- const fdSelect = [];
947
- let i;
948
-
949
- switch (format) {
950
- case 0:
951
- for (i = 0; i < length; ++i) {
952
- const id = bytes[pos++];
953
- fdSelect.push(id);
954
- }
955
- break;
956
- case 3:
957
- const rangesCount = (bytes[pos++] << 8) | bytes[pos++];
958
- for (i = 0; i < rangesCount; ++i) {
959
- let first = (bytes[pos++] << 8) | bytes[pos++];
960
- if (i === 0 && first !== 0) {
961
- warn(
962
- "parseFDSelect: The first range must have a first GID of 0" +
963
- " -- trying to recover."
964
- );
965
- first = 0;
966
- }
967
- const fdIndex = bytes[pos++];
968
- const next = (bytes[pos] << 8) | bytes[pos + 1];
969
- for (let j = first; j < next; ++j) {
970
- fdSelect.push(fdIndex);
971
- }
972
- }
973
- // Advance past the sentinel(next).
974
- pos += 2;
975
- break;
976
- default:
977
- throw new FormatError(`parseFDSelect: Unknown format "${format}".`);
978
- }
979
- if (fdSelect.length !== length) {
980
- throw new FormatError("parseFDSelect: Invalid font data.");
981
- }
982
-
983
- return new CFFFDSelect(format, fdSelect);
984
- }
985
- }
986
- return CFFParser;
987
- })();
988
-
989
- // Compact Font Format
990
- class CFF {
991
- constructor() {
992
- this.header = null;
993
- this.names = [];
994
- this.topDict = null;
995
- this.strings = new CFFStrings();
996
- this.globalSubrIndex = null;
997
-
998
- // The following could really be per font, but since we only have one font
999
- // store them here.
1000
- this.encoding = null;
1001
- this.charset = null;
1002
- this.charStrings = null;
1003
- this.fdArray = [];
1004
- this.fdSelect = null;
1005
-
1006
- this.isCIDFont = false;
1007
- }
1008
-
1009
- duplicateFirstGlyph() {
1010
- // Browsers will not display a glyph at position 0. Typically glyph 0 is
1011
- // notdef, but a number of fonts put a valid glyph there so it must be
1012
- // duplicated and appended.
1013
- if (this.charStrings.count >= 65535) {
1014
- warn("Not enough space in charstrings to duplicate first glyph.");
1015
- return;
1016
- }
1017
- const glyphZero = this.charStrings.get(0);
1018
- this.charStrings.add(glyphZero);
1019
- if (this.isCIDFont) {
1020
- this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]);
1021
- }
1022
- }
1023
-
1024
- hasGlyphId(id) {
1025
- if (id < 0 || id >= this.charStrings.count) {
1026
- return false;
1027
- }
1028
- const glyph = this.charStrings.get(id);
1029
- return glyph.length > 0;
1030
- }
1031
- }
1032
-
1033
- class CFFHeader {
1034
- constructor(major, minor, hdrSize, offSize) {
1035
- this.major = major;
1036
- this.minor = minor;
1037
- this.hdrSize = hdrSize;
1038
- this.offSize = offSize;
1039
- }
1040
- }
1041
-
1042
- class CFFStrings {
1043
- constructor() {
1044
- this.strings = [];
1045
- }
1046
-
1047
- get(index) {
1048
- if (index >= 0 && index <= NUM_STANDARD_CFF_STRINGS - 1) {
1049
- return CFFStandardStrings[index];
1050
- }
1051
- if (index - NUM_STANDARD_CFF_STRINGS <= this.strings.length) {
1052
- return this.strings[index - NUM_STANDARD_CFF_STRINGS];
1053
- }
1054
- return CFFStandardStrings[0];
1055
- }
1056
-
1057
- getSID(str) {
1058
- let index = CFFStandardStrings.indexOf(str);
1059
- if (index !== -1) {
1060
- return index;
1061
- }
1062
- index = this.strings.indexOf(str);
1063
- if (index !== -1) {
1064
- return index + NUM_STANDARD_CFF_STRINGS;
1065
- }
1066
- return -1;
1067
- }
1068
-
1069
- add(value) {
1070
- this.strings.push(value);
1071
- }
1072
-
1073
- get count() {
1074
- return this.strings.length;
1075
- }
1076
- }
1077
-
1078
- class CFFIndex {
1079
- constructor() {
1080
- this.objects = [];
1081
- this.length = 0;
1082
- }
1083
-
1084
- add(data) {
1085
- this.length += data.length;
1086
- this.objects.push(data);
1087
- }
1088
-
1089
- set(index, data) {
1090
- this.length += data.length - this.objects[index].length;
1091
- this.objects[index] = data;
1092
- }
1093
-
1094
- get(index) {
1095
- return this.objects[index];
1096
- }
1097
-
1098
- get count() {
1099
- return this.objects.length;
1100
- }
1101
- }
1102
-
1103
- class CFFDict {
1104
- constructor(tables, strings) {
1105
- this.keyToNameMap = tables.keyToNameMap;
1106
- this.nameToKeyMap = tables.nameToKeyMap;
1107
- this.defaults = tables.defaults;
1108
- this.types = tables.types;
1109
- this.opcodes = tables.opcodes;
1110
- this.order = tables.order;
1111
- this.strings = strings;
1112
- this.values = Object.create(null);
1113
- }
1114
-
1115
- // value should always be an array
1116
- setByKey(key, value) {
1117
- if (!(key in this.keyToNameMap)) {
1118
- return false;
1119
- }
1120
- const valueLength = value.length;
1121
- // ignore empty values
1122
- if (valueLength === 0) {
1123
- return true;
1124
- }
1125
- // Ignore invalid values (fixes bug1068432.pdf and bug1308536.pdf).
1126
- for (let i = 0; i < valueLength; i++) {
1127
- if (isNaN(value[i])) {
1128
- warn('Invalid CFFDict value: "' + value + '" for key "' + key + '".');
1129
- return true;
1130
- }
1131
- }
1132
- const type = this.types[key];
1133
- // remove the array wrapping these types of values
1134
- if (type === "num" || type === "sid" || type === "offset") {
1135
- value = value[0];
1136
- }
1137
- this.values[key] = value;
1138
- return true;
1139
- }
1140
-
1141
- setByName(name, value) {
1142
- if (!(name in this.nameToKeyMap)) {
1143
- throw new FormatError(`Invalid dictionary name "${name}"`);
1144
- }
1145
- this.values[this.nameToKeyMap[name]] = value;
1146
- }
1147
-
1148
- hasName(name) {
1149
- return this.nameToKeyMap[name] in this.values;
1150
- }
1151
-
1152
- getByName(name) {
1153
- if (!(name in this.nameToKeyMap)) {
1154
- throw new FormatError(`Invalid dictionary name ${name}"`);
1155
- }
1156
- const key = this.nameToKeyMap[name];
1157
- if (!(key in this.values)) {
1158
- return this.defaults[key];
1159
- }
1160
- return this.values[key];
1161
- }
1162
-
1163
- removeByName(name) {
1164
- delete this.values[this.nameToKeyMap[name]];
1165
- }
1166
-
1167
- static createTables(layout) {
1168
- const tables = {
1169
- keyToNameMap: {},
1170
- nameToKeyMap: {},
1171
- defaults: {},
1172
- types: {},
1173
- opcodes: {},
1174
- order: [],
1175
- };
1176
- for (let i = 0, ii = layout.length; i < ii; ++i) {
1177
- const entry = layout[i];
1178
- const key = Array.isArray(entry[0])
1179
- ? (entry[0][0] << 8) + entry[0][1]
1180
- : entry[0];
1181
- tables.keyToNameMap[key] = entry[1];
1182
- tables.nameToKeyMap[entry[1]] = key;
1183
- tables.types[key] = entry[2];
1184
- tables.defaults[key] = entry[3];
1185
- tables.opcodes[key] = Array.isArray(entry[0]) ? entry[0] : [entry[0]];
1186
- tables.order.push(key);
1187
- }
1188
- return tables;
1189
- }
1190
- }
1191
-
1192
- const CFFTopDict = (function CFFTopDictClosure() {
1193
- const layout = [
1194
- [[12, 30], "ROS", ["sid", "sid", "num"], null],
1195
- [[12, 20], "SyntheticBase", "num", null],
1196
- [0, "version", "sid", null],
1197
- [1, "Notice", "sid", null],
1198
- [[12, 0], "Copyright", "sid", null],
1199
- [2, "FullName", "sid", null],
1200
- [3, "FamilyName", "sid", null],
1201
- [4, "Weight", "sid", null],
1202
- [[12, 1], "isFixedPitch", "num", 0],
1203
- [[12, 2], "ItalicAngle", "num", 0],
1204
- [[12, 3], "UnderlinePosition", "num", -100],
1205
- [[12, 4], "UnderlineThickness", "num", 50],
1206
- [[12, 5], "PaintType", "num", 0],
1207
- [[12, 6], "CharstringType", "num", 2],
1208
- // prettier-ignore
1209
- [[12, 7], "FontMatrix", ["num", "num", "num", "num", "num", "num"],
1210
- [0.001, 0, 0, 0.001, 0, 0]],
1211
- [13, "UniqueID", "num", null],
1212
- [5, "FontBBox", ["num", "num", "num", "num"], [0, 0, 0, 0]],
1213
- [[12, 8], "StrokeWidth", "num", 0],
1214
- [14, "XUID", "array", null],
1215
- [15, "charset", "offset", 0],
1216
- [16, "Encoding", "offset", 0],
1217
- [17, "CharStrings", "offset", 0],
1218
- [18, "Private", ["offset", "offset"], null],
1219
- [[12, 21], "PostScript", "sid", null],
1220
- [[12, 22], "BaseFontName", "sid", null],
1221
- [[12, 23], "BaseFontBlend", "delta", null],
1222
- [[12, 31], "CIDFontVersion", "num", 0],
1223
- [[12, 32], "CIDFontRevision", "num", 0],
1224
- [[12, 33], "CIDFontType", "num", 0],
1225
- [[12, 34], "CIDCount", "num", 8720],
1226
- [[12, 35], "UIDBase", "num", null],
1227
- // XXX: CID Fonts on DirectWrite 6.1 only seem to work if FDSelect comes
1228
- // before FDArray.
1229
- [[12, 37], "FDSelect", "offset", null],
1230
- [[12, 36], "FDArray", "offset", null],
1231
- [[12, 38], "FontName", "sid", null],
1232
- ];
1233
- let tables = null;
1234
-
1235
- // eslint-disable-next-line no-shadow
1236
- class CFFTopDict extends CFFDict {
1237
- constructor(strings) {
1238
- if (tables === null) {
1239
- tables = CFFDict.createTables(layout);
1240
- }
1241
- super(tables, strings);
1242
- this.privateDict = null;
1243
- }
1244
- }
1245
- return CFFTopDict;
1246
- })();
1247
-
1248
- const CFFPrivateDict = (function CFFPrivateDictClosure() {
1249
- const layout = [
1250
- [6, "BlueValues", "delta", null],
1251
- [7, "OtherBlues", "delta", null],
1252
- [8, "FamilyBlues", "delta", null],
1253
- [9, "FamilyOtherBlues", "delta", null],
1254
- [[12, 9], "BlueScale", "num", 0.039625],
1255
- [[12, 10], "BlueShift", "num", 7],
1256
- [[12, 11], "BlueFuzz", "num", 1],
1257
- [10, "StdHW", "num", null],
1258
- [11, "StdVW", "num", null],
1259
- [[12, 12], "StemSnapH", "delta", null],
1260
- [[12, 13], "StemSnapV", "delta", null],
1261
- [[12, 14], "ForceBold", "num", 0],
1262
- [[12, 17], "LanguageGroup", "num", 0],
1263
- [[12, 18], "ExpansionFactor", "num", 0.06],
1264
- [[12, 19], "initialRandomSeed", "num", 0],
1265
- [20, "defaultWidthX", "num", 0],
1266
- [21, "nominalWidthX", "num", 0],
1267
- [19, "Subrs", "offset", null],
1268
- ];
1269
- let tables = null;
1270
-
1271
- // eslint-disable-next-line no-shadow
1272
- class CFFPrivateDict extends CFFDict {
1273
- constructor(strings) {
1274
- if (tables === null) {
1275
- tables = CFFDict.createTables(layout);
1276
- }
1277
- super(tables, strings);
1278
- this.subrsIndex = null;
1279
- }
1280
- }
1281
- return CFFPrivateDict;
1282
- })();
1283
-
1284
- const CFFCharsetPredefinedTypes = {
1285
- ISO_ADOBE: 0,
1286
- EXPERT: 1,
1287
- EXPERT_SUBSET: 2,
1288
- };
1289
-
1290
- class CFFCharset {
1291
- constructor(predefined, format, charset, raw) {
1292
- this.predefined = predefined;
1293
- this.format = format;
1294
- this.charset = charset;
1295
- this.raw = raw;
1296
- }
1297
- }
1298
-
1299
- class CFFEncoding {
1300
- constructor(predefined, format, encoding, raw) {
1301
- this.predefined = predefined;
1302
- this.format = format;
1303
- this.encoding = encoding;
1304
- this.raw = raw;
1305
- }
1306
- }
1307
-
1308
- class CFFFDSelect {
1309
- constructor(format, fdSelect) {
1310
- this.format = format;
1311
- this.fdSelect = fdSelect;
1312
- }
1313
-
1314
- getFDIndex(glyphIndex) {
1315
- if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) {
1316
- return -1;
1317
- }
1318
- return this.fdSelect[glyphIndex];
1319
- }
1320
- }
1321
-
1322
- // Helper class to keep track of where an offset is within the data and helps
1323
- // filling in that offset once it's known.
1324
- class CFFOffsetTracker {
1325
- constructor() {
1326
- this.offsets = Object.create(null);
1327
- }
1328
-
1329
- isTracking(key) {
1330
- return key in this.offsets;
1331
- }
1332
-
1333
- track(key, location) {
1334
- if (key in this.offsets) {
1335
- throw new FormatError(`Already tracking location of ${key}`);
1336
- }
1337
- this.offsets[key] = location;
1338
- }
1339
-
1340
- offset(value) {
1341
- for (const key in this.offsets) {
1342
- this.offsets[key] += value;
1343
- }
1344
- }
1345
-
1346
- setEntryLocation(key, values, output) {
1347
- if (!(key in this.offsets)) {
1348
- throw new FormatError(`Not tracking location of ${key}`);
1349
- }
1350
- const data = output.data;
1351
- const dataOffset = this.offsets[key];
1352
- const size = 5;
1353
- for (let i = 0, ii = values.length; i < ii; ++i) {
1354
- const offset0 = i * size + dataOffset;
1355
- const offset1 = offset0 + 1;
1356
- const offset2 = offset0 + 2;
1357
- const offset3 = offset0 + 3;
1358
- const offset4 = offset0 + 4;
1359
- // It's easy to screw up offsets so perform this sanity check.
1360
- if (
1361
- data[offset0] !== 0x1d ||
1362
- data[offset1] !== 0 ||
1363
- data[offset2] !== 0 ||
1364
- data[offset3] !== 0 ||
1365
- data[offset4] !== 0
1366
- ) {
1367
- throw new FormatError("writing to an offset that is not empty");
1368
- }
1369
- const value = values[i];
1370
- data[offset0] = 0x1d;
1371
- data[offset1] = (value >> 24) & 0xff;
1372
- data[offset2] = (value >> 16) & 0xff;
1373
- data[offset3] = (value >> 8) & 0xff;
1374
- data[offset4] = value & 0xff;
1375
- }
1376
- }
1377
- }
1378
-
1379
- // Takes a CFF and converts it to the binary representation.
1380
- class CFFCompiler {
1381
- constructor(cff) {
1382
- this.cff = cff;
1383
- }
1384
-
1385
- compile() {
1386
- const cff = this.cff;
1387
- const output = {
1388
- data: [],
1389
- length: 0,
1390
- add: function CFFCompiler_add(data) {
1391
- this.data = this.data.concat(data);
1392
- this.length = this.data.length;
1393
- },
1394
- };
1395
-
1396
- // Compile the five entries that must be in order.
1397
- const header = this.compileHeader(cff.header);
1398
- output.add(header);
1399
-
1400
- const nameIndex = this.compileNameIndex(cff.names);
1401
- output.add(nameIndex);
1402
-
1403
- if (cff.isCIDFont) {
1404
- // The spec is unclear on how font matrices should relate to each other
1405
- // when there is one in the main top dict and the sub top dicts.
1406
- // Windows handles this differently than linux and osx so we have to
1407
- // normalize to work on all.
1408
- // Rules based off of some mailing list discussions:
1409
- // - If main font has a matrix and subfont doesn't, use the main matrix.
1410
- // - If no main font matrix and there is a subfont matrix, use the
1411
- // subfont matrix.
1412
- // - If both have matrices, concat together.
1413
- // - If neither have matrices, use default.
1414
- // To make this work on all platforms we move the top matrix into each
1415
- // sub top dict and concat if necessary.
1416
- if (cff.topDict.hasName("FontMatrix")) {
1417
- const base = cff.topDict.getByName("FontMatrix");
1418
- cff.topDict.removeByName("FontMatrix");
1419
- for (let i = 0, ii = cff.fdArray.length; i < ii; i++) {
1420
- const subDict = cff.fdArray[i];
1421
- let matrix = base.slice(0);
1422
- if (subDict.hasName("FontMatrix")) {
1423
- matrix = Util.transform(matrix, subDict.getByName("FontMatrix"));
1424
- }
1425
- subDict.setByName("FontMatrix", matrix);
1426
- }
1427
- }
1428
- }
1429
-
1430
- const xuid = cff.topDict.getByName("XUID");
1431
- if (xuid && xuid.length > 16) {
1432
- // Length of XUID array must not be greater than 16 (issue #12399).
1433
- cff.topDict.removeByName("XUID");
1434
- }
1435
-
1436
- cff.topDict.setByName("charset", 0);
1437
- let compiled = this.compileTopDicts(
1438
- [cff.topDict],
1439
- output.length,
1440
- cff.isCIDFont
1441
- );
1442
- output.add(compiled.output);
1443
- const topDictTracker = compiled.trackers[0];
1444
-
1445
- const stringIndex = this.compileStringIndex(cff.strings.strings);
1446
- output.add(stringIndex);
1447
-
1448
- const globalSubrIndex = this.compileIndex(cff.globalSubrIndex);
1449
- output.add(globalSubrIndex);
1450
-
1451
- // Now start on the other entries that have no specific order.
1452
- if (cff.encoding && cff.topDict.hasName("Encoding")) {
1453
- if (cff.encoding.predefined) {
1454
- topDictTracker.setEntryLocation(
1455
- "Encoding",
1456
- [cff.encoding.format],
1457
- output
1458
- );
1459
- } else {
1460
- const encoding = this.compileEncoding(cff.encoding);
1461
- topDictTracker.setEntryLocation("Encoding", [output.length], output);
1462
- output.add(encoding);
1463
- }
1464
- }
1465
- const charset = this.compileCharset(
1466
- cff.charset,
1467
- cff.charStrings.count,
1468
- cff.strings,
1469
- cff.isCIDFont
1470
- );
1471
- topDictTracker.setEntryLocation("charset", [output.length], output);
1472
- output.add(charset);
1473
-
1474
- const charStrings = this.compileCharStrings(cff.charStrings);
1475
- topDictTracker.setEntryLocation("CharStrings", [output.length], output);
1476
- output.add(charStrings);
1477
-
1478
- if (cff.isCIDFont) {
1479
- // For some reason FDSelect must be in front of FDArray on windows. OSX
1480
- // and linux don't seem to care.
1481
- topDictTracker.setEntryLocation("FDSelect", [output.length], output);
1482
- const fdSelect = this.compileFDSelect(cff.fdSelect);
1483
- output.add(fdSelect);
1484
- // It is unclear if the sub font dictionary can have CID related
1485
- // dictionary keys, but the sanitizer doesn't like them so remove them.
1486
- compiled = this.compileTopDicts(cff.fdArray, output.length, true);
1487
- topDictTracker.setEntryLocation("FDArray", [output.length], output);
1488
- output.add(compiled.output);
1489
- const fontDictTrackers = compiled.trackers;
1490
-
1491
- this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
1492
- }
1493
-
1494
- this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
1495
-
1496
- // If the font data ends with INDEX whose object data is zero-length,
1497
- // the sanitizer will bail out. Add a dummy byte to avoid that.
1498
- output.add([0]);
1499
-
1500
- return output.data;
1501
- }
1502
-
1503
- encodeNumber(value) {
1504
- if (Number.isInteger(value)) {
1505
- return this.encodeInteger(value);
1506
- }
1507
- return this.encodeFloat(value);
1508
- }
1509
-
1510
- static get EncodeFloatRegExp() {
1511
- return shadow(
1512
- this,
1513
- "EncodeFloatRegExp",
1514
- /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/
1515
- );
1516
- }
1517
-
1518
- encodeFloat(num) {
1519
- let value = num.toString();
1520
-
1521
- // Rounding inaccurate doubles.
1522
- const m = CFFCompiler.EncodeFloatRegExp.exec(value);
1523
- if (m) {
1524
- const epsilon = parseFloat("1e" + ((m[2] ? +m[2] : 0) + m[1].length));
1525
- value = (Math.round(num * epsilon) / epsilon).toString();
1526
- }
1527
-
1528
- let nibbles = "";
1529
- let i, ii;
1530
- for (i = 0, ii = value.length; i < ii; ++i) {
1531
- const a = value[i];
1532
- if (a === "e") {
1533
- nibbles += value[++i] === "-" ? "c" : "b";
1534
- } else if (a === ".") {
1535
- nibbles += "a";
1536
- } else if (a === "-") {
1537
- nibbles += "e";
1538
- } else {
1539
- nibbles += a;
1540
- }
1541
- }
1542
- nibbles += nibbles.length & 1 ? "f" : "ff";
1543
- const out = [30];
1544
- for (i = 0, ii = nibbles.length; i < ii; i += 2) {
1545
- out.push(parseInt(nibbles.substring(i, i + 2), 16));
1546
- }
1547
- return out;
1548
- }
1549
-
1550
- encodeInteger(value) {
1551
- let code;
1552
- if (value >= -107 && value <= 107) {
1553
- code = [value + 139];
1554
- } else if (value >= 108 && value <= 1131) {
1555
- value -= 108;
1556
- code = [(value >> 8) + 247, value & 0xff];
1557
- } else if (value >= -1131 && value <= -108) {
1558
- value = -value - 108;
1559
- code = [(value >> 8) + 251, value & 0xff];
1560
- } else if (value >= -32768 && value <= 32767) {
1561
- code = [0x1c, (value >> 8) & 0xff, value & 0xff];
1562
- } else {
1563
- code = [
1564
- 0x1d,
1565
- (value >> 24) & 0xff,
1566
- (value >> 16) & 0xff,
1567
- (value >> 8) & 0xff,
1568
- value & 0xff,
1569
- ];
1570
- }
1571
- return code;
1572
- }
1573
-
1574
- compileHeader(header) {
1575
- // `header.hdrSize` can be any value but we only write 4 values
1576
- // so header size is 4 (prevents OTS from rejecting the font).
1577
- return [header.major, header.minor, 4, header.offSize];
1578
- }
1579
-
1580
- compileNameIndex(names) {
1581
- const nameIndex = new CFFIndex();
1582
- for (let i = 0, ii = names.length; i < ii; ++i) {
1583
- const name = names[i];
1584
- // OTS doesn't allow names to be over 127 characters.
1585
- const length = Math.min(name.length, 127);
1586
- let sanitizedName = new Array(length);
1587
- for (let j = 0; j < length; j++) {
1588
- // OTS requires chars to be between a range and not certain other
1589
- // chars.
1590
- let char = name[j];
1591
- if (
1592
- char < "!" ||
1593
- char > "~" ||
1594
- char === "[" ||
1595
- char === "]" ||
1596
- char === "(" ||
1597
- char === ")" ||
1598
- char === "{" ||
1599
- char === "}" ||
1600
- char === "<" ||
1601
- char === ">" ||
1602
- char === "/" ||
1603
- char === "%"
1604
- ) {
1605
- char = "_";
1606
- }
1607
- sanitizedName[j] = char;
1608
- }
1609
- sanitizedName = sanitizedName.join("");
1610
-
1611
- if (sanitizedName === "") {
1612
- sanitizedName = "Bad_Font_Name";
1613
- }
1614
- nameIndex.add(stringToBytes(sanitizedName));
1615
- }
1616
- return this.compileIndex(nameIndex);
1617
- }
1618
-
1619
- compileTopDicts(dicts, length, removeCidKeys) {
1620
- const fontDictTrackers = [];
1621
- let fdArrayIndex = new CFFIndex();
1622
- for (let i = 0, ii = dicts.length; i < ii; ++i) {
1623
- const fontDict = dicts[i];
1624
- if (removeCidKeys) {
1625
- fontDict.removeByName("CIDFontVersion");
1626
- fontDict.removeByName("CIDFontRevision");
1627
- fontDict.removeByName("CIDFontType");
1628
- fontDict.removeByName("CIDCount");
1629
- fontDict.removeByName("UIDBase");
1630
- }
1631
- const fontDictTracker = new CFFOffsetTracker();
1632
- const fontDictData = this.compileDict(fontDict, fontDictTracker);
1633
- fontDictTrackers.push(fontDictTracker);
1634
- fdArrayIndex.add(fontDictData);
1635
- fontDictTracker.offset(length);
1636
- }
1637
- fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);
1638
- return {
1639
- trackers: fontDictTrackers,
1640
- output: fdArrayIndex,
1641
- };
1642
- }
1643
-
1644
- compilePrivateDicts(dicts, trackers, output) {
1645
- for (let i = 0, ii = dicts.length; i < ii; ++i) {
1646
- const fontDict = dicts[i];
1647
- const privateDict = fontDict.privateDict;
1648
- if (!privateDict || !fontDict.hasName("Private")) {
1649
- throw new FormatError("There must be a private dictionary.");
1650
- }
1651
- const privateDictTracker = new CFFOffsetTracker();
1652
- const privateDictData = this.compileDict(privateDict, privateDictTracker);
1653
-
1654
- let outputLength = output.length;
1655
- privateDictTracker.offset(outputLength);
1656
- if (!privateDictData.length) {
1657
- // The private dictionary was empty, set the output length to zero to
1658
- // ensure the offset length isn't out of bounds in the eyes of the
1659
- // sanitizer.
1660
- outputLength = 0;
1661
- }
1662
-
1663
- trackers[i].setEntryLocation(
1664
- "Private",
1665
- [privateDictData.length, outputLength],
1666
- output
1667
- );
1668
- output.add(privateDictData);
1669
-
1670
- if (privateDict.subrsIndex && privateDict.hasName("Subrs")) {
1671
- const subrs = this.compileIndex(privateDict.subrsIndex);
1672
- privateDictTracker.setEntryLocation(
1673
- "Subrs",
1674
- [privateDictData.length],
1675
- output
1676
- );
1677
- output.add(subrs);
1678
- }
1679
- }
1680
- }
1681
-
1682
- compileDict(dict, offsetTracker) {
1683
- let out = [];
1684
- // The dictionary keys must be in a certain order.
1685
- const order = dict.order;
1686
- for (let i = 0; i < order.length; ++i) {
1687
- const key = order[i];
1688
- if (!(key in dict.values)) {
1689
- continue;
1690
- }
1691
- let values = dict.values[key];
1692
- let types = dict.types[key];
1693
- if (!Array.isArray(types)) {
1694
- types = [types];
1695
- }
1696
- if (!Array.isArray(values)) {
1697
- values = [values];
1698
- }
1699
-
1700
- // Remove any empty dict values.
1701
- if (values.length === 0) {
1702
- continue;
1703
- }
1704
-
1705
- for (let j = 0, jj = types.length; j < jj; ++j) {
1706
- const type = types[j];
1707
- const value = values[j];
1708
- switch (type) {
1709
- case "num":
1710
- case "sid":
1711
- out = out.concat(this.encodeNumber(value));
1712
- break;
1713
- case "offset":
1714
- // For offsets we just insert a 32bit integer so we don't have to
1715
- // deal with figuring out the length of the offset when it gets
1716
- // replaced later on by the compiler.
1717
- const name = dict.keyToNameMap[key];
1718
- // Some offsets have the offset and the length, so just record the
1719
- // position of the first one.
1720
- if (!offsetTracker.isTracking(name)) {
1721
- offsetTracker.track(name, out.length);
1722
- }
1723
- out = out.concat([0x1d, 0, 0, 0, 0]);
1724
- break;
1725
- case "array":
1726
- case "delta":
1727
- out = out.concat(this.encodeNumber(value));
1728
- for (let k = 1, kk = values.length; k < kk; ++k) {
1729
- out = out.concat(this.encodeNumber(values[k]));
1730
- }
1731
- break;
1732
- default:
1733
- throw new FormatError(`Unknown data type of ${type}`);
1734
- }
1735
- }
1736
- out = out.concat(dict.opcodes[key]);
1737
- }
1738
- return out;
1739
- }
1740
-
1741
- compileStringIndex(strings) {
1742
- const stringIndex = new CFFIndex();
1743
- for (let i = 0, ii = strings.length; i < ii; ++i) {
1744
- stringIndex.add(stringToBytes(strings[i]));
1745
- }
1746
- return this.compileIndex(stringIndex);
1747
- }
1748
-
1749
- compileGlobalSubrIndex() {
1750
- const globalSubrIndex = this.cff.globalSubrIndex;
1751
- this.out.writeByteArray(this.compileIndex(globalSubrIndex));
1752
- }
1753
-
1754
- compileCharStrings(charStrings) {
1755
- const charStringsIndex = new CFFIndex();
1756
- for (let i = 0; i < charStrings.count; i++) {
1757
- const glyph = charStrings.get(i);
1758
- // If the CharString outline is empty, replace it with .notdef to
1759
- // prevent OTS from rejecting the font (fixes bug1252420.pdf).
1760
- if (glyph.length === 0) {
1761
- charStringsIndex.add(new Uint8Array([0x8b, 0x0e]));
1762
- continue;
1763
- }
1764
- charStringsIndex.add(glyph);
1765
- }
1766
- return this.compileIndex(charStringsIndex);
1767
- }
1768
-
1769
- compileCharset(charset, numGlyphs, strings, isCIDFont) {
1770
- // Freetype requires the number of charset strings be correct and MacOS
1771
- // requires a valid mapping for printing.
1772
- let out;
1773
- const numGlyphsLessNotDef = numGlyphs - 1;
1774
- if (isCIDFont) {
1775
- // In a CID font, the charset is a mapping of CIDs not SIDs so just
1776
- // create an identity mapping.
1777
- out = new Uint8Array([
1778
- 2, // format
1779
- 0, // first CID upper byte
1780
- 0, // first CID lower byte
1781
- (numGlyphsLessNotDef >> 8) & 0xff,
1782
- numGlyphsLessNotDef & 0xff,
1783
- ]);
1784
- } else {
1785
- const length = 1 + numGlyphsLessNotDef * 2;
1786
- out = new Uint8Array(length);
1787
- out[0] = 0; // format 0
1788
- let charsetIndex = 0;
1789
- const numCharsets = charset.charset.length;
1790
- let warned = false;
1791
- for (let i = 1; i < out.length; i += 2) {
1792
- let sid = 0;
1793
- if (charsetIndex < numCharsets) {
1794
- const name = charset.charset[charsetIndex++];
1795
- sid = strings.getSID(name);
1796
- if (sid === -1) {
1797
- sid = 0;
1798
- if (!warned) {
1799
- warned = true;
1800
- warn(`Couldn't find ${name} in CFF strings`);
1801
- }
1802
- }
1803
- }
1804
- out[i] = (sid >> 8) & 0xff;
1805
- out[i + 1] = sid & 0xff;
1806
- }
1807
- }
1808
- return this.compileTypedArray(out);
1809
- }
1810
-
1811
- compileEncoding(encoding) {
1812
- return this.compileTypedArray(encoding.raw);
1813
- }
1814
-
1815
- compileFDSelect(fdSelect) {
1816
- const format = fdSelect.format;
1817
- let out, i;
1818
- switch (format) {
1819
- case 0:
1820
- out = new Uint8Array(1 + fdSelect.fdSelect.length);
1821
- out[0] = format;
1822
- for (i = 0; i < fdSelect.fdSelect.length; i++) {
1823
- out[i + 1] = fdSelect.fdSelect[i];
1824
- }
1825
- break;
1826
- case 3:
1827
- const start = 0;
1828
- let lastFD = fdSelect.fdSelect[0];
1829
- const ranges = [
1830
- format,
1831
- 0, // nRanges place holder
1832
- 0, // nRanges place holder
1833
- (start >> 8) & 0xff,
1834
- start & 0xff,
1835
- lastFD,
1836
- ];
1837
- for (i = 1; i < fdSelect.fdSelect.length; i++) {
1838
- const currentFD = fdSelect.fdSelect[i];
1839
- if (currentFD !== lastFD) {
1840
- ranges.push((i >> 8) & 0xff, i & 0xff, currentFD);
1841
- lastFD = currentFD;
1842
- }
1843
- }
1844
- // 3 bytes are pushed for every range and there are 3 header bytes.
1845
- const numRanges = (ranges.length - 3) / 3;
1846
- ranges[1] = (numRanges >> 8) & 0xff;
1847
- ranges[2] = numRanges & 0xff;
1848
- // sentinel
1849
- ranges.push((i >> 8) & 0xff, i & 0xff);
1850
- out = new Uint8Array(ranges);
1851
- break;
1852
- }
1853
- return this.compileTypedArray(out);
1854
- }
1855
-
1856
- compileTypedArray(data) {
1857
- const out = [];
1858
- for (let i = 0, ii = data.length; i < ii; ++i) {
1859
- out[i] = data[i];
1860
- }
1861
- return out;
1862
- }
1863
-
1864
- compileIndex(index, trackers = []) {
1865
- const objects = index.objects;
1866
- // First 2 bytes contains the number of objects contained into this index
1867
- const count = objects.length;
1868
-
1869
- // If there is no object, just create an index. This technically
1870
- // should just be [0, 0] but OTS has an issue with that.
1871
- if (count === 0) {
1872
- return [0, 0, 0];
1873
- }
1874
-
1875
- const data = [(count >> 8) & 0xff, count & 0xff];
1876
-
1877
- let lastOffset = 1,
1878
- i;
1879
- for (i = 0; i < count; ++i) {
1880
- lastOffset += objects[i].length;
1881
- }
1882
-
1883
- let offsetSize;
1884
- if (lastOffset < 0x100) {
1885
- offsetSize = 1;
1886
- } else if (lastOffset < 0x10000) {
1887
- offsetSize = 2;
1888
- } else if (lastOffset < 0x1000000) {
1889
- offsetSize = 3;
1890
- } else {
1891
- offsetSize = 4;
1892
- }
1893
-
1894
- // Next byte contains the offset size use to reference object in the file
1895
- data.push(offsetSize);
1896
-
1897
- // Add another offset after this one because we need a new offset
1898
- let relativeOffset = 1;
1899
- for (i = 0; i < count + 1; i++) {
1900
- if (offsetSize === 1) {
1901
- data.push(relativeOffset & 0xff);
1902
- } else if (offsetSize === 2) {
1903
- data.push((relativeOffset >> 8) & 0xff, relativeOffset & 0xff);
1904
- } else if (offsetSize === 3) {
1905
- data.push(
1906
- (relativeOffset >> 16) & 0xff,
1907
- (relativeOffset >> 8) & 0xff,
1908
- relativeOffset & 0xff
1909
- );
1910
- } else {
1911
- data.push(
1912
- (relativeOffset >>> 24) & 0xff,
1913
- (relativeOffset >> 16) & 0xff,
1914
- (relativeOffset >> 8) & 0xff,
1915
- relativeOffset & 0xff
1916
- );
1917
- }
1918
-
1919
- if (objects[i]) {
1920
- relativeOffset += objects[i].length;
1921
- }
1922
- }
1923
-
1924
- for (i = 0; i < count; i++) {
1925
- // Notify the tracker where the object will be offset in the data.
1926
- if (trackers[i]) {
1927
- trackers[i].offset(data.length);
1928
- }
1929
- for (let j = 0, jj = objects[i].length; j < jj; j++) {
1930
- data.push(objects[i][j]);
1931
- }
1932
- }
1933
- return data;
1934
- }
1935
- }
1936
-
1937
- export {
1938
- CFF,
1939
- CFFCharset,
1940
- CFFCompiler,
1941
- CFFFDSelect,
1942
- CFFHeader,
1943
- CFFIndex,
1944
- CFFParser,
1945
- CFFPrivateDict,
1946
- CFFStandardStrings,
1947
- CFFStrings,
1948
- CFFTopDict,
1949
- };