@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
@@ -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
- };