1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | Object.defineProperty(exports, "__esModule", {
|
25 | value: true
|
26 | });
|
27 | exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = exports.DocStats = void 0;
|
28 | exports.collectActions = collectActions;
|
29 | exports.encodeToXmlString = encodeToXmlString;
|
30 | exports.escapePDFName = escapePDFName;
|
31 | exports.getArrayLookupTableFactory = getArrayLookupTableFactory;
|
32 | exports.getInheritableProperty = getInheritableProperty;
|
33 | exports.getLookupTableFactory = getLookupTableFactory;
|
34 | exports.getNewAnnotationsMap = getNewAnnotationsMap;
|
35 | exports.isWhiteSpace = isWhiteSpace;
|
36 | exports.log2 = log2;
|
37 | exports.numberToString = numberToString;
|
38 | exports.parseXFAPath = parseXFAPath;
|
39 | exports.readInt8 = readInt8;
|
40 | exports.readUint16 = readUint16;
|
41 | exports.readUint32 = readUint32;
|
42 | exports.recoverJsURL = recoverJsURL;
|
43 | exports.toRomanNumerals = toRomanNumerals;
|
44 | exports.validateCSSFont = validateCSSFont;
|
45 |
|
46 | var _util = require("../shared/util.js");
|
47 |
|
48 | var _primitives = require("./primitives.js");
|
49 |
|
50 | var _base_stream = require("./base_stream.js");
|
51 |
|
52 | function getLookupTableFactory(initializer) {
|
53 | let lookup;
|
54 | return function () {
|
55 | if (initializer) {
|
56 | lookup = Object.create(null);
|
57 | initializer(lookup);
|
58 | initializer = null;
|
59 | }
|
60 |
|
61 | return lookup;
|
62 | };
|
63 | }
|
64 |
|
65 | function getArrayLookupTableFactory(initializer) {
|
66 | let lookup;
|
67 | return function () {
|
68 | if (initializer) {
|
69 | let arr = initializer();
|
70 | initializer = null;
|
71 | lookup = Object.create(null);
|
72 |
|
73 | for (let i = 0, ii = arr.length; i < ii; i += 2) {
|
74 | lookup[arr[i]] = arr[i + 1];
|
75 | }
|
76 |
|
77 | arr = null;
|
78 | }
|
79 |
|
80 | return lookup;
|
81 | };
|
82 | }
|
83 |
|
84 | class MissingDataException extends _util.BaseException {
|
85 | constructor(begin, end) {
|
86 | super(`Missing data [${begin}, ${end})`, "MissingDataException");
|
87 | this.begin = begin;
|
88 | this.end = end;
|
89 | }
|
90 |
|
91 | }
|
92 |
|
93 | exports.MissingDataException = MissingDataException;
|
94 |
|
95 | class ParserEOFException extends _util.BaseException {
|
96 | constructor(msg) {
|
97 | super(msg, "ParserEOFException");
|
98 | }
|
99 |
|
100 | }
|
101 |
|
102 | exports.ParserEOFException = ParserEOFException;
|
103 |
|
104 | class XRefEntryException extends _util.BaseException {
|
105 | constructor(msg) {
|
106 | super(msg, "XRefEntryException");
|
107 | }
|
108 |
|
109 | }
|
110 |
|
111 | exports.XRefEntryException = XRefEntryException;
|
112 |
|
113 | class XRefParseException extends _util.BaseException {
|
114 | constructor(msg) {
|
115 | super(msg, "XRefParseException");
|
116 | }
|
117 |
|
118 | }
|
119 |
|
120 | exports.XRefParseException = XRefParseException;
|
121 |
|
122 | class DocStats {
|
123 | constructor(handler) {
|
124 | this._handler = handler;
|
125 | this._streamTypes = new Set();
|
126 | this._fontTypes = new Set();
|
127 | }
|
128 |
|
129 | _send() {
|
130 | const streamTypes = Object.create(null),
|
131 | fontTypes = Object.create(null);
|
132 |
|
133 | for (const type of this._streamTypes) {
|
134 | streamTypes[type] = true;
|
135 | }
|
136 |
|
137 | for (const type of this._fontTypes) {
|
138 | fontTypes[type] = true;
|
139 | }
|
140 |
|
141 | this._handler.send("DocStats", {
|
142 | streamTypes,
|
143 | fontTypes
|
144 | });
|
145 | }
|
146 |
|
147 | addStreamType(type) {
|
148 | if (this._streamTypes.has(type)) {
|
149 | return;
|
150 | }
|
151 |
|
152 | this._streamTypes.add(type);
|
153 |
|
154 | this._send();
|
155 | }
|
156 |
|
157 | addFontType(type) {
|
158 | if (this._fontTypes.has(type)) {
|
159 | return;
|
160 | }
|
161 |
|
162 | this._fontTypes.add(type);
|
163 |
|
164 | this._send();
|
165 | }
|
166 |
|
167 | }
|
168 |
|
169 | exports.DocStats = DocStats;
|
170 |
|
171 | function getInheritableProperty({
|
172 | dict,
|
173 | key,
|
174 | getArray = false,
|
175 | stopWhenFound = true
|
176 | }) {
|
177 | let values;
|
178 | const visited = new _primitives.RefSet();
|
179 |
|
180 | while (dict instanceof _primitives.Dict && !(dict.objId && visited.has(dict.objId))) {
|
181 | if (dict.objId) {
|
182 | visited.put(dict.objId);
|
183 | }
|
184 |
|
185 | const value = getArray ? dict.getArray(key) : dict.get(key);
|
186 |
|
187 | if (value !== undefined) {
|
188 | if (stopWhenFound) {
|
189 | return value;
|
190 | }
|
191 |
|
192 | if (!values) {
|
193 | values = [];
|
194 | }
|
195 |
|
196 | values.push(value);
|
197 | }
|
198 |
|
199 | dict = dict.get("Parent");
|
200 | }
|
201 |
|
202 | return values;
|
203 | }
|
204 |
|
205 | const ROMAN_NUMBER_MAP = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
|
206 |
|
207 | function toRomanNumerals(number, lowerCase = false) {
|
208 | (0, _util.assert)(Number.isInteger(number) && number > 0, "The number should be a positive integer.");
|
209 | const romanBuf = [];
|
210 | let pos;
|
211 |
|
212 | while (number >= 1000) {
|
213 | number -= 1000;
|
214 | romanBuf.push("M");
|
215 | }
|
216 |
|
217 | pos = number / 100 | 0;
|
218 | number %= 100;
|
219 | romanBuf.push(ROMAN_NUMBER_MAP[pos]);
|
220 | pos = number / 10 | 0;
|
221 | number %= 10;
|
222 | romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);
|
223 | romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);
|
224 | const romanStr = romanBuf.join("");
|
225 | return lowerCase ? romanStr.toLowerCase() : romanStr;
|
226 | }
|
227 |
|
228 | function log2(x) {
|
229 | if (x <= 0) {
|
230 | return 0;
|
231 | }
|
232 |
|
233 | return Math.ceil(Math.log2(x));
|
234 | }
|
235 |
|
236 | function readInt8(data, offset) {
|
237 | return data[offset] << 24 >> 24;
|
238 | }
|
239 |
|
240 | function readUint16(data, offset) {
|
241 | return data[offset] << 8 | data[offset + 1];
|
242 | }
|
243 |
|
244 | function readUint32(data, offset) {
|
245 | return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
|
246 | }
|
247 |
|
248 | function isWhiteSpace(ch) {
|
249 | return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
|
250 | }
|
251 |
|
252 | function parseXFAPath(path) {
|
253 | const positionPattern = /(.+)\[(\d+)\]$/;
|
254 | return path.split(".").map(component => {
|
255 | const m = component.match(positionPattern);
|
256 |
|
257 | if (m) {
|
258 | return {
|
259 | name: m[1],
|
260 | pos: parseInt(m[2], 10)
|
261 | };
|
262 | }
|
263 |
|
264 | return {
|
265 | name: component,
|
266 | pos: 0
|
267 | };
|
268 | });
|
269 | }
|
270 |
|
271 | function escapePDFName(str) {
|
272 | const buffer = [];
|
273 | let start = 0;
|
274 |
|
275 | for (let i = 0, ii = str.length; i < ii; i++) {
|
276 | const char = str.charCodeAt(i);
|
277 |
|
278 | if (char < 0x21 || char > 0x7e || char === 0x23 || char === 0x28 || char === 0x29 || char === 0x3c || char === 0x3e || char === 0x5b || char === 0x5d || char === 0x7b || char === 0x7d || char === 0x2f || char === 0x25) {
|
279 | if (start < i) {
|
280 | buffer.push(str.substring(start, i));
|
281 | }
|
282 |
|
283 | buffer.push(`#${char.toString(16)}`);
|
284 | start = i + 1;
|
285 | }
|
286 | }
|
287 |
|
288 | if (buffer.length === 0) {
|
289 | return str;
|
290 | }
|
291 |
|
292 | if (start < str.length) {
|
293 | buffer.push(str.substring(start, str.length));
|
294 | }
|
295 |
|
296 | return buffer.join("");
|
297 | }
|
298 |
|
299 | function _collectJS(entry, xref, list, parents) {
|
300 | if (!entry) {
|
301 | return;
|
302 | }
|
303 |
|
304 | let parent = null;
|
305 |
|
306 | if (entry instanceof _primitives.Ref) {
|
307 | if (parents.has(entry)) {
|
308 | return;
|
309 | }
|
310 |
|
311 | parent = entry;
|
312 | parents.put(parent);
|
313 | entry = xref.fetch(entry);
|
314 | }
|
315 |
|
316 | if (Array.isArray(entry)) {
|
317 | for (const element of entry) {
|
318 | _collectJS(element, xref, list, parents);
|
319 | }
|
320 | } else if (entry instanceof _primitives.Dict) {
|
321 | if ((0, _primitives.isName)(entry.get("S"), "JavaScript")) {
|
322 | const js = entry.get("JS");
|
323 | let code;
|
324 |
|
325 | if (js instanceof _base_stream.BaseStream) {
|
326 | code = js.getString();
|
327 | } else if (typeof js === "string") {
|
328 | code = js;
|
329 | }
|
330 |
|
331 | code = code && (0, _util.stringToPDFString)(code).replace(/\u0000/g, "");
|
332 |
|
333 | if (code) {
|
334 | list.push(code);
|
335 | }
|
336 | }
|
337 |
|
338 | _collectJS(entry.getRaw("Next"), xref, list, parents);
|
339 | }
|
340 |
|
341 | if (parent) {
|
342 | parents.remove(parent);
|
343 | }
|
344 | }
|
345 |
|
346 | function collectActions(xref, dict, eventType) {
|
347 | const actions = Object.create(null);
|
348 | const additionalActionsDicts = getInheritableProperty({
|
349 | dict,
|
350 | key: "AA",
|
351 | stopWhenFound: false
|
352 | });
|
353 |
|
354 | if (additionalActionsDicts) {
|
355 | for (let i = additionalActionsDicts.length - 1; i >= 0; i--) {
|
356 | const additionalActions = additionalActionsDicts[i];
|
357 |
|
358 | if (!(additionalActions instanceof _primitives.Dict)) {
|
359 | continue;
|
360 | }
|
361 |
|
362 | for (const key of additionalActions.getKeys()) {
|
363 | const action = eventType[key];
|
364 |
|
365 | if (!action) {
|
366 | continue;
|
367 | }
|
368 |
|
369 | const actionDict = additionalActions.getRaw(key);
|
370 | const parents = new _primitives.RefSet();
|
371 | const list = [];
|
372 |
|
373 | _collectJS(actionDict, xref, list, parents);
|
374 |
|
375 | if (list.length > 0) {
|
376 | actions[action] = list;
|
377 | }
|
378 | }
|
379 | }
|
380 | }
|
381 |
|
382 | if (dict.has("A")) {
|
383 | const actionDict = dict.get("A");
|
384 | const parents = new _primitives.RefSet();
|
385 | const list = [];
|
386 |
|
387 | _collectJS(actionDict, xref, list, parents);
|
388 |
|
389 | if (list.length > 0) {
|
390 | actions.Action = list;
|
391 | }
|
392 | }
|
393 |
|
394 | return (0, _util.objectSize)(actions) > 0 ? actions : null;
|
395 | }
|
396 |
|
397 | const XMLEntities = {
|
398 | 0x3c: "<",
|
399 | 0x3e: ">",
|
400 | 0x26: "&",
|
401 | 0x22: """,
|
402 | 0x27: "'"
|
403 | };
|
404 |
|
405 | function encodeToXmlString(str) {
|
406 | const buffer = [];
|
407 | let start = 0;
|
408 |
|
409 | for (let i = 0, ii = str.length; i < ii; i++) {
|
410 | const char = str.codePointAt(i);
|
411 |
|
412 | if (0x20 <= char && char <= 0x7e) {
|
413 | const entity = XMLEntities[char];
|
414 |
|
415 | if (entity) {
|
416 | if (start < i) {
|
417 | buffer.push(str.substring(start, i));
|
418 | }
|
419 |
|
420 | buffer.push(entity);
|
421 | start = i + 1;
|
422 | }
|
423 | } else {
|
424 | if (start < i) {
|
425 | buffer.push(str.substring(start, i));
|
426 | }
|
427 |
|
428 | buffer.push(`&#x${char.toString(16).toUpperCase()};`);
|
429 |
|
430 | if (char > 0xd7ff && (char < 0xe000 || char > 0xfffd)) {
|
431 | i++;
|
432 | }
|
433 |
|
434 | start = i + 1;
|
435 | }
|
436 | }
|
437 |
|
438 | if (buffer.length === 0) {
|
439 | return str;
|
440 | }
|
441 |
|
442 | if (start < str.length) {
|
443 | buffer.push(str.substring(start, str.length));
|
444 | }
|
445 |
|
446 | return buffer.join("");
|
447 | }
|
448 |
|
449 | function validateCSSFont(cssFontInfo) {
|
450 | const DEFAULT_CSS_FONT_OBLIQUE = "14";
|
451 | const DEFAULT_CSS_FONT_WEIGHT = "400";
|
452 | const CSS_FONT_WEIGHT_VALUES = new Set(["100", "200", "300", "400", "500", "600", "700", "800", "900", "1000", "normal", "bold", "bolder", "lighter"]);
|
453 | const {
|
454 | fontFamily,
|
455 | fontWeight,
|
456 | italicAngle
|
457 | } = cssFontInfo;
|
458 |
|
459 | if (/^".*"$/.test(fontFamily)) {
|
460 | if (/[^\\]"/.test(fontFamily.slice(1, fontFamily.length - 1))) {
|
461 | (0, _util.warn)(`XFA - FontFamily contains some unescaped ": ${fontFamily}.`);
|
462 | return false;
|
463 | }
|
464 | } else if (/^'.*'$/.test(fontFamily)) {
|
465 | if (/[^\\]'/.test(fontFamily.slice(1, fontFamily.length - 1))) {
|
466 | (0, _util.warn)(`XFA - FontFamily contains some unescaped ': ${fontFamily}.`);
|
467 | return false;
|
468 | }
|
469 | } else {
|
470 | for (const ident of fontFamily.split(/[ \t]+/)) {
|
471 | if (/^(\d|(-(\d|-)))/.test(ident) || !/^[\w-\\]+$/.test(ident)) {
|
472 | (0, _util.warn)(`XFA - FontFamily contains some invalid <custom-ident>: ${fontFamily}.`);
|
473 | return false;
|
474 | }
|
475 | }
|
476 | }
|
477 |
|
478 | const weight = fontWeight ? fontWeight.toString() : "";
|
479 | cssFontInfo.fontWeight = CSS_FONT_WEIGHT_VALUES.has(weight) ? weight : DEFAULT_CSS_FONT_WEIGHT;
|
480 | const angle = parseFloat(italicAngle);
|
481 | cssFontInfo.italicAngle = isNaN(angle) || angle < -90 || angle > 90 ? DEFAULT_CSS_FONT_OBLIQUE : italicAngle.toString();
|
482 | return true;
|
483 | }
|
484 |
|
485 | function recoverJsURL(str) {
|
486 | const URL_OPEN_METHODS = ["app.launchURL", "window.open", "xfa.host.gotoURL"];
|
487 | const regex = new RegExp("^\\s*(" + URL_OPEN_METHODS.join("|").split(".").join("\\.") + ")\\((?:'|\")([^'\"]*)(?:'|\")(?:,\\s*(\\w+)\\)|\\))", "i");
|
488 | const jsUrl = regex.exec(str);
|
489 |
|
490 | if (jsUrl && jsUrl[2]) {
|
491 | const url = jsUrl[2];
|
492 | let newWindow = false;
|
493 |
|
494 | if (jsUrl[3] === "true" && jsUrl[1] === "app.launchURL") {
|
495 | newWindow = true;
|
496 | }
|
497 |
|
498 | return {
|
499 | url,
|
500 | newWindow
|
501 | };
|
502 | }
|
503 |
|
504 | return null;
|
505 | }
|
506 |
|
507 | function numberToString(value) {
|
508 | if (Number.isInteger(value)) {
|
509 | return value.toString();
|
510 | }
|
511 |
|
512 | const roundedValue = Math.round(value * 100);
|
513 |
|
514 | if (roundedValue % 100 === 0) {
|
515 | return (roundedValue / 100).toString();
|
516 | }
|
517 |
|
518 | if (roundedValue % 10 === 0) {
|
519 | return value.toFixed(1);
|
520 | }
|
521 |
|
522 | return value.toFixed(2);
|
523 | }
|
524 |
|
525 | function getNewAnnotationsMap(annotationStorage) {
|
526 | if (!annotationStorage) {
|
527 | return null;
|
528 | }
|
529 |
|
530 | const newAnnotationsByPage = new Map();
|
531 |
|
532 | for (const [key, value] of annotationStorage) {
|
533 | if (!key.startsWith(_util.AnnotationEditorPrefix)) {
|
534 | continue;
|
535 | }
|
536 |
|
537 | let annotations = newAnnotationsByPage.get(value.pageIndex);
|
538 |
|
539 | if (!annotations) {
|
540 | annotations = [];
|
541 | newAnnotationsByPage.set(value.pageIndex, annotations);
|
542 | }
|
543 |
|
544 | annotations.push(value);
|
545 | }
|
546 |
|
547 | return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;
|
548 | } |
\ | No newline at end of file |