UNPKG

96.9 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * JavaScript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.MarkupAnnotation = exports.AnnotationFactory = exports.AnnotationBorderStyle = exports.Annotation = void 0;
28exports.getQuadPoints = getQuadPoints;
29
30var _util = require("../shared/util.js");
31
32var _core_utils = require("./core_utils.js");
33
34var _default_appearance = require("./default_appearance.js");
35
36var _primitives = require("./primitives.js");
37
38var _writer = require("./writer.js");
39
40var _base_stream = require("./base_stream.js");
41
42var _bidi = require("./bidi.js");
43
44var _catalog = require("./catalog.js");
45
46var _colorspace = require("./colorspace.js");
47
48var _file_spec = require("./file_spec.js");
49
50var _object_loader = require("./object_loader.js");
51
52var _operator_list = require("./operator_list.js");
53
54var _stream = require("./stream.js");
55
56var _factory = require("./xfa/factory.js");
57
58class AnnotationFactory {
59 static create(xref, ref, pdfManager, idFactory, collectFields) {
60 return Promise.all([pdfManager.ensureCatalog("acroForm"), pdfManager.ensureCatalog("baseUrl"), pdfManager.ensureDoc("xfaDatasets"), collectFields ? this._getPageIndex(xref, ref, pdfManager) : -1]).then(([acroForm, baseUrl, xfaDatasets, pageIndex]) => pdfManager.ensure(this, "_create", [xref, ref, pdfManager, idFactory, acroForm, xfaDatasets, collectFields, pageIndex]));
61 }
62
63 static _create(xref, ref, pdfManager, idFactory, acroForm, xfaDatasets, collectFields, pageIndex = -1) {
64 const dict = xref.fetchIfRef(ref);
65
66 if (!(dict instanceof _primitives.Dict)) {
67 return undefined;
68 }
69
70 const id = ref instanceof _primitives.Ref ? ref.toString() : `annot_${idFactory.createObjId()}`;
71 let subtype = dict.get("Subtype");
72 subtype = subtype instanceof _primitives.Name ? subtype.name : null;
73 const parameters = {
74 xref,
75 ref,
76 dict,
77 subtype,
78 id,
79 pdfManager,
80 acroForm: acroForm instanceof _primitives.Dict ? acroForm : _primitives.Dict.empty,
81 xfaDatasets,
82 collectFields,
83 pageIndex
84 };
85
86 switch (subtype) {
87 case "Link":
88 return new LinkAnnotation(parameters);
89
90 case "Text":
91 return new TextAnnotation(parameters);
92
93 case "Widget":
94 let fieldType = (0, _core_utils.getInheritableProperty)({
95 dict,
96 key: "FT"
97 });
98 fieldType = fieldType instanceof _primitives.Name ? fieldType.name : null;
99
100 switch (fieldType) {
101 case "Tx":
102 return new TextWidgetAnnotation(parameters);
103
104 case "Btn":
105 return new ButtonWidgetAnnotation(parameters);
106
107 case "Ch":
108 return new ChoiceWidgetAnnotation(parameters);
109
110 case "Sig":
111 return new SignatureWidgetAnnotation(parameters);
112 }
113
114 (0, _util.warn)(`Unimplemented widget field type "${fieldType}", ` + "falling back to base field type.");
115 return new WidgetAnnotation(parameters);
116
117 case "Popup":
118 return new PopupAnnotation(parameters);
119
120 case "FreeText":
121 return new FreeTextAnnotation(parameters);
122
123 case "Line":
124 return new LineAnnotation(parameters);
125
126 case "Square":
127 return new SquareAnnotation(parameters);
128
129 case "Circle":
130 return new CircleAnnotation(parameters);
131
132 case "PolyLine":
133 return new PolylineAnnotation(parameters);
134
135 case "Polygon":
136 return new PolygonAnnotation(parameters);
137
138 case "Caret":
139 return new CaretAnnotation(parameters);
140
141 case "Ink":
142 return new InkAnnotation(parameters);
143
144 case "Highlight":
145 return new HighlightAnnotation(parameters);
146
147 case "Underline":
148 return new UnderlineAnnotation(parameters);
149
150 case "Squiggly":
151 return new SquigglyAnnotation(parameters);
152
153 case "StrikeOut":
154 return new StrikeOutAnnotation(parameters);
155
156 case "Stamp":
157 return new StampAnnotation(parameters);
158
159 case "FileAttachment":
160 return new FileAttachmentAnnotation(parameters);
161
162 default:
163 if (!collectFields) {
164 if (!subtype) {
165 (0, _util.warn)("Annotation is missing the required /Subtype.");
166 } else {
167 (0, _util.warn)(`Unimplemented annotation type "${subtype}", ` + "falling back to base annotation.");
168 }
169 }
170
171 return new Annotation(parameters);
172 }
173 }
174
175 static async _getPageIndex(xref, ref, pdfManager) {
176 try {
177 const annotDict = await xref.fetchIfRefAsync(ref);
178
179 if (!(annotDict instanceof _primitives.Dict)) {
180 return -1;
181 }
182
183 const pageRef = annotDict.getRaw("P");
184
185 if (!(pageRef instanceof _primitives.Ref)) {
186 return -1;
187 }
188
189 const pageIndex = await pdfManager.ensureCatalog("getPageIndex", [pageRef]);
190 return pageIndex;
191 } catch (ex) {
192 (0, _util.warn)(`_getPageIndex: "${ex}".`);
193 return -1;
194 }
195 }
196
197 static async saveNewAnnotations(evaluator, task, annotations) {
198 const xref = evaluator.xref;
199 let baseFontRef;
200 const dependencies = [];
201 const promises = [];
202
203 for (const annotation of annotations) {
204 switch (annotation.annotationType) {
205 case _util.AnnotationEditorType.FREETEXT:
206 if (!baseFontRef) {
207 const baseFont = new _primitives.Dict(xref);
208 baseFont.set("BaseFont", _primitives.Name.get("Helvetica"));
209 baseFont.set("Type", _primitives.Name.get("Font"));
210 baseFont.set("Subtype", _primitives.Name.get("Type1"));
211 baseFont.set("Encoding", _primitives.Name.get("WinAnsiEncoding"));
212 const buffer = [];
213 baseFontRef = xref.getNewRef();
214 (0, _writer.writeObject)(baseFontRef, baseFont, buffer, null);
215 dependencies.push({
216 ref: baseFontRef,
217 data: buffer.join("")
218 });
219 }
220
221 promises.push(FreeTextAnnotation.createNewAnnotation(xref, annotation, dependencies, {
222 evaluator,
223 task,
224 baseFontRef
225 }));
226 break;
227
228 case _util.AnnotationEditorType.INK:
229 promises.push(InkAnnotation.createNewAnnotation(xref, annotation, dependencies));
230 }
231 }
232
233 return {
234 annotations: await Promise.all(promises),
235 dependencies
236 };
237 }
238
239 static async printNewAnnotations(evaluator, task, annotations) {
240 if (!annotations) {
241 return null;
242 }
243
244 const xref = evaluator.xref;
245 const promises = [];
246
247 for (const annotation of annotations) {
248 switch (annotation.annotationType) {
249 case _util.AnnotationEditorType.FREETEXT:
250 promises.push(FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
251 evaluator,
252 task
253 }));
254 break;
255
256 case _util.AnnotationEditorType.INK:
257 promises.push(InkAnnotation.createNewPrintAnnotation(xref, annotation));
258 break;
259 }
260 }
261
262 return Promise.all(promises);
263 }
264
265}
266
267exports.AnnotationFactory = AnnotationFactory;
268
269function getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) {
270 if (!Array.isArray(color)) {
271 return defaultColor;
272 }
273
274 const rgbColor = defaultColor || new Uint8ClampedArray(3);
275
276 switch (color.length) {
277 case 0:
278 return null;
279
280 case 1:
281 _colorspace.ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);
282
283 return rgbColor;
284
285 case 3:
286 _colorspace.ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);
287
288 return rgbColor;
289
290 case 4:
291 _colorspace.ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);
292
293 return rgbColor;
294
295 default:
296 return defaultColor;
297 }
298}
299
300function getQuadPoints(dict, rect) {
301 if (!dict.has("QuadPoints")) {
302 return null;
303 }
304
305 const quadPoints = dict.getArray("QuadPoints");
306
307 if (!Array.isArray(quadPoints) || quadPoints.length === 0 || quadPoints.length % 8 > 0) {
308 return null;
309 }
310
311 const quadPointsLists = [];
312
313 for (let i = 0, ii = quadPoints.length / 8; i < ii; i++) {
314 quadPointsLists.push([]);
315
316 for (let j = i * 8, jj = i * 8 + 8; j < jj; j += 2) {
317 const x = quadPoints[j];
318 const y = quadPoints[j + 1];
319
320 if (rect !== null && (x < rect[0] || x > rect[2] || y < rect[1] || y > rect[3])) {
321 return null;
322 }
323
324 quadPointsLists[i].push({
325 x,
326 y
327 });
328 }
329 }
330
331 return quadPointsLists.map(quadPointsList => {
332 const [minX, maxX, minY, maxY] = quadPointsList.reduce(([mX, MX, mY, MY], quadPoint) => [Math.min(mX, quadPoint.x), Math.max(MX, quadPoint.x), Math.min(mY, quadPoint.y), Math.max(MY, quadPoint.y)], [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]);
333 return [{
334 x: minX,
335 y: maxY
336 }, {
337 x: maxX,
338 y: maxY
339 }, {
340 x: minX,
341 y: minY
342 }, {
343 x: maxX,
344 y: minY
345 }];
346 });
347}
348
349function getTransformMatrix(rect, bbox, matrix) {
350 const [minX, minY, maxX, maxY] = _util.Util.getAxialAlignedBoundingBox(bbox, matrix);
351
352 if (minX === maxX || minY === maxY) {
353 return [1, 0, 0, 1, rect[0], rect[1]];
354 }
355
356 const xRatio = (rect[2] - rect[0]) / (maxX - minX);
357 const yRatio = (rect[3] - rect[1]) / (maxY - minY);
358 return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio];
359}
360
361class Annotation {
362 constructor(params) {
363 const dict = params.dict;
364 this.setTitle(dict.get("T"));
365 this.setContents(dict.get("Contents"));
366 this.setModificationDate(dict.get("M"));
367 this.setFlags(dict.get("F"));
368 this.setRectangle(dict.getArray("Rect"));
369 this.setColor(dict.getArray("C"));
370 this.setBorderStyle(dict);
371 this.setAppearance(dict);
372 this.setOptionalContent(dict);
373 const MK = dict.get("MK");
374 this.setBorderAndBackgroundColors(MK);
375 this.setRotation(MK);
376 this._streams = [];
377
378 if (this.appearance) {
379 this._streams.push(this.appearance);
380 }
381
382 this.data = {
383 annotationFlags: this.flags,
384 borderStyle: this.borderStyle,
385 color: this.color,
386 backgroundColor: this.backgroundColor,
387 borderColor: this.borderColor,
388 rotation: this.rotation,
389 contentsObj: this._contents,
390 hasAppearance: !!this.appearance,
391 id: params.id,
392 modificationDate: this.modificationDate,
393 rect: this.rectangle,
394 subtype: params.subtype,
395 hasOwnCanvas: false
396 };
397
398 if (params.collectFields) {
399 const kids = dict.get("Kids");
400
401 if (Array.isArray(kids)) {
402 const kidIds = [];
403
404 for (const kid of kids) {
405 if (kid instanceof _primitives.Ref) {
406 kidIds.push(kid.toString());
407 }
408 }
409
410 if (kidIds.length !== 0) {
411 this.data.kidIds = kidIds;
412 }
413 }
414
415 this.data.actions = (0, _core_utils.collectActions)(params.xref, dict, _util.AnnotationActionEventType);
416 this.data.fieldName = this._constructFieldName(dict);
417 this.data.pageIndex = params.pageIndex;
418 }
419
420 this._fallbackFontDict = null;
421 }
422
423 _hasFlag(flags, flag) {
424 return !!(flags & flag);
425 }
426
427 _isViewable(flags) {
428 return !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.NOVIEW);
429 }
430
431 _isPrintable(flags) {
432 return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE);
433 }
434
435 mustBeViewed(annotationStorage) {
436 const storageEntry = annotationStorage && annotationStorage.get(this.data.id);
437
438 if (storageEntry && storageEntry.hidden !== undefined) {
439 return !storageEntry.hidden;
440 }
441
442 return this.viewable && !this._hasFlag(this.flags, _util.AnnotationFlag.HIDDEN);
443 }
444
445 mustBePrinted(annotationStorage) {
446 const storageEntry = annotationStorage && annotationStorage.get(this.data.id);
447
448 if (storageEntry && storageEntry.print !== undefined) {
449 return storageEntry.print;
450 }
451
452 return this.printable;
453 }
454
455 get viewable() {
456 if (this.data.quadPoints === null) {
457 return false;
458 }
459
460 if (this.flags === 0) {
461 return true;
462 }
463
464 return this._isViewable(this.flags);
465 }
466
467 get printable() {
468 if (this.data.quadPoints === null) {
469 return false;
470 }
471
472 if (this.flags === 0) {
473 return false;
474 }
475
476 return this._isPrintable(this.flags);
477 }
478
479 _parseStringHelper(data) {
480 const str = typeof data === "string" ? (0, _util.stringToPDFString)(data) : "";
481 const dir = str && (0, _bidi.bidi)(str).dir === "rtl" ? "rtl" : "ltr";
482 return {
483 str,
484 dir
485 };
486 }
487
488 setTitle(title) {
489 this._title = this._parseStringHelper(title);
490 }
491
492 setContents(contents) {
493 this._contents = this._parseStringHelper(contents);
494 }
495
496 setModificationDate(modificationDate) {
497 this.modificationDate = typeof modificationDate === "string" ? modificationDate : null;
498 }
499
500 setFlags(flags) {
501 this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0;
502 }
503
504 hasFlag(flag) {
505 return this._hasFlag(this.flags, flag);
506 }
507
508 setRectangle(rectangle) {
509 if (Array.isArray(rectangle) && rectangle.length === 4) {
510 this.rectangle = _util.Util.normalizeRect(rectangle);
511 } else {
512 this.rectangle = [0, 0, 0, 0];
513 }
514 }
515
516 setColor(color) {
517 this.color = getRgbColor(color);
518 }
519
520 setLineEndings(lineEndings) {
521 this.lineEndings = ["None", "None"];
522
523 if (Array.isArray(lineEndings) && lineEndings.length === 2) {
524 for (let i = 0; i < 2; i++) {
525 const obj = lineEndings[i];
526
527 if (obj instanceof _primitives.Name) {
528 switch (obj.name) {
529 case "None":
530 continue;
531
532 case "Square":
533 case "Circle":
534 case "Diamond":
535 case "OpenArrow":
536 case "ClosedArrow":
537 case "Butt":
538 case "ROpenArrow":
539 case "RClosedArrow":
540 case "Slash":
541 this.lineEndings[i] = obj.name;
542 continue;
543 }
544 }
545
546 (0, _util.warn)(`Ignoring invalid lineEnding: ${obj}`);
547 }
548 }
549 }
550
551 setRotation(mk) {
552 this.rotation = 0;
553
554 if (mk instanceof _primitives.Dict) {
555 let angle = mk.get("R") || 0;
556
557 if (Number.isInteger(angle) && angle !== 0) {
558 angle %= 360;
559
560 if (angle < 0) {
561 angle += 360;
562 }
563
564 if (angle % 90 === 0) {
565 this.rotation = angle;
566 }
567 }
568 }
569 }
570
571 setBorderAndBackgroundColors(mk) {
572 if (mk instanceof _primitives.Dict) {
573 this.borderColor = getRgbColor(mk.getArray("BC"), null);
574 this.backgroundColor = getRgbColor(mk.getArray("BG"), null);
575 } else {
576 this.borderColor = this.backgroundColor = null;
577 }
578 }
579
580 setBorderStyle(borderStyle) {
581 this.borderStyle = new AnnotationBorderStyle();
582
583 if (!(borderStyle instanceof _primitives.Dict)) {
584 return;
585 }
586
587 if (borderStyle.has("BS")) {
588 const dict = borderStyle.get("BS");
589 const dictType = dict.get("Type");
590
591 if (!dictType || (0, _primitives.isName)(dictType, "Border")) {
592 this.borderStyle.setWidth(dict.get("W"), this.rectangle);
593 this.borderStyle.setStyle(dict.get("S"));
594 this.borderStyle.setDashArray(dict.getArray("D"));
595 }
596 } else if (borderStyle.has("Border")) {
597 const array = borderStyle.getArray("Border");
598
599 if (Array.isArray(array) && array.length >= 3) {
600 this.borderStyle.setHorizontalCornerRadius(array[0]);
601 this.borderStyle.setVerticalCornerRadius(array[1]);
602 this.borderStyle.setWidth(array[2], this.rectangle);
603
604 if (array.length === 4) {
605 this.borderStyle.setDashArray(array[3], true);
606 }
607 }
608 } else {
609 this.borderStyle.setWidth(0);
610 }
611 }
612
613 setAppearance(dict) {
614 this.appearance = null;
615 const appearanceStates = dict.get("AP");
616
617 if (!(appearanceStates instanceof _primitives.Dict)) {
618 return;
619 }
620
621 const normalAppearanceState = appearanceStates.get("N");
622
623 if (normalAppearanceState instanceof _base_stream.BaseStream) {
624 this.appearance = normalAppearanceState;
625 return;
626 }
627
628 if (!(normalAppearanceState instanceof _primitives.Dict)) {
629 return;
630 }
631
632 const as = dict.get("AS");
633
634 if (!(as instanceof _primitives.Name) || !normalAppearanceState.has(as.name)) {
635 return;
636 }
637
638 this.appearance = normalAppearanceState.get(as.name);
639 }
640
641 setOptionalContent(dict) {
642 this.oc = null;
643 const oc = dict.get("OC");
644
645 if (oc instanceof _primitives.Name) {
646 (0, _util.warn)("setOptionalContent: Support for /Name-entry is not implemented.");
647 } else if (oc instanceof _primitives.Dict) {
648 this.oc = oc;
649 }
650 }
651
652 loadResources(keys, appearance) {
653 return appearance.dict.getAsync("Resources").then(resources => {
654 if (!resources) {
655 return undefined;
656 }
657
658 const objectLoader = new _object_loader.ObjectLoader(resources, keys, resources.xref);
659 return objectLoader.load().then(function () {
660 return resources;
661 });
662 });
663 }
664
665 async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {
666 const data = this.data;
667 let appearance = this.appearance;
668 const isUsingOwnCanvas = !!(this.data.hasOwnCanvas && intent & _util.RenderingIntentFlag.DISPLAY);
669
670 if (!appearance) {
671 if (!isUsingOwnCanvas) {
672 return {
673 opList: new _operator_list.OperatorList(),
674 separateForm: false,
675 separateCanvas: false
676 };
677 }
678
679 appearance = new _stream.StringStream("");
680 appearance.dict = new _primitives.Dict();
681 }
682
683 const appearanceDict = appearance.dict;
684 const resources = await this.loadResources(["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], appearance);
685 const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1];
686 const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0];
687 const transform = getTransformMatrix(data.rect, bbox, matrix);
688 const opList = new _operator_list.OperatorList();
689 let optionalContent;
690
691 if (this.oc) {
692 optionalContent = await evaluator.parseMarkedContentProps(this.oc, null);
693 }
694
695 if (optionalContent !== undefined) {
696 opList.addOp(_util.OPS.beginMarkedContentProps, ["OC", optionalContent]);
697 }
698
699 opList.addOp(_util.OPS.beginAnnotation, [data.id, data.rect, transform, matrix, isUsingOwnCanvas]);
700 await evaluator.getOperatorList({
701 stream: appearance,
702 task,
703 resources,
704 operatorList: opList,
705 fallbackFontDict: this._fallbackFontDict
706 });
707 opList.addOp(_util.OPS.endAnnotation, []);
708
709 if (optionalContent !== undefined) {
710 opList.addOp(_util.OPS.endMarkedContent, []);
711 }
712
713 this.reset();
714 return {
715 opList,
716 separateForm: false,
717 separateCanvas: isUsingOwnCanvas
718 };
719 }
720
721 async save(evaluator, task, annotationStorage) {
722 return null;
723 }
724
725 getFieldObject() {
726 if (this.data.kidIds) {
727 return {
728 id: this.data.id,
729 actions: this.data.actions,
730 name: this.data.fieldName,
731 strokeColor: this.data.borderColor,
732 fillColor: this.data.backgroundColor,
733 type: "",
734 kidIds: this.data.kidIds,
735 page: this.data.pageIndex,
736 rotation: this.rotation
737 };
738 }
739
740 return null;
741 }
742
743 reset() {
744 for (const stream of this._streams) {
745 stream.reset();
746 }
747 }
748
749 _constructFieldName(dict) {
750 if (!dict.has("T") && !dict.has("Parent")) {
751 (0, _util.warn)("Unknown field name, falling back to empty field name.");
752 return "";
753 }
754
755 if (!dict.has("Parent")) {
756 return (0, _util.stringToPDFString)(dict.get("T"));
757 }
758
759 const fieldName = [];
760
761 if (dict.has("T")) {
762 fieldName.unshift((0, _util.stringToPDFString)(dict.get("T")));
763 }
764
765 let loopDict = dict;
766 const visited = new _primitives.RefSet();
767
768 if (dict.objId) {
769 visited.put(dict.objId);
770 }
771
772 while (loopDict.has("Parent")) {
773 loopDict = loopDict.get("Parent");
774
775 if (!(loopDict instanceof _primitives.Dict) || loopDict.objId && visited.has(loopDict.objId)) {
776 break;
777 }
778
779 if (loopDict.objId) {
780 visited.put(loopDict.objId);
781 }
782
783 if (loopDict.has("T")) {
784 fieldName.unshift((0, _util.stringToPDFString)(loopDict.get("T")));
785 }
786 }
787
788 return fieldName.join(".");
789 }
790
791}
792
793exports.Annotation = Annotation;
794
795class AnnotationBorderStyle {
796 constructor() {
797 this.width = 1;
798 this.style = _util.AnnotationBorderStyleType.SOLID;
799 this.dashArray = [3];
800 this.horizontalCornerRadius = 0;
801 this.verticalCornerRadius = 0;
802 }
803
804 setWidth(width, rect = [0, 0, 0, 0]) {
805 if (width instanceof _primitives.Name) {
806 this.width = 0;
807 return;
808 }
809
810 if (typeof width === "number") {
811 if (width > 0) {
812 const maxWidth = (rect[2] - rect[0]) / 2;
813 const maxHeight = (rect[3] - rect[1]) / 2;
814
815 if (maxWidth > 0 && maxHeight > 0 && (width > maxWidth || width > maxHeight)) {
816 (0, _util.warn)(`AnnotationBorderStyle.setWidth - ignoring width: ${width}`);
817 width = 1;
818 }
819 }
820
821 this.width = width;
822 }
823 }
824
825 setStyle(style) {
826 if (!(style instanceof _primitives.Name)) {
827 return;
828 }
829
830 switch (style.name) {
831 case "S":
832 this.style = _util.AnnotationBorderStyleType.SOLID;
833 break;
834
835 case "D":
836 this.style = _util.AnnotationBorderStyleType.DASHED;
837 break;
838
839 case "B":
840 this.style = _util.AnnotationBorderStyleType.BEVELED;
841 break;
842
843 case "I":
844 this.style = _util.AnnotationBorderStyleType.INSET;
845 break;
846
847 case "U":
848 this.style = _util.AnnotationBorderStyleType.UNDERLINE;
849 break;
850
851 default:
852 break;
853 }
854 }
855
856 setDashArray(dashArray, forceStyle = false) {
857 if (Array.isArray(dashArray) && dashArray.length > 0) {
858 let isValid = true;
859 let allZeros = true;
860
861 for (const element of dashArray) {
862 const validNumber = +element >= 0;
863
864 if (!validNumber) {
865 isValid = false;
866 break;
867 } else if (element > 0) {
868 allZeros = false;
869 }
870 }
871
872 if (isValid && !allZeros) {
873 this.dashArray = dashArray;
874
875 if (forceStyle) {
876 this.setStyle(_primitives.Name.get("D"));
877 }
878 } else {
879 this.width = 0;
880 }
881 } else if (dashArray) {
882 this.width = 0;
883 }
884 }
885
886 setHorizontalCornerRadius(radius) {
887 if (Number.isInteger(radius)) {
888 this.horizontalCornerRadius = radius;
889 }
890 }
891
892 setVerticalCornerRadius(radius) {
893 if (Number.isInteger(radius)) {
894 this.verticalCornerRadius = radius;
895 }
896 }
897
898}
899
900exports.AnnotationBorderStyle = AnnotationBorderStyle;
901
902class MarkupAnnotation extends Annotation {
903 constructor(parameters) {
904 super(parameters);
905 const dict = parameters.dict;
906
907 if (dict.has("IRT")) {
908 const rawIRT = dict.getRaw("IRT");
909 this.data.inReplyTo = rawIRT instanceof _primitives.Ref ? rawIRT.toString() : null;
910 const rt = dict.get("RT");
911 this.data.replyType = rt instanceof _primitives.Name ? rt.name : _util.AnnotationReplyType.REPLY;
912 }
913
914 if (this.data.replyType === _util.AnnotationReplyType.GROUP) {
915 const parent = dict.get("IRT");
916 this.setTitle(parent.get("T"));
917 this.data.titleObj = this._title;
918 this.setContents(parent.get("Contents"));
919 this.data.contentsObj = this._contents;
920
921 if (!parent.has("CreationDate")) {
922 this.data.creationDate = null;
923 } else {
924 this.setCreationDate(parent.get("CreationDate"));
925 this.data.creationDate = this.creationDate;
926 }
927
928 if (!parent.has("M")) {
929 this.data.modificationDate = null;
930 } else {
931 this.setModificationDate(parent.get("M"));
932 this.data.modificationDate = this.modificationDate;
933 }
934
935 this.data.hasPopup = parent.has("Popup");
936
937 if (!parent.has("C")) {
938 this.data.color = null;
939 } else {
940 this.setColor(parent.getArray("C"));
941 this.data.color = this.color;
942 }
943 } else {
944 this.data.titleObj = this._title;
945 this.setCreationDate(dict.get("CreationDate"));
946 this.data.creationDate = this.creationDate;
947 this.data.hasPopup = dict.has("Popup");
948
949 if (!dict.has("C")) {
950 this.data.color = null;
951 }
952 }
953
954 if (dict.has("RC")) {
955 this.data.richText = _factory.XFAFactory.getRichTextAsHtml(dict.get("RC"));
956 }
957 }
958
959 setCreationDate(creationDate) {
960 this.creationDate = typeof creationDate === "string" ? creationDate : null;
961 }
962
963 _setDefaultAppearance({
964 xref,
965 extra,
966 strokeColor,
967 fillColor,
968 blendMode,
969 strokeAlpha,
970 fillAlpha,
971 pointsCallback
972 }) {
973 let minX = Number.MAX_VALUE;
974 let minY = Number.MAX_VALUE;
975 let maxX = Number.MIN_VALUE;
976 let maxY = Number.MIN_VALUE;
977 const buffer = ["q"];
978
979 if (extra) {
980 buffer.push(extra);
981 }
982
983 if (strokeColor) {
984 buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`);
985 }
986
987 if (fillColor) {
988 buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
989 }
990
991 let pointsArray = this.data.quadPoints;
992
993 if (!pointsArray) {
994 pointsArray = [[{
995 x: this.rectangle[0],
996 y: this.rectangle[3]
997 }, {
998 x: this.rectangle[2],
999 y: this.rectangle[3]
1000 }, {
1001 x: this.rectangle[0],
1002 y: this.rectangle[1]
1003 }, {
1004 x: this.rectangle[2],
1005 y: this.rectangle[1]
1006 }]];
1007 }
1008
1009 for (const points of pointsArray) {
1010 const [mX, MX, mY, MY] = pointsCallback(buffer, points);
1011 minX = Math.min(minX, mX);
1012 maxX = Math.max(maxX, MX);
1013 minY = Math.min(minY, mY);
1014 maxY = Math.max(maxY, MY);
1015 }
1016
1017 buffer.push("Q");
1018 const formDict = new _primitives.Dict(xref);
1019 const appearanceStreamDict = new _primitives.Dict(xref);
1020 appearanceStreamDict.set("Subtype", _primitives.Name.get("Form"));
1021 const appearanceStream = new _stream.StringStream(buffer.join(" "));
1022 appearanceStream.dict = appearanceStreamDict;
1023 formDict.set("Fm0", appearanceStream);
1024 const gsDict = new _primitives.Dict(xref);
1025
1026 if (blendMode) {
1027 gsDict.set("BM", _primitives.Name.get(blendMode));
1028 }
1029
1030 if (typeof strokeAlpha === "number") {
1031 gsDict.set("CA", strokeAlpha);
1032 }
1033
1034 if (typeof fillAlpha === "number") {
1035 gsDict.set("ca", fillAlpha);
1036 }
1037
1038 const stateDict = new _primitives.Dict(xref);
1039 stateDict.set("GS0", gsDict);
1040 const resources = new _primitives.Dict(xref);
1041 resources.set("ExtGState", stateDict);
1042 resources.set("XObject", formDict);
1043 const appearanceDict = new _primitives.Dict(xref);
1044 appearanceDict.set("Resources", resources);
1045 const bbox = this.data.rect = [minX, minY, maxX, maxY];
1046 appearanceDict.set("BBox", bbox);
1047 this.appearance = new _stream.StringStream("/GS0 gs /Fm0 Do");
1048 this.appearance.dict = appearanceDict;
1049
1050 this._streams.push(this.appearance, appearanceStream);
1051 }
1052
1053 static async createNewAnnotation(xref, annotation, dependencies, params) {
1054 const annotationRef = xref.getNewRef();
1055 const apRef = xref.getNewRef();
1056 const annotationDict = this.createNewDict(annotation, xref, {
1057 apRef
1058 });
1059 const ap = await this.createNewAppearanceStream(annotation, xref, params);
1060 const buffer = [];
1061 let transform = xref.encrypt ? xref.encrypt.createCipherTransform(apRef.num, apRef.gen) : null;
1062 (0, _writer.writeObject)(apRef, ap, buffer, transform);
1063 dependencies.push({
1064 ref: apRef,
1065 data: buffer.join("")
1066 });
1067 buffer.length = 0;
1068 transform = xref.encrypt ? xref.encrypt.createCipherTransform(annotationRef.num, annotationRef.gen) : null;
1069 (0, _writer.writeObject)(annotationRef, annotationDict, buffer, transform);
1070 return {
1071 ref: annotationRef,
1072 data: buffer.join("")
1073 };
1074 }
1075
1076 static async createNewPrintAnnotation(xref, annotation, params) {
1077 const ap = await this.createNewAppearanceStream(annotation, xref, params);
1078 const annotationDict = this.createNewDict(annotation, xref, {
1079 ap
1080 });
1081 return new this.prototype.constructor({
1082 dict: annotationDict,
1083 xref
1084 });
1085 }
1086
1087}
1088
1089exports.MarkupAnnotation = MarkupAnnotation;
1090
1091class WidgetAnnotation extends Annotation {
1092 constructor(params) {
1093 super(params);
1094 const dict = params.dict;
1095 const data = this.data;
1096 this.ref = params.ref;
1097 data.annotationType = _util.AnnotationType.WIDGET;
1098
1099 if (data.fieldName === undefined) {
1100 data.fieldName = this._constructFieldName(dict);
1101 }
1102
1103 if (data.actions === undefined) {
1104 data.actions = (0, _core_utils.collectActions)(params.xref, dict, _util.AnnotationActionEventType);
1105 }
1106
1107 let fieldValue = (0, _core_utils.getInheritableProperty)({
1108 dict,
1109 key: "V",
1110 getArray: true
1111 });
1112 data.fieldValue = this._decodeFormValue(fieldValue);
1113 const defaultFieldValue = (0, _core_utils.getInheritableProperty)({
1114 dict,
1115 key: "DV",
1116 getArray: true
1117 });
1118 data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);
1119
1120 if (fieldValue === undefined && params.xfaDatasets) {
1121 const path = this._title.str;
1122
1123 if (path) {
1124 this._hasValueFromXFA = true;
1125 data.fieldValue = fieldValue = params.xfaDatasets.getValue(path);
1126 }
1127 }
1128
1129 if (fieldValue === undefined && data.defaultFieldValue !== null) {
1130 data.fieldValue = data.defaultFieldValue;
1131 }
1132
1133 data.alternativeText = (0, _util.stringToPDFString)(dict.get("TU") || "");
1134 const defaultAppearance = (0, _core_utils.getInheritableProperty)({
1135 dict,
1136 key: "DA"
1137 }) || params.acroForm.get("DA");
1138 this._defaultAppearance = typeof defaultAppearance === "string" ? defaultAppearance : "";
1139 data.defaultAppearanceData = (0, _default_appearance.parseDefaultAppearance)(this._defaultAppearance);
1140 const fieldType = (0, _core_utils.getInheritableProperty)({
1141 dict,
1142 key: "FT"
1143 });
1144 data.fieldType = fieldType instanceof _primitives.Name ? fieldType.name : null;
1145 const localResources = (0, _core_utils.getInheritableProperty)({
1146 dict,
1147 key: "DR"
1148 });
1149 const acroFormResources = params.acroForm.get("DR");
1150 const appearanceResources = this.appearance && this.appearance.dict.get("Resources");
1151 this._fieldResources = {
1152 localResources,
1153 acroFormResources,
1154 appearanceResources,
1155 mergedResources: _primitives.Dict.merge({
1156 xref: params.xref,
1157 dictArray: [localResources, appearanceResources, acroFormResources],
1158 mergeSubDicts: true
1159 })
1160 };
1161 data.fieldFlags = (0, _core_utils.getInheritableProperty)({
1162 dict,
1163 key: "Ff"
1164 });
1165
1166 if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {
1167 data.fieldFlags = 0;
1168 }
1169
1170 data.readOnly = this.hasFieldFlag(_util.AnnotationFieldFlag.READONLY);
1171 data.required = this.hasFieldFlag(_util.AnnotationFieldFlag.REQUIRED);
1172 data.hidden = this._hasFlag(data.annotationFlags, _util.AnnotationFlag.HIDDEN);
1173 }
1174
1175 _decodeFormValue(formValue) {
1176 if (Array.isArray(formValue)) {
1177 return formValue.filter(item => typeof item === "string").map(item => (0, _util.stringToPDFString)(item));
1178 } else if (formValue instanceof _primitives.Name) {
1179 return (0, _util.stringToPDFString)(formValue.name);
1180 } else if (typeof formValue === "string") {
1181 return (0, _util.stringToPDFString)(formValue);
1182 }
1183
1184 return null;
1185 }
1186
1187 hasFieldFlag(flag) {
1188 return !!(this.data.fieldFlags & flag);
1189 }
1190
1191 static _getRotationMatrix(rotation, width, height) {
1192 switch (rotation) {
1193 case 90:
1194 return [0, 1, -1, 0, width, 0];
1195
1196 case 180:
1197 return [-1, 0, 0, -1, width, height];
1198
1199 case 270:
1200 return [0, -1, 1, 0, 0, height];
1201
1202 default:
1203 throw new Error("Invalid rotation");
1204 }
1205 }
1206
1207 getRotationMatrix(annotationStorage) {
1208 const storageEntry = annotationStorage ? annotationStorage.get(this.data.id) : undefined;
1209 let rotation = storageEntry && storageEntry.rotation;
1210
1211 if (rotation === undefined) {
1212 rotation = this.rotation;
1213 }
1214
1215 if (rotation === 0) {
1216 return _util.IDENTITY_MATRIX;
1217 }
1218
1219 const width = this.data.rect[2] - this.data.rect[0];
1220 const height = this.data.rect[3] - this.data.rect[1];
1221 return WidgetAnnotation._getRotationMatrix(rotation, width, height);
1222 }
1223
1224 getBorderAndBackgroundAppearances(annotationStorage) {
1225 const storageEntry = annotationStorage ? annotationStorage.get(this.data.id) : undefined;
1226 let rotation = storageEntry && storageEntry.rotation;
1227
1228 if (rotation === undefined) {
1229 rotation = this.rotation;
1230 }
1231
1232 if (!this.backgroundColor && !this.borderColor) {
1233 return "";
1234 }
1235
1236 const width = this.data.rect[2] - this.data.rect[0];
1237 const height = this.data.rect[3] - this.data.rect[1];
1238 const rect = rotation === 0 || rotation === 180 ? `0 0 ${width} ${height} re` : `0 0 ${height} ${width} re`;
1239 let str = "";
1240
1241 if (this.backgroundColor) {
1242 str = `${(0, _default_appearance.getPdfColor)(this.backgroundColor, true)} ${rect} f `;
1243 }
1244
1245 if (this.borderColor) {
1246 const borderWidth = this.borderStyle.width || 1;
1247 str += `${borderWidth} w ${(0, _default_appearance.getPdfColor)(this.borderColor, false)} ${rect} S `;
1248 }
1249
1250 return str;
1251 }
1252
1253 async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {
1254 if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
1255 return {
1256 opList: new _operator_list.OperatorList(),
1257 separateForm: true,
1258 separateCanvas: false
1259 };
1260 }
1261
1262 if (!this._hasText) {
1263 return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
1264 }
1265
1266 const content = await this._getAppearance(evaluator, task, annotationStorage);
1267
1268 if (this.appearance && content === null) {
1269 return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
1270 }
1271
1272 const opList = new _operator_list.OperatorList();
1273
1274 if (!this._defaultAppearance || content === null) {
1275 return {
1276 opList,
1277 separateForm: false,
1278 separateCanvas: false
1279 };
1280 }
1281
1282 const matrix = [1, 0, 0, 1, 0, 0];
1283 const bbox = [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]];
1284 const transform = getTransformMatrix(this.data.rect, bbox, matrix);
1285 let optionalContent;
1286
1287 if (this.oc) {
1288 optionalContent = await evaluator.parseMarkedContentProps(this.oc, null);
1289 }
1290
1291 if (optionalContent !== undefined) {
1292 opList.addOp(_util.OPS.beginMarkedContentProps, ["OC", optionalContent]);
1293 }
1294
1295 opList.addOp(_util.OPS.beginAnnotation, [this.data.id, this.data.rect, transform, this.getRotationMatrix(annotationStorage), false]);
1296 const stream = new _stream.StringStream(content);
1297 await evaluator.getOperatorList({
1298 stream,
1299 task,
1300 resources: this._fieldResources.mergedResources,
1301 operatorList: opList
1302 });
1303 opList.addOp(_util.OPS.endAnnotation, []);
1304
1305 if (optionalContent !== undefined) {
1306 opList.addOp(_util.OPS.endMarkedContent, []);
1307 }
1308
1309 return {
1310 opList,
1311 separateForm: false,
1312 separateCanvas: false
1313 };
1314 }
1315
1316 _getMKDict(rotation) {
1317 const mk = new _primitives.Dict(null);
1318
1319 if (rotation) {
1320 mk.set("R", rotation);
1321 }
1322
1323 if (this.borderColor) {
1324 mk.set("BC", Array.from(this.borderColor).map(c => c / 255));
1325 }
1326
1327 if (this.backgroundColor) {
1328 mk.set("BG", Array.from(this.backgroundColor).map(c => c / 255));
1329 }
1330
1331 return mk.size > 0 ? mk : null;
1332 }
1333
1334 async save(evaluator, task, annotationStorage) {
1335 const storageEntry = annotationStorage ? annotationStorage.get(this.data.id) : undefined;
1336 let value = storageEntry && storageEntry.value;
1337 let rotation = storageEntry && storageEntry.rotation;
1338
1339 if (value === this.data.fieldValue || value === undefined) {
1340 if (!this._hasValueFromXFA && rotation === undefined) {
1341 return null;
1342 }
1343
1344 value = value || this.data.fieldValue;
1345 }
1346
1347 if (rotation === undefined && !this._hasValueFromXFA && Array.isArray(value) && Array.isArray(this.data.fieldValue) && value.length === this.data.fieldValue.length && value.every((x, i) => x === this.data.fieldValue[i])) {
1348 return null;
1349 }
1350
1351 if (rotation === undefined) {
1352 rotation = this.rotation;
1353 }
1354
1355 let appearance = await this._getAppearance(evaluator, task, annotationStorage);
1356
1357 if (appearance === null) {
1358 return null;
1359 }
1360
1361 const {
1362 xref
1363 } = evaluator;
1364 const dict = xref.fetchIfRef(this.ref);
1365
1366 if (!(dict instanceof _primitives.Dict)) {
1367 return null;
1368 }
1369
1370 const bbox = [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]];
1371 const xfa = {
1372 path: (0, _util.stringToPDFString)(dict.get("T") || ""),
1373 value
1374 };
1375 const newRef = xref.getNewRef();
1376 const AP = new _primitives.Dict(xref);
1377 AP.set("N", newRef);
1378 const encrypt = xref.encrypt;
1379 let originalTransform = null;
1380 let newTransform = null;
1381
1382 if (encrypt) {
1383 originalTransform = encrypt.createCipherTransform(this.ref.num, this.ref.gen);
1384 newTransform = encrypt.createCipherTransform(newRef.num, newRef.gen);
1385 appearance = newTransform.encryptString(appearance);
1386 }
1387
1388 const encoder = val => (0, _util.isAscii)(val) ? val : (0, _util.stringToUTF16BEString)(val);
1389
1390 dict.set("V", Array.isArray(value) ? value.map(encoder) : encoder(value));
1391 dict.set("AP", AP);
1392 dict.set("M", `D:${(0, _util.getModificationDate)()}`);
1393
1394 const maybeMK = this._getMKDict(rotation);
1395
1396 if (maybeMK) {
1397 dict.set("MK", maybeMK);
1398 }
1399
1400 const appearanceDict = new _primitives.Dict(xref);
1401 appearanceDict.set("Length", appearance.length);
1402 appearanceDict.set("Subtype", _primitives.Name.get("Form"));
1403 appearanceDict.set("Resources", this._getSaveFieldResources(xref));
1404 appearanceDict.set("BBox", bbox);
1405 const rotationMatrix = this.getRotationMatrix(annotationStorage);
1406
1407 if (rotationMatrix !== _util.IDENTITY_MATRIX) {
1408 appearanceDict.set("Matrix", rotationMatrix);
1409 }
1410
1411 const bufferOriginal = [`${this.ref.num} ${this.ref.gen} obj\n`];
1412 (0, _writer.writeDict)(dict, bufferOriginal, originalTransform);
1413 bufferOriginal.push("\nendobj\n");
1414 const bufferNew = [`${newRef.num} ${newRef.gen} obj\n`];
1415 (0, _writer.writeDict)(appearanceDict, bufferNew, newTransform);
1416 bufferNew.push(" stream\n", appearance, "\nendstream\nendobj\n");
1417 return [{
1418 ref: this.ref,
1419 data: bufferOriginal.join(""),
1420 xfa
1421 }, {
1422 ref: newRef,
1423 data: bufferNew.join(""),
1424 xfa: null
1425 }];
1426 }
1427
1428 async _getAppearance(evaluator, task, annotationStorage) {
1429 const isPassword = this.hasFieldFlag(_util.AnnotationFieldFlag.PASSWORD);
1430
1431 if (isPassword) {
1432 return null;
1433 }
1434
1435 const storageEntry = annotationStorage ? annotationStorage.get(this.data.id) : undefined;
1436 let value, rotation;
1437
1438 if (storageEntry) {
1439 value = storageEntry.formattedValue || storageEntry.value;
1440 rotation = storageEntry.rotation;
1441 }
1442
1443 if (rotation === undefined && value === undefined) {
1444 if (!this._hasValueFromXFA || this.appearance) {
1445 return null;
1446 }
1447 }
1448
1449 if (value === undefined) {
1450 value = this.data.fieldValue;
1451
1452 if (!value) {
1453 return "";
1454 }
1455 }
1456
1457 if (Array.isArray(value) && value.length === 1) {
1458 value = value[0];
1459 }
1460
1461 (0, _util.assert)(typeof value === "string", "Expected `value` to be a string.");
1462 value = value.trim();
1463
1464 if (value === "") {
1465 return "";
1466 }
1467
1468 if (rotation === undefined) {
1469 rotation = this.rotation;
1470 }
1471
1472 let lineCount = -1;
1473
1474 if (this.data.multiLine) {
1475 lineCount = value.split(/\r\n|\r|\n/).length;
1476 }
1477
1478 const defaultPadding = 2;
1479 const hPadding = defaultPadding;
1480 let totalHeight = this.data.rect[3] - this.data.rect[1];
1481 let totalWidth = this.data.rect[2] - this.data.rect[0];
1482
1483 if (rotation === 90 || rotation === 270) {
1484 [totalWidth, totalHeight] = [totalHeight, totalWidth];
1485 }
1486
1487 if (!this._defaultAppearance) {
1488 this.data.defaultAppearanceData = (0, _default_appearance.parseDefaultAppearance)(this._defaultAppearance = "/Helvetica 0 Tf 0 g");
1489 }
1490
1491 const font = await WidgetAnnotation._getFontData(evaluator, task, this.data.defaultAppearanceData, this._fieldResources.mergedResources);
1492
1493 const [defaultAppearance, fontSize] = this._computeFontSize(totalHeight - defaultPadding, totalWidth - 2 * hPadding, value, font, lineCount);
1494
1495 let descent = font.descent;
1496
1497 if (isNaN(descent)) {
1498 descent = 0;
1499 }
1500
1501 const defaultVPadding = Math.min(Math.floor((totalHeight - fontSize) / 2), defaultPadding);
1502 const vPadding = defaultVPadding + Math.abs(descent) * fontSize;
1503 const alignment = this.data.textAlignment;
1504
1505 if (this.data.multiLine) {
1506 return this._getMultilineAppearance(defaultAppearance, value, font, fontSize, totalWidth, totalHeight, alignment, hPadding, vPadding, annotationStorage);
1507 }
1508
1509 const encodedString = font.encodeString(value).join("");
1510
1511 if (this.data.comb) {
1512 return this._getCombAppearance(defaultAppearance, font, encodedString, totalWidth, hPadding, vPadding, annotationStorage);
1513 }
1514
1515 const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
1516
1517 if (alignment === 0 || alignment > 2) {
1518 return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 ${hPadding} ${vPadding} Tm (${(0, _util.escapeString)(encodedString)}) Tj` + " ET Q EMC";
1519 }
1520
1521 const renderedText = this._renderText(encodedString, font, fontSize, totalWidth, alignment, hPadding, vPadding);
1522
1523 return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 0 0 Tm ${renderedText}` + " ET Q EMC";
1524 }
1525
1526 static async _getFontData(evaluator, task, appearanceData, resources) {
1527 const operatorList = new _operator_list.OperatorList();
1528 const initialState = {
1529 font: null,
1530
1531 clone() {
1532 return this;
1533 }
1534
1535 };
1536 const {
1537 fontName,
1538 fontSize
1539 } = appearanceData;
1540 await evaluator.handleSetFont(resources, [fontName && _primitives.Name.get(fontName), fontSize], null, operatorList, task, initialState, null);
1541 return initialState.font;
1542 }
1543
1544 _getTextWidth(text, font) {
1545 return font.charsToGlyphs(text).reduce((width, glyph) => width + glyph.width, 0) / 1000;
1546 }
1547
1548 _computeFontSize(height, width, text, font, lineCount) {
1549 let {
1550 fontSize
1551 } = this.data.defaultAppearanceData;
1552
1553 if (!fontSize) {
1554 const roundWithTwoDigits = x => Math.floor(x * 100) / 100;
1555
1556 if (lineCount === -1) {
1557 const textWidth = this._getTextWidth(text, font);
1558
1559 fontSize = roundWithTwoDigits(Math.min(height / _util.LINE_FACTOR, width / textWidth));
1560 } else {
1561 const lines = text.split(/\r\n?|\n/);
1562 const cachedLines = [];
1563
1564 for (const line of lines) {
1565 const encoded = font.encodeString(line).join("");
1566 const glyphs = font.charsToGlyphs(encoded);
1567 const positions = font.getCharPositions(encoded);
1568 cachedLines.push({
1569 line: encoded,
1570 glyphs,
1571 positions
1572 });
1573 }
1574
1575 const isTooBig = fsize => {
1576 let totalHeight = 0;
1577
1578 for (const cache of cachedLines) {
1579 const chunks = this._splitLine(null, font, fsize, width, cache);
1580
1581 totalHeight += chunks.length * fsize;
1582
1583 if (totalHeight > height) {
1584 return true;
1585 }
1586 }
1587
1588 return false;
1589 };
1590
1591 fontSize = 12;
1592 let lineHeight = fontSize * _util.LINE_FACTOR;
1593 let numberOfLines = Math.round(height / lineHeight);
1594 numberOfLines = Math.max(numberOfLines, lineCount);
1595
1596 while (true) {
1597 lineHeight = height / numberOfLines;
1598 fontSize = roundWithTwoDigits(lineHeight / _util.LINE_FACTOR);
1599
1600 if (isTooBig(fontSize)) {
1601 numberOfLines++;
1602 continue;
1603 }
1604
1605 break;
1606 }
1607 }
1608
1609 const {
1610 fontName,
1611 fontColor
1612 } = this.data.defaultAppearanceData;
1613 this._defaultAppearance = (0, _default_appearance.createDefaultAppearance)({
1614 fontSize,
1615 fontName,
1616 fontColor
1617 });
1618 }
1619
1620 return [this._defaultAppearance, fontSize];
1621 }
1622
1623 _renderText(text, font, fontSize, totalWidth, alignment, hPadding, vPadding) {
1624 let shift;
1625
1626 if (alignment === 1) {
1627 const width = this._getTextWidth(text, font) * fontSize;
1628 shift = (totalWidth - width) / 2;
1629 } else if (alignment === 2) {
1630 const width = this._getTextWidth(text, font) * fontSize;
1631 shift = totalWidth - width - hPadding;
1632 } else {
1633 shift = hPadding;
1634 }
1635
1636 shift = (0, _core_utils.numberToString)(shift);
1637 vPadding = (0, _core_utils.numberToString)(vPadding);
1638 return `${shift} ${vPadding} Td (${(0, _util.escapeString)(text)}) Tj`;
1639 }
1640
1641 _getSaveFieldResources(xref) {
1642 const {
1643 localResources,
1644 appearanceResources,
1645 acroFormResources
1646 } = this._fieldResources;
1647 const fontName = this.data.defaultAppearanceData && this.data.defaultAppearanceData.fontName;
1648
1649 if (!fontName) {
1650 return localResources || _primitives.Dict.empty;
1651 }
1652
1653 for (const resources of [localResources, appearanceResources]) {
1654 if (resources instanceof _primitives.Dict) {
1655 const localFont = resources.get("Font");
1656
1657 if (localFont instanceof _primitives.Dict && localFont.has(fontName)) {
1658 return resources;
1659 }
1660 }
1661 }
1662
1663 if (acroFormResources instanceof _primitives.Dict) {
1664 const acroFormFont = acroFormResources.get("Font");
1665
1666 if (acroFormFont instanceof _primitives.Dict && acroFormFont.has(fontName)) {
1667 const subFontDict = new _primitives.Dict(xref);
1668 subFontDict.set(fontName, acroFormFont.getRaw(fontName));
1669 const subResourcesDict = new _primitives.Dict(xref);
1670 subResourcesDict.set("Font", subFontDict);
1671 return _primitives.Dict.merge({
1672 xref,
1673 dictArray: [subResourcesDict, localResources],
1674 mergeSubDicts: true
1675 });
1676 }
1677 }
1678
1679 return localResources || _primitives.Dict.empty;
1680 }
1681
1682 getFieldObject() {
1683 return null;
1684 }
1685
1686}
1687
1688class TextWidgetAnnotation extends WidgetAnnotation {
1689 constructor(params) {
1690 super(params);
1691 this._hasText = true;
1692 const dict = params.dict;
1693
1694 if (typeof this.data.fieldValue !== "string") {
1695 this.data.fieldValue = "";
1696 }
1697
1698 let alignment = (0, _core_utils.getInheritableProperty)({
1699 dict,
1700 key: "Q"
1701 });
1702
1703 if (!Number.isInteger(alignment) || alignment < 0 || alignment > 2) {
1704 alignment = null;
1705 }
1706
1707 this.data.textAlignment = alignment;
1708 let maximumLength = (0, _core_utils.getInheritableProperty)({
1709 dict,
1710 key: "MaxLen"
1711 });
1712
1713 if (!Number.isInteger(maximumLength) || maximumLength < 0) {
1714 maximumLength = null;
1715 }
1716
1717 this.data.maxLen = maximumLength;
1718 this.data.multiLine = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE);
1719 this.data.comb = this.hasFieldFlag(_util.AnnotationFieldFlag.COMB) && !this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(_util.AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== null;
1720 this.data.doNotScroll = this.hasFieldFlag(_util.AnnotationFieldFlag.DONOTSCROLL);
1721 }
1722
1723 _getCombAppearance(defaultAppearance, font, text, width, hPadding, vPadding, annotationStorage) {
1724 const combWidth = (0, _core_utils.numberToString)(width / this.data.maxLen);
1725 const buf = [];
1726 const positions = font.getCharPositions(text);
1727
1728 for (const [start, end] of positions) {
1729 buf.push(`(${(0, _util.escapeString)(text.substring(start, end))}) Tj`);
1730 }
1731
1732 const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
1733 const renderedComb = buf.join(` ${combWidth} 0 Td `);
1734 return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 ${hPadding} ${vPadding} Tm ${renderedComb}` + " ET Q EMC";
1735 }
1736
1737 _getMultilineAppearance(defaultAppearance, text, font, fontSize, width, height, alignment, hPadding, vPadding, annotationStorage) {
1738 const lines = text.split(/\r\n?|\n/);
1739 const buf = [];
1740 const totalWidth = width - 2 * hPadding;
1741
1742 for (const line of lines) {
1743 const chunks = this._splitLine(line, font, fontSize, totalWidth);
1744
1745 for (const chunk of chunks) {
1746 const padding = buf.length === 0 ? hPadding : 0;
1747 buf.push(this._renderText(chunk, font, fontSize, width, alignment, padding, -fontSize));
1748 }
1749 }
1750
1751 const renderedText = buf.join("\n");
1752 const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
1753 return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 0 ${height} Tm ${renderedText}` + " ET Q EMC";
1754 }
1755
1756 _splitLine(line, font, fontSize, width, cache = {}) {
1757 line = cache.line || font.encodeString(line).join("");
1758 const glyphs = cache.glyphs || font.charsToGlyphs(line);
1759
1760 if (glyphs.length <= 1) {
1761 return [line];
1762 }
1763
1764 const positions = cache.positions || font.getCharPositions(line);
1765 const scale = fontSize / 1000;
1766 const chunks = [];
1767 let lastSpacePosInStringStart = -1,
1768 lastSpacePosInStringEnd = -1,
1769 lastSpacePos = -1,
1770 startChunk = 0,
1771 currentWidth = 0;
1772
1773 for (let i = 0, ii = glyphs.length; i < ii; i++) {
1774 const [start, end] = positions[i];
1775 const glyph = glyphs[i];
1776 const glyphWidth = glyph.width * scale;
1777
1778 if (glyph.unicode === " ") {
1779 if (currentWidth + glyphWidth > width) {
1780 chunks.push(line.substring(startChunk, start));
1781 startChunk = start;
1782 currentWidth = glyphWidth;
1783 lastSpacePosInStringStart = -1;
1784 lastSpacePos = -1;
1785 } else {
1786 currentWidth += glyphWidth;
1787 lastSpacePosInStringStart = start;
1788 lastSpacePosInStringEnd = end;
1789 lastSpacePos = i;
1790 }
1791 } else {
1792 if (currentWidth + glyphWidth > width) {
1793 if (lastSpacePosInStringStart !== -1) {
1794 chunks.push(line.substring(startChunk, lastSpacePosInStringEnd));
1795 startChunk = lastSpacePosInStringEnd;
1796 i = lastSpacePos + 1;
1797 lastSpacePosInStringStart = -1;
1798 currentWidth = 0;
1799 } else {
1800 chunks.push(line.substring(startChunk, start));
1801 startChunk = start;
1802 currentWidth = glyphWidth;
1803 }
1804 } else {
1805 currentWidth += glyphWidth;
1806 }
1807 }
1808 }
1809
1810 if (startChunk < line.length) {
1811 chunks.push(line.substring(startChunk, line.length));
1812 }
1813
1814 return chunks;
1815 }
1816
1817 getFieldObject() {
1818 return {
1819 id: this.data.id,
1820 value: this.data.fieldValue,
1821 defaultValue: this.data.defaultFieldValue || "",
1822 multiline: this.data.multiLine,
1823 password: this.hasFieldFlag(_util.AnnotationFieldFlag.PASSWORD),
1824 charLimit: this.data.maxLen,
1825 comb: this.data.comb,
1826 editable: !this.data.readOnly,
1827 hidden: this.data.hidden,
1828 name: this.data.fieldName,
1829 rect: this.data.rect,
1830 actions: this.data.actions,
1831 page: this.data.pageIndex,
1832 strokeColor: this.data.borderColor,
1833 fillColor: this.data.backgroundColor,
1834 rotation: this.rotation,
1835 type: "text"
1836 };
1837 }
1838
1839}
1840
1841class ButtonWidgetAnnotation extends WidgetAnnotation {
1842 constructor(params) {
1843 super(params);
1844 this.checkedAppearance = null;
1845 this.uncheckedAppearance = null;
1846 this.data.checkBox = !this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON);
1847 this.data.radioButton = this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON);
1848 this.data.pushButton = this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON);
1849 this.data.isTooltipOnly = false;
1850
1851 if (this.data.checkBox) {
1852 this._processCheckBox(params);
1853 } else if (this.data.radioButton) {
1854 this._processRadioButton(params);
1855 } else if (this.data.pushButton) {
1856 this.data.hasOwnCanvas = true;
1857
1858 this._processPushButton(params);
1859 } else {
1860 (0, _util.warn)("Invalid field flags for button widget annotation");
1861 }
1862 }
1863
1864 async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {
1865 if (this.data.pushButton) {
1866 return super.getOperatorList(evaluator, task, intent, false, annotationStorage);
1867 }
1868
1869 let value = null;
1870 let rotation = null;
1871
1872 if (annotationStorage) {
1873 const storageEntry = annotationStorage.get(this.data.id);
1874 value = storageEntry ? storageEntry.value : null;
1875 rotation = storageEntry ? storageEntry.rotation : null;
1876 }
1877
1878 if (value === null && this.appearance) {
1879 return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
1880 }
1881
1882 if (value === null || value === undefined) {
1883 if (this.data.checkBox) {
1884 value = this.data.fieldValue === this.data.exportValue;
1885 } else {
1886 value = this.data.fieldValue === this.data.buttonValue;
1887 }
1888 }
1889
1890 const appearance = value ? this.checkedAppearance : this.uncheckedAppearance;
1891
1892 if (appearance) {
1893 const savedAppearance = this.appearance;
1894
1895 const savedMatrix = appearance.dict.getArray("Matrix") || _util.IDENTITY_MATRIX;
1896
1897 if (rotation) {
1898 appearance.dict.set("Matrix", this.getRotationMatrix(annotationStorage));
1899 }
1900
1901 this.appearance = appearance;
1902 const operatorList = super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
1903 this.appearance = savedAppearance;
1904 appearance.dict.set("Matrix", savedMatrix);
1905 return operatorList;
1906 }
1907
1908 return {
1909 opList: new _operator_list.OperatorList(),
1910 separateForm: false,
1911 separateCanvas: false
1912 };
1913 }
1914
1915 async save(evaluator, task, annotationStorage) {
1916 if (this.data.checkBox) {
1917 return this._saveCheckbox(evaluator, task, annotationStorage);
1918 }
1919
1920 if (this.data.radioButton) {
1921 return this._saveRadioButton(evaluator, task, annotationStorage);
1922 }
1923
1924 return null;
1925 }
1926
1927 async _saveCheckbox(evaluator, task, annotationStorage) {
1928 if (!annotationStorage) {
1929 return null;
1930 }
1931
1932 const storageEntry = annotationStorage.get(this.data.id);
1933 let rotation = storageEntry && storageEntry.rotation;
1934 let value = storageEntry && storageEntry.value;
1935
1936 if (rotation === undefined) {
1937 if (value === undefined) {
1938 return null;
1939 }
1940
1941 const defaultValue = this.data.fieldValue === this.data.exportValue;
1942
1943 if (defaultValue === value) {
1944 return null;
1945 }
1946 }
1947
1948 const dict = evaluator.xref.fetchIfRef(this.ref);
1949
1950 if (!(dict instanceof _primitives.Dict)) {
1951 return null;
1952 }
1953
1954 if (rotation === undefined) {
1955 rotation = this.rotation;
1956 }
1957
1958 if (value === undefined) {
1959 value = this.data.fieldValue === this.data.exportValue;
1960 }
1961
1962 const xfa = {
1963 path: (0, _util.stringToPDFString)(dict.get("T") || ""),
1964 value: value ? this.data.exportValue : ""
1965 };
1966
1967 const name = _primitives.Name.get(value ? this.data.exportValue : "Off");
1968
1969 dict.set("V", name);
1970 dict.set("AS", name);
1971 dict.set("M", `D:${(0, _util.getModificationDate)()}`);
1972
1973 const maybeMK = this._getMKDict(rotation);
1974
1975 if (maybeMK) {
1976 dict.set("MK", maybeMK);
1977 }
1978
1979 const encrypt = evaluator.xref.encrypt;
1980 let originalTransform = null;
1981
1982 if (encrypt) {
1983 originalTransform = encrypt.createCipherTransform(this.ref.num, this.ref.gen);
1984 }
1985
1986 const buffer = [`${this.ref.num} ${this.ref.gen} obj\n`];
1987 (0, _writer.writeDict)(dict, buffer, originalTransform);
1988 buffer.push("\nendobj\n");
1989 return [{
1990 ref: this.ref,
1991 data: buffer.join(""),
1992 xfa
1993 }];
1994 }
1995
1996 async _saveRadioButton(evaluator, task, annotationStorage) {
1997 if (!annotationStorage) {
1998 return null;
1999 }
2000
2001 const storageEntry = annotationStorage.get(this.data.id);
2002 let rotation = storageEntry && storageEntry.rotation;
2003 let value = storageEntry && storageEntry.value;
2004
2005 if (rotation === undefined) {
2006 if (value === undefined) {
2007 return null;
2008 }
2009
2010 const defaultValue = this.data.fieldValue === this.data.buttonValue;
2011
2012 if (defaultValue === value) {
2013 return null;
2014 }
2015 }
2016
2017 const dict = evaluator.xref.fetchIfRef(this.ref);
2018
2019 if (!(dict instanceof _primitives.Dict)) {
2020 return null;
2021 }
2022
2023 if (value === undefined) {
2024 value = this.data.fieldValue === this.data.buttonValue;
2025 }
2026
2027 if (rotation === undefined) {
2028 rotation = this.rotation;
2029 }
2030
2031 const xfa = {
2032 path: (0, _util.stringToPDFString)(dict.get("T") || ""),
2033 value: value ? this.data.buttonValue : ""
2034 };
2035
2036 const name = _primitives.Name.get(value ? this.data.buttonValue : "Off");
2037
2038 let parentBuffer = null;
2039 const encrypt = evaluator.xref.encrypt;
2040
2041 if (value) {
2042 if (this.parent instanceof _primitives.Ref) {
2043 const parent = evaluator.xref.fetch(this.parent);
2044 let parentTransform = null;
2045
2046 if (encrypt) {
2047 parentTransform = encrypt.createCipherTransform(this.parent.num, this.parent.gen);
2048 }
2049
2050 parent.set("V", name);
2051 parentBuffer = [`${this.parent.num} ${this.parent.gen} obj\n`];
2052 (0, _writer.writeDict)(parent, parentBuffer, parentTransform);
2053 parentBuffer.push("\nendobj\n");
2054 } else if (this.parent instanceof _primitives.Dict) {
2055 this.parent.set("V", name);
2056 }
2057 }
2058
2059 dict.set("AS", name);
2060 dict.set("M", `D:${(0, _util.getModificationDate)()}`);
2061
2062 const maybeMK = this._getMKDict(rotation);
2063
2064 if (maybeMK) {
2065 dict.set("MK", maybeMK);
2066 }
2067
2068 let originalTransform = null;
2069
2070 if (encrypt) {
2071 originalTransform = encrypt.createCipherTransform(this.ref.num, this.ref.gen);
2072 }
2073
2074 const buffer = [`${this.ref.num} ${this.ref.gen} obj\n`];
2075 (0, _writer.writeDict)(dict, buffer, originalTransform);
2076 buffer.push("\nendobj\n");
2077 const newRefs = [{
2078 ref: this.ref,
2079 data: buffer.join(""),
2080 xfa
2081 }];
2082
2083 if (parentBuffer !== null) {
2084 newRefs.push({
2085 ref: this.parent,
2086 data: parentBuffer.join(""),
2087 xfa: null
2088 });
2089 }
2090
2091 return newRefs;
2092 }
2093
2094 _getDefaultCheckedAppearance(params, type) {
2095 const width = this.data.rect[2] - this.data.rect[0];
2096 const height = this.data.rect[3] - this.data.rect[1];
2097 const bbox = [0, 0, width, height];
2098 const FONT_RATIO = 0.8;
2099 const fontSize = Math.min(width, height) * FONT_RATIO;
2100 let metrics, char;
2101
2102 if (type === "check") {
2103 metrics = {
2104 width: 0.755 * fontSize,
2105 height: 0.705 * fontSize
2106 };
2107 char = "\x33";
2108 } else if (type === "disc") {
2109 metrics = {
2110 width: 0.791 * fontSize,
2111 height: 0.705 * fontSize
2112 };
2113 char = "\x6C";
2114 } else {
2115 (0, _util.unreachable)(`_getDefaultCheckedAppearance - unsupported type: ${type}`);
2116 }
2117
2118 const xShift = (0, _core_utils.numberToString)((width - metrics.width) / 2);
2119 const yShift = (0, _core_utils.numberToString)((height - metrics.height) / 2);
2120 const appearance = `q BT /PdfJsZaDb ${fontSize} Tf 0 g ${xShift} ${yShift} Td (${char}) Tj ET Q`;
2121 const appearanceStreamDict = new _primitives.Dict(params.xref);
2122 appearanceStreamDict.set("FormType", 1);
2123 appearanceStreamDict.set("Subtype", _primitives.Name.get("Form"));
2124 appearanceStreamDict.set("Type", _primitives.Name.get("XObject"));
2125 appearanceStreamDict.set("BBox", bbox);
2126 appearanceStreamDict.set("Matrix", [1, 0, 0, 1, 0, 0]);
2127 appearanceStreamDict.set("Length", appearance.length);
2128 const resources = new _primitives.Dict(params.xref);
2129 const font = new _primitives.Dict(params.xref);
2130 font.set("PdfJsZaDb", this.fallbackFontDict);
2131 resources.set("Font", font);
2132 appearanceStreamDict.set("Resources", resources);
2133 this.checkedAppearance = new _stream.StringStream(appearance);
2134 this.checkedAppearance.dict = appearanceStreamDict;
2135
2136 this._streams.push(this.checkedAppearance);
2137 }
2138
2139 _processCheckBox(params) {
2140 const customAppearance = params.dict.get("AP");
2141
2142 if (!(customAppearance instanceof _primitives.Dict)) {
2143 return;
2144 }
2145
2146 const normalAppearance = customAppearance.get("N");
2147
2148 if (!(normalAppearance instanceof _primitives.Dict)) {
2149 return;
2150 }
2151
2152 const asValue = this._decodeFormValue(params.dict.get("AS"));
2153
2154 if (typeof asValue === "string") {
2155 this.data.fieldValue = asValue;
2156 }
2157
2158 const yes = this.data.fieldValue !== null && this.data.fieldValue !== "Off" ? this.data.fieldValue : "Yes";
2159 const exportValues = normalAppearance.getKeys();
2160
2161 if (exportValues.length === 0) {
2162 exportValues.push("Off", yes);
2163 } else if (exportValues.length === 1) {
2164 if (exportValues[0] === "Off") {
2165 exportValues.push(yes);
2166 } else {
2167 exportValues.unshift("Off");
2168 }
2169 } else if (exportValues.includes(yes)) {
2170 exportValues.length = 0;
2171 exportValues.push("Off", yes);
2172 } else {
2173 const otherYes = exportValues.find(v => v !== "Off");
2174 exportValues.length = 0;
2175 exportValues.push("Off", otherYes);
2176 }
2177
2178 if (!exportValues.includes(this.data.fieldValue)) {
2179 this.data.fieldValue = "Off";
2180 }
2181
2182 this.data.exportValue = exportValues[1];
2183 this.checkedAppearance = normalAppearance.get(this.data.exportValue) || null;
2184 this.uncheckedAppearance = normalAppearance.get("Off") || null;
2185
2186 if (this.checkedAppearance) {
2187 this._streams.push(this.checkedAppearance);
2188 } else {
2189 this._getDefaultCheckedAppearance(params, "check");
2190 }
2191
2192 if (this.uncheckedAppearance) {
2193 this._streams.push(this.uncheckedAppearance);
2194 }
2195
2196 this._fallbackFontDict = this.fallbackFontDict;
2197 }
2198
2199 _processRadioButton(params) {
2200 this.data.fieldValue = this.data.buttonValue = null;
2201 const fieldParent = params.dict.get("Parent");
2202
2203 if (fieldParent instanceof _primitives.Dict) {
2204 this.parent = params.dict.getRaw("Parent");
2205 const fieldParentValue = fieldParent.get("V");
2206
2207 if (fieldParentValue instanceof _primitives.Name) {
2208 this.data.fieldValue = this._decodeFormValue(fieldParentValue);
2209 }
2210 }
2211
2212 const appearanceStates = params.dict.get("AP");
2213
2214 if (!(appearanceStates instanceof _primitives.Dict)) {
2215 return;
2216 }
2217
2218 const normalAppearance = appearanceStates.get("N");
2219
2220 if (!(normalAppearance instanceof _primitives.Dict)) {
2221 return;
2222 }
2223
2224 for (const key of normalAppearance.getKeys()) {
2225 if (key !== "Off") {
2226 this.data.buttonValue = this._decodeFormValue(key);
2227 break;
2228 }
2229 }
2230
2231 this.checkedAppearance = normalAppearance.get(this.data.buttonValue) || null;
2232 this.uncheckedAppearance = normalAppearance.get("Off") || null;
2233
2234 if (this.checkedAppearance) {
2235 this._streams.push(this.checkedAppearance);
2236 } else {
2237 this._getDefaultCheckedAppearance(params, "disc");
2238 }
2239
2240 if (this.uncheckedAppearance) {
2241 this._streams.push(this.uncheckedAppearance);
2242 }
2243
2244 this._fallbackFontDict = this.fallbackFontDict;
2245 }
2246
2247 _processPushButton(params) {
2248 if (!params.dict.has("A") && !params.dict.has("AA") && !this.data.alternativeText) {
2249 (0, _util.warn)("Push buttons without action dictionaries are not supported");
2250 return;
2251 }
2252
2253 this.data.isTooltipOnly = !params.dict.has("A") && !params.dict.has("AA");
2254
2255 _catalog.Catalog.parseDestDictionary({
2256 destDict: params.dict,
2257 resultObj: this.data,
2258 docBaseUrl: params.pdfManager.docBaseUrl
2259 });
2260 }
2261
2262 getFieldObject() {
2263 let type = "button";
2264 let exportValues;
2265
2266 if (this.data.checkBox) {
2267 type = "checkbox";
2268 exportValues = this.data.exportValue;
2269 } else if (this.data.radioButton) {
2270 type = "radiobutton";
2271 exportValues = this.data.buttonValue;
2272 }
2273
2274 return {
2275 id: this.data.id,
2276 value: this.data.fieldValue || "Off",
2277 defaultValue: this.data.defaultFieldValue,
2278 exportValues,
2279 editable: !this.data.readOnly,
2280 name: this.data.fieldName,
2281 rect: this.data.rect,
2282 hidden: this.data.hidden,
2283 actions: this.data.actions,
2284 page: this.data.pageIndex,
2285 strokeColor: this.data.borderColor,
2286 fillColor: this.data.backgroundColor,
2287 rotation: this.rotation,
2288 type
2289 };
2290 }
2291
2292 get fallbackFontDict() {
2293 const dict = new _primitives.Dict();
2294 dict.set("BaseFont", _primitives.Name.get("ZapfDingbats"));
2295 dict.set("Type", _primitives.Name.get("FallbackType"));
2296 dict.set("Subtype", _primitives.Name.get("FallbackType"));
2297 dict.set("Encoding", _primitives.Name.get("ZapfDingbatsEncoding"));
2298 return (0, _util.shadow)(this, "fallbackFontDict", dict);
2299 }
2300
2301}
2302
2303class ChoiceWidgetAnnotation extends WidgetAnnotation {
2304 constructor(params) {
2305 super(params);
2306 this.data.options = [];
2307 const options = (0, _core_utils.getInheritableProperty)({
2308 dict: params.dict,
2309 key: "Opt"
2310 });
2311
2312 if (Array.isArray(options)) {
2313 const xref = params.xref;
2314
2315 for (let i = 0, ii = options.length; i < ii; i++) {
2316 const option = xref.fetchIfRef(options[i]);
2317 const isOptionArray = Array.isArray(option);
2318 this.data.options[i] = {
2319 exportValue: this._decodeFormValue(isOptionArray ? xref.fetchIfRef(option[0]) : option),
2320 displayValue: this._decodeFormValue(isOptionArray ? xref.fetchIfRef(option[1]) : option)
2321 };
2322 }
2323 }
2324
2325 if (typeof this.data.fieldValue === "string") {
2326 this.data.fieldValue = [this.data.fieldValue];
2327 } else if (!this.data.fieldValue) {
2328 this.data.fieldValue = [];
2329 }
2330
2331 this.data.combo = this.hasFieldFlag(_util.AnnotationFieldFlag.COMBO);
2332 this.data.multiSelect = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTISELECT);
2333 this._hasText = true;
2334 }
2335
2336 getFieldObject() {
2337 const type = this.data.combo ? "combobox" : "listbox";
2338 const value = this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null;
2339 return {
2340 id: this.data.id,
2341 value,
2342 defaultValue: this.data.defaultFieldValue,
2343 editable: !this.data.readOnly,
2344 name: this.data.fieldName,
2345 rect: this.data.rect,
2346 numItems: this.data.fieldValue.length,
2347 multipleSelection: this.data.multiSelect,
2348 hidden: this.data.hidden,
2349 actions: this.data.actions,
2350 items: this.data.options,
2351 page: this.data.pageIndex,
2352 strokeColor: this.data.borderColor,
2353 fillColor: this.data.backgroundColor,
2354 rotation: this.rotation,
2355 type
2356 };
2357 }
2358
2359 async _getAppearance(evaluator, task, annotationStorage) {
2360 if (this.data.combo) {
2361 return super._getAppearance(evaluator, task, annotationStorage);
2362 }
2363
2364 if (!annotationStorage) {
2365 return null;
2366 }
2367
2368 const storageEntry = annotationStorage.get(this.data.id);
2369
2370 if (!storageEntry) {
2371 return null;
2372 }
2373
2374 const rotation = storageEntry.rotation;
2375 let exportedValue = storageEntry.value;
2376
2377 if (rotation === undefined && exportedValue === undefined) {
2378 return null;
2379 }
2380
2381 if (exportedValue === undefined) {
2382 exportedValue = this.data.fieldValue;
2383 } else if (!Array.isArray(exportedValue)) {
2384 exportedValue = [exportedValue];
2385 }
2386
2387 const defaultPadding = 2;
2388 const hPadding = defaultPadding;
2389 let totalHeight = this.data.rect[3] - this.data.rect[1];
2390 let totalWidth = this.data.rect[2] - this.data.rect[0];
2391
2392 if (rotation === 90 || rotation === 270) {
2393 [totalWidth, totalHeight] = [totalHeight, totalWidth];
2394 }
2395
2396 const lineCount = this.data.options.length;
2397 const valueIndices = [];
2398
2399 for (let i = 0; i < lineCount; i++) {
2400 const {
2401 exportValue
2402 } = this.data.options[i];
2403
2404 if (exportedValue.includes(exportValue)) {
2405 valueIndices.push(i);
2406 }
2407 }
2408
2409 if (!this._defaultAppearance) {
2410 this.data.defaultAppearanceData = (0, _default_appearance.parseDefaultAppearance)(this._defaultAppearance = "/Helvetica 0 Tf 0 g");
2411 }
2412
2413 const font = await WidgetAnnotation._getFontData(evaluator, task, this.data.defaultAppearanceData, this._fieldResources.mergedResources);
2414 let defaultAppearance;
2415 let {
2416 fontSize
2417 } = this.data.defaultAppearanceData;
2418
2419 if (!fontSize) {
2420 const lineHeight = (totalHeight - defaultPadding) / lineCount;
2421 let lineWidth = -1;
2422 let value;
2423
2424 for (const {
2425 displayValue
2426 } of this.data.options) {
2427 const width = this._getTextWidth(displayValue, font);
2428
2429 if (width > lineWidth) {
2430 lineWidth = width;
2431 value = displayValue;
2432 }
2433 }
2434
2435 [defaultAppearance, fontSize] = this._computeFontSize(lineHeight, totalWidth - 2 * hPadding, value, font, -1);
2436 } else {
2437 defaultAppearance = this._defaultAppearance;
2438 }
2439
2440 const lineHeight = fontSize * _util.LINE_FACTOR;
2441 const vPadding = (lineHeight - fontSize) / 2;
2442 const numberOfVisibleLines = Math.floor(totalHeight / lineHeight);
2443 let firstIndex;
2444
2445 if (valueIndices.length === 1) {
2446 const valuePosition = valueIndices[0];
2447 const indexInPage = valuePosition % numberOfVisibleLines;
2448 firstIndex = valuePosition - indexInPage;
2449 } else {
2450 firstIndex = valueIndices.length ? valueIndices[0] : 0;
2451 }
2452
2453 const end = Math.min(firstIndex + numberOfVisibleLines + 1, lineCount);
2454 const buf = ["/Tx BMC q", `1 1 ${totalWidth} ${totalHeight} re W n`];
2455
2456 if (valueIndices.length) {
2457 buf.push("0.600006 0.756866 0.854904 rg");
2458
2459 for (const index of valueIndices) {
2460 if (firstIndex <= index && index < end) {
2461 buf.push(`1 ${totalHeight - (index - firstIndex + 1) * lineHeight} ${totalWidth} ${lineHeight} re f`);
2462 }
2463 }
2464 }
2465
2466 buf.push("BT", defaultAppearance, `1 0 0 1 0 ${totalHeight} Tm`);
2467
2468 for (let i = firstIndex; i < end; i++) {
2469 const {
2470 displayValue
2471 } = this.data.options[i];
2472 const hpadding = i === firstIndex ? hPadding : 0;
2473 const vpadding = i === firstIndex ? vPadding : 0;
2474 buf.push(this._renderText(displayValue, font, fontSize, totalWidth, 0, hpadding, -lineHeight + vpadding));
2475 }
2476
2477 buf.push("ET Q EMC");
2478 return buf.join("\n");
2479 }
2480
2481}
2482
2483class SignatureWidgetAnnotation extends WidgetAnnotation {
2484 constructor(params) {
2485 super(params);
2486 this.data.fieldValue = null;
2487 }
2488
2489 getFieldObject() {
2490 return {
2491 id: this.data.id,
2492 value: null,
2493 page: this.data.pageIndex,
2494 type: "signature"
2495 };
2496 }
2497
2498}
2499
2500class TextAnnotation extends MarkupAnnotation {
2501 constructor(parameters) {
2502 const DEFAULT_ICON_SIZE = 22;
2503 super(parameters);
2504 const dict = parameters.dict;
2505 this.data.annotationType = _util.AnnotationType.TEXT;
2506
2507 if (this.data.hasAppearance) {
2508 this.data.name = "NoIcon";
2509 } else {
2510 this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;
2511 this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;
2512 this.data.name = dict.has("Name") ? dict.get("Name").name : "Note";
2513 }
2514
2515 if (dict.has("State")) {
2516 this.data.state = dict.get("State") || null;
2517 this.data.stateModel = dict.get("StateModel") || null;
2518 } else {
2519 this.data.state = null;
2520 this.data.stateModel = null;
2521 }
2522 }
2523
2524}
2525
2526class LinkAnnotation extends Annotation {
2527 constructor(params) {
2528 super(params);
2529 this.data.annotationType = _util.AnnotationType.LINK;
2530 const quadPoints = getQuadPoints(params.dict, this.rectangle);
2531
2532 if (quadPoints) {
2533 this.data.quadPoints = quadPoints;
2534 }
2535
2536 this.data.borderColor = this.data.borderColor || this.data.color;
2537
2538 _catalog.Catalog.parseDestDictionary({
2539 destDict: params.dict,
2540 resultObj: this.data,
2541 docBaseUrl: params.pdfManager.docBaseUrl
2542 });
2543 }
2544
2545}
2546
2547class PopupAnnotation extends Annotation {
2548 constructor(parameters) {
2549 super(parameters);
2550 this.data.annotationType = _util.AnnotationType.POPUP;
2551 let parentItem = parameters.dict.get("Parent");
2552
2553 if (!parentItem) {
2554 (0, _util.warn)("Popup annotation has a missing or invalid parent annotation.");
2555 return;
2556 }
2557
2558 const parentSubtype = parentItem.get("Subtype");
2559 this.data.parentType = parentSubtype instanceof _primitives.Name ? parentSubtype.name : null;
2560 const rawParent = parameters.dict.getRaw("Parent");
2561 this.data.parentId = rawParent instanceof _primitives.Ref ? rawParent.toString() : null;
2562 const parentRect = parentItem.getArray("Rect");
2563
2564 if (Array.isArray(parentRect) && parentRect.length === 4) {
2565 this.data.parentRect = _util.Util.normalizeRect(parentRect);
2566 } else {
2567 this.data.parentRect = [0, 0, 0, 0];
2568 }
2569
2570 const rt = parentItem.get("RT");
2571
2572 if ((0, _primitives.isName)(rt, _util.AnnotationReplyType.GROUP)) {
2573 parentItem = parentItem.get("IRT");
2574 }
2575
2576 if (!parentItem.has("M")) {
2577 this.data.modificationDate = null;
2578 } else {
2579 this.setModificationDate(parentItem.get("M"));
2580 this.data.modificationDate = this.modificationDate;
2581 }
2582
2583 if (!parentItem.has("C")) {
2584 this.data.color = null;
2585 } else {
2586 this.setColor(parentItem.getArray("C"));
2587 this.data.color = this.color;
2588 }
2589
2590 if (!this.viewable) {
2591 const parentFlags = parentItem.get("F");
2592
2593 if (this._isViewable(parentFlags)) {
2594 this.setFlags(parentFlags);
2595 }
2596 }
2597
2598 this.setTitle(parentItem.get("T"));
2599 this.data.titleObj = this._title;
2600 this.setContents(parentItem.get("Contents"));
2601 this.data.contentsObj = this._contents;
2602
2603 if (parentItem.has("RC")) {
2604 this.data.richText = _factory.XFAFactory.getRichTextAsHtml(parentItem.get("RC"));
2605 }
2606 }
2607
2608}
2609
2610class FreeTextAnnotation extends MarkupAnnotation {
2611 constructor(parameters) {
2612 super(parameters);
2613 this.data.annotationType = _util.AnnotationType.FREETEXT;
2614 }
2615
2616 static createNewDict(annotation, xref, {
2617 apRef,
2618 ap
2619 }) {
2620 const {
2621 color,
2622 fontSize,
2623 rect,
2624 rotation,
2625 user,
2626 value
2627 } = annotation;
2628 const freetext = new _primitives.Dict(xref);
2629 freetext.set("Type", _primitives.Name.get("Annot"));
2630 freetext.set("Subtype", _primitives.Name.get("FreeText"));
2631 freetext.set("CreationDate", `D:${(0, _util.getModificationDate)()}`);
2632 freetext.set("Rect", rect);
2633 const da = `/Helv ${fontSize} Tf ${(0, _default_appearance.getPdfColor)(color, true)}`;
2634 freetext.set("DA", da);
2635 freetext.set("Contents", value);
2636 freetext.set("F", 4);
2637 freetext.set("Border", [0, 0, 0]);
2638 freetext.set("Rotate", rotation);
2639
2640 if (user) {
2641 freetext.set("T", (0, _util.stringToUTF8String)(user));
2642 }
2643
2644 const n = new _primitives.Dict(xref);
2645 freetext.set("AP", n);
2646
2647 if (apRef) {
2648 n.set("N", apRef);
2649 } else {
2650 n.set("N", ap);
2651 }
2652
2653 return freetext;
2654 }
2655
2656 static async createNewAppearanceStream(annotation, xref, params) {
2657 const {
2658 baseFontRef,
2659 evaluator,
2660 task
2661 } = params;
2662 const {
2663 color,
2664 fontSize,
2665 rect,
2666 rotation,
2667 value
2668 } = annotation;
2669 const resources = new _primitives.Dict(xref);
2670 const font = new _primitives.Dict(xref);
2671
2672 if (baseFontRef) {
2673 font.set("Helv", baseFontRef);
2674 } else {
2675 const baseFont = new _primitives.Dict(xref);
2676 baseFont.set("BaseFont", _primitives.Name.get("Helvetica"));
2677 baseFont.set("Type", _primitives.Name.get("Font"));
2678 baseFont.set("Subtype", _primitives.Name.get("Type1"));
2679 baseFont.set("Encoding", _primitives.Name.get("WinAnsiEncoding"));
2680 font.set("Helv", baseFont);
2681 }
2682
2683 resources.set("Font", font);
2684 const helv = await WidgetAnnotation._getFontData(evaluator, task, {
2685 fontName: "Helvetica",
2686 fontSize
2687 }, resources);
2688 const [x1, y1, x2, y2] = rect;
2689 let w = x2 - x1;
2690 let h = y2 - y1;
2691
2692 if (rotation % 180 !== 0) {
2693 [w, h] = [h, w];
2694 }
2695
2696 const lines = value.split("\n");
2697 const scale = fontSize / 1000;
2698 let totalWidth = -Infinity;
2699 const encodedLines = [];
2700
2701 for (let line of lines) {
2702 line = helv.encodeString(line).join("");
2703 encodedLines.push(line);
2704 let lineWidth = 0;
2705 const glyphs = helv.charsToGlyphs(line);
2706
2707 for (const glyph of glyphs) {
2708 lineWidth += glyph.width * scale;
2709 }
2710
2711 totalWidth = Math.max(totalWidth, lineWidth);
2712 }
2713
2714 let hscale = 1;
2715
2716 if (totalWidth > w) {
2717 hscale = w / totalWidth;
2718 }
2719
2720 let vscale = 1;
2721 const lineHeight = _util.LINE_FACTOR * fontSize;
2722 const lineDescent = _util.LINE_DESCENT_FACTOR * fontSize;
2723 const totalHeight = lineHeight * lines.length;
2724
2725 if (totalHeight > h) {
2726 vscale = h / totalHeight;
2727 }
2728
2729 const fscale = Math.min(hscale, vscale);
2730 const newFontSize = fontSize * fscale;
2731 const buffer = ["q", `0 0 ${(0, _core_utils.numberToString)(w)} ${(0, _core_utils.numberToString)(h)} re W n`, `BT`, `1 0 0 1 0 ${(0, _core_utils.numberToString)(h + lineDescent)} Tm 0 Tc ${(0, _default_appearance.getPdfColor)(color, true)}`, `/Helv ${(0, _core_utils.numberToString)(newFontSize)} Tf`];
2732 const vShift = (0, _core_utils.numberToString)(lineHeight);
2733
2734 for (const line of encodedLines) {
2735 buffer.push(`0 -${vShift} Td (${(0, _util.escapeString)(line)}) Tj`);
2736 }
2737
2738 buffer.push("ET", "Q");
2739 const appearance = buffer.join("\n");
2740 const appearanceStreamDict = new _primitives.Dict(xref);
2741 appearanceStreamDict.set("FormType", 1);
2742 appearanceStreamDict.set("Subtype", _primitives.Name.get("Form"));
2743 appearanceStreamDict.set("Type", _primitives.Name.get("XObject"));
2744 appearanceStreamDict.set("BBox", [0, 0, w, h]);
2745 appearanceStreamDict.set("Length", appearance.length);
2746 appearanceStreamDict.set("Resources", resources);
2747
2748 if (rotation) {
2749 const matrix = WidgetAnnotation._getRotationMatrix(rotation, w, h);
2750
2751 appearanceStreamDict.set("Matrix", matrix);
2752 }
2753
2754 const ap = new _stream.StringStream(appearance);
2755 ap.dict = appearanceStreamDict;
2756 return ap;
2757 }
2758
2759}
2760
2761class LineAnnotation extends MarkupAnnotation {
2762 constructor(parameters) {
2763 super(parameters);
2764 const {
2765 dict
2766 } = parameters;
2767 this.data.annotationType = _util.AnnotationType.LINE;
2768 const lineCoordinates = dict.getArray("L");
2769 this.data.lineCoordinates = _util.Util.normalizeRect(lineCoordinates);
2770 this.setLineEndings(dict.getArray("LE"));
2771 this.data.lineEndings = this.lineEndings;
2772
2773 if (!this.appearance) {
2774 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
2775 const strokeAlpha = dict.get("CA");
2776 let fillColor = null,
2777 interiorColor = dict.getArray("IC");
2778
2779 if (interiorColor) {
2780 interiorColor = getRgbColor(interiorColor, null);
2781 fillColor = interiorColor ? Array.from(interiorColor).map(c => c / 255) : null;
2782 }
2783
2784 const fillAlpha = fillColor ? strokeAlpha : null;
2785 const borderWidth = this.borderStyle.width || 1,
2786 borderAdjust = 2 * borderWidth;
2787 const bbox = [this.data.lineCoordinates[0] - borderAdjust, this.data.lineCoordinates[1] - borderAdjust, this.data.lineCoordinates[2] + borderAdjust, this.data.lineCoordinates[3] + borderAdjust];
2788
2789 if (!_util.Util.intersect(this.rectangle, bbox)) {
2790 this.rectangle = bbox;
2791 }
2792
2793 this._setDefaultAppearance({
2794 xref: parameters.xref,
2795 extra: `${borderWidth} w`,
2796 strokeColor,
2797 fillColor,
2798 strokeAlpha,
2799 fillAlpha,
2800 pointsCallback: (buffer, points) => {
2801 buffer.push(`${lineCoordinates[0]} ${lineCoordinates[1]} m`, `${lineCoordinates[2]} ${lineCoordinates[3]} l`, "S");
2802 return [points[0].x - borderWidth, points[1].x + borderWidth, points[3].y - borderWidth, points[1].y + borderWidth];
2803 }
2804 });
2805 }
2806 }
2807
2808}
2809
2810class SquareAnnotation extends MarkupAnnotation {
2811 constructor(parameters) {
2812 super(parameters);
2813 this.data.annotationType = _util.AnnotationType.SQUARE;
2814
2815 if (!this.appearance) {
2816 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
2817 const strokeAlpha = parameters.dict.get("CA");
2818 let fillColor = null,
2819 interiorColor = parameters.dict.getArray("IC");
2820
2821 if (interiorColor) {
2822 interiorColor = getRgbColor(interiorColor, null);
2823 fillColor = interiorColor ? Array.from(interiorColor).map(c => c / 255) : null;
2824 }
2825
2826 const fillAlpha = fillColor ? strokeAlpha : null;
2827
2828 if (this.borderStyle.width === 0 && !fillColor) {
2829 return;
2830 }
2831
2832 this._setDefaultAppearance({
2833 xref: parameters.xref,
2834 extra: `${this.borderStyle.width} w`,
2835 strokeColor,
2836 fillColor,
2837 strokeAlpha,
2838 fillAlpha,
2839 pointsCallback: (buffer, points) => {
2840 const x = points[2].x + this.borderStyle.width / 2;
2841 const y = points[2].y + this.borderStyle.width / 2;
2842 const width = points[3].x - points[2].x - this.borderStyle.width;
2843 const height = points[1].y - points[3].y - this.borderStyle.width;
2844 buffer.push(`${x} ${y} ${width} ${height} re`);
2845
2846 if (fillColor) {
2847 buffer.push("B");
2848 } else {
2849 buffer.push("S");
2850 }
2851
2852 return [points[0].x, points[1].x, points[3].y, points[1].y];
2853 }
2854 });
2855 }
2856 }
2857
2858}
2859
2860class CircleAnnotation extends MarkupAnnotation {
2861 constructor(parameters) {
2862 super(parameters);
2863 this.data.annotationType = _util.AnnotationType.CIRCLE;
2864
2865 if (!this.appearance) {
2866 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
2867 const strokeAlpha = parameters.dict.get("CA");
2868 let fillColor = null;
2869 let interiorColor = parameters.dict.getArray("IC");
2870
2871 if (interiorColor) {
2872 interiorColor = getRgbColor(interiorColor, null);
2873 fillColor = interiorColor ? Array.from(interiorColor).map(c => c / 255) : null;
2874 }
2875
2876 const fillAlpha = fillColor ? strokeAlpha : null;
2877
2878 if (this.borderStyle.width === 0 && !fillColor) {
2879 return;
2880 }
2881
2882 const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4));
2883
2884 this._setDefaultAppearance({
2885 xref: parameters.xref,
2886 extra: `${this.borderStyle.width} w`,
2887 strokeColor,
2888 fillColor,
2889 strokeAlpha,
2890 fillAlpha,
2891 pointsCallback: (buffer, points) => {
2892 const x0 = points[0].x + this.borderStyle.width / 2;
2893 const y0 = points[0].y - this.borderStyle.width / 2;
2894 const x1 = points[3].x - this.borderStyle.width / 2;
2895 const y1 = points[3].y + this.borderStyle.width / 2;
2896 const xMid = x0 + (x1 - x0) / 2;
2897 const yMid = y0 + (y1 - y0) / 2;
2898 const xOffset = (x1 - x0) / 2 * controlPointsDistance;
2899 const yOffset = (y1 - y0) / 2 * controlPointsDistance;
2900 buffer.push(`${xMid} ${y1} m`, `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`, `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`, `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`, `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`, "h");
2901
2902 if (fillColor) {
2903 buffer.push("B");
2904 } else {
2905 buffer.push("S");
2906 }
2907
2908 return [points[0].x, points[1].x, points[3].y, points[1].y];
2909 }
2910 });
2911 }
2912 }
2913
2914}
2915
2916class PolylineAnnotation extends MarkupAnnotation {
2917 constructor(parameters) {
2918 super(parameters);
2919 const {
2920 dict
2921 } = parameters;
2922 this.data.annotationType = _util.AnnotationType.POLYLINE;
2923 this.data.vertices = [];
2924
2925 if (!(this instanceof PolygonAnnotation)) {
2926 this.setLineEndings(dict.getArray("LE"));
2927 this.data.lineEndings = this.lineEndings;
2928 }
2929
2930 const rawVertices = dict.getArray("Vertices");
2931
2932 if (!Array.isArray(rawVertices)) {
2933 return;
2934 }
2935
2936 for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {
2937 this.data.vertices.push({
2938 x: rawVertices[i],
2939 y: rawVertices[i + 1]
2940 });
2941 }
2942
2943 if (!this.appearance) {
2944 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
2945 const strokeAlpha = dict.get("CA");
2946 const borderWidth = this.borderStyle.width || 1,
2947 borderAdjust = 2 * borderWidth;
2948 const bbox = [Infinity, Infinity, -Infinity, -Infinity];
2949
2950 for (const vertex of this.data.vertices) {
2951 bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
2952 bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
2953 bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
2954 bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
2955 }
2956
2957 if (!_util.Util.intersect(this.rectangle, bbox)) {
2958 this.rectangle = bbox;
2959 }
2960
2961 this._setDefaultAppearance({
2962 xref: parameters.xref,
2963 extra: `${borderWidth} w`,
2964 strokeColor,
2965 strokeAlpha,
2966 pointsCallback: (buffer, points) => {
2967 const vertices = this.data.vertices;
2968
2969 for (let i = 0, ii = vertices.length; i < ii; i++) {
2970 buffer.push(`${vertices[i].x} ${vertices[i].y} ${i === 0 ? "m" : "l"}`);
2971 }
2972
2973 buffer.push("S");
2974 return [points[0].x, points[1].x, points[3].y, points[1].y];
2975 }
2976 });
2977 }
2978 }
2979
2980}
2981
2982class PolygonAnnotation extends PolylineAnnotation {
2983 constructor(parameters) {
2984 super(parameters);
2985 this.data.annotationType = _util.AnnotationType.POLYGON;
2986 }
2987
2988}
2989
2990class CaretAnnotation extends MarkupAnnotation {
2991 constructor(parameters) {
2992 super(parameters);
2993 this.data.annotationType = _util.AnnotationType.CARET;
2994 }
2995
2996}
2997
2998class InkAnnotation extends MarkupAnnotation {
2999 constructor(parameters) {
3000 super(parameters);
3001 this.data.annotationType = _util.AnnotationType.INK;
3002 this.data.inkLists = [];
3003 const rawInkLists = parameters.dict.getArray("InkList");
3004
3005 if (!Array.isArray(rawInkLists)) {
3006 return;
3007 }
3008
3009 const xref = parameters.xref;
3010
3011 for (let i = 0, ii = rawInkLists.length; i < ii; ++i) {
3012 this.data.inkLists.push([]);
3013
3014 for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {
3015 this.data.inkLists[i].push({
3016 x: xref.fetchIfRef(rawInkLists[i][j]),
3017 y: xref.fetchIfRef(rawInkLists[i][j + 1])
3018 });
3019 }
3020 }
3021
3022 if (!this.appearance) {
3023 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
3024 const strokeAlpha = parameters.dict.get("CA");
3025 const borderWidth = this.borderStyle.width || 1,
3026 borderAdjust = 2 * borderWidth;
3027 const bbox = [Infinity, Infinity, -Infinity, -Infinity];
3028
3029 for (const inkLists of this.data.inkLists) {
3030 for (const vertex of inkLists) {
3031 bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
3032 bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
3033 bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
3034 bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
3035 }
3036 }
3037
3038 if (!_util.Util.intersect(this.rectangle, bbox)) {
3039 this.rectangle = bbox;
3040 }
3041
3042 this._setDefaultAppearance({
3043 xref: parameters.xref,
3044 extra: `${borderWidth} w`,
3045 strokeColor,
3046 strokeAlpha,
3047 pointsCallback: (buffer, points) => {
3048 for (const inkList of this.data.inkLists) {
3049 for (let i = 0, ii = inkList.length; i < ii; i++) {
3050 buffer.push(`${inkList[i].x} ${inkList[i].y} ${i === 0 ? "m" : "l"}`);
3051 }
3052
3053 buffer.push("S");
3054 }
3055
3056 return [points[0].x, points[1].x, points[3].y, points[1].y];
3057 }
3058 });
3059 }
3060 }
3061
3062 static createNewDict(annotation, xref, {
3063 apRef,
3064 ap
3065 }) {
3066 const {
3067 paths,
3068 rect,
3069 rotation
3070 } = annotation;
3071 const ink = new _primitives.Dict(xref);
3072 ink.set("Type", _primitives.Name.get("Annot"));
3073 ink.set("Subtype", _primitives.Name.get("Ink"));
3074 ink.set("CreationDate", `D:${(0, _util.getModificationDate)()}`);
3075 ink.set("Rect", rect);
3076 ink.set("InkList", paths.map(p => p.points));
3077 ink.set("F", 4);
3078 ink.set("Border", [0, 0, 0]);
3079 ink.set("Rotate", rotation);
3080 const n = new _primitives.Dict(xref);
3081 ink.set("AP", n);
3082
3083 if (apRef) {
3084 n.set("N", apRef);
3085 } else {
3086 n.set("N", ap);
3087 }
3088
3089 return ink;
3090 }
3091
3092 static async createNewAppearanceStream(annotation, xref, params) {
3093 const {
3094 color,
3095 rect,
3096 rotation,
3097 paths,
3098 thickness,
3099 opacity
3100 } = annotation;
3101 const [x1, y1, x2, y2] = rect;
3102 let w = x2 - x1;
3103 let h = y2 - y1;
3104
3105 if (rotation % 180 !== 0) {
3106 [w, h] = [h, w];
3107 }
3108
3109 const appearanceBuffer = [`${thickness} w 1 J 1 j`, `${(0, _default_appearance.getPdfColor)(color, false)}`];
3110
3111 if (opacity !== 1) {
3112 appearanceBuffer.push("/R0 gs");
3113 }
3114
3115 const buffer = [];
3116
3117 for (const {
3118 bezier
3119 } of paths) {
3120 buffer.length = 0;
3121 buffer.push(`${(0, _core_utils.numberToString)(bezier[0])} ${(0, _core_utils.numberToString)(bezier[1])} m`);
3122
3123 for (let i = 2, ii = bezier.length; i < ii; i += 6) {
3124 const curve = bezier.slice(i, i + 6).map(_core_utils.numberToString).join(" ");
3125 buffer.push(`${curve} c`);
3126 }
3127
3128 buffer.push("S");
3129 appearanceBuffer.push(buffer.join("\n"));
3130 }
3131
3132 const appearance = appearanceBuffer.join("\n");
3133 const appearanceStreamDict = new _primitives.Dict(xref);
3134 appearanceStreamDict.set("FormType", 1);
3135 appearanceStreamDict.set("Subtype", _primitives.Name.get("Form"));
3136 appearanceStreamDict.set("Type", _primitives.Name.get("XObject"));
3137 appearanceStreamDict.set("BBox", [0, 0, w, h]);
3138 appearanceStreamDict.set("Length", appearance.length);
3139
3140 if (rotation) {
3141 const matrix = WidgetAnnotation._getRotationMatrix(rotation, w, h);
3142
3143 appearanceStreamDict.set("Matrix", matrix);
3144 }
3145
3146 if (opacity !== 1) {
3147 const resources = new _primitives.Dict(xref);
3148 const extGState = new _primitives.Dict(xref);
3149 const r0 = new _primitives.Dict(xref);
3150 r0.set("CA", opacity);
3151 r0.set("Type", _primitives.Name.get("ExtGState"));
3152 extGState.set("R0", r0);
3153 resources.set("ExtGState", extGState);
3154 appearanceStreamDict.set("Resources", resources);
3155 }
3156
3157 const ap = new _stream.StringStream(appearance);
3158 ap.dict = appearanceStreamDict;
3159 return ap;
3160 }
3161
3162}
3163
3164class HighlightAnnotation extends MarkupAnnotation {
3165 constructor(parameters) {
3166 super(parameters);
3167 this.data.annotationType = _util.AnnotationType.HIGHLIGHT;
3168 const quadPoints = this.data.quadPoints = getQuadPoints(parameters.dict, null);
3169
3170 if (quadPoints) {
3171 const resources = this.appearance && this.appearance.dict.get("Resources");
3172
3173 if (!this.appearance || !(resources && resources.has("ExtGState"))) {
3174 if (this.appearance) {
3175 (0, _util.warn)("HighlightAnnotation - ignoring built-in appearance stream.");
3176 }
3177
3178 const fillColor = this.color ? Array.from(this.color).map(c => c / 255) : [1, 1, 0];
3179 const fillAlpha = parameters.dict.get("CA");
3180
3181 this._setDefaultAppearance({
3182 xref: parameters.xref,
3183 fillColor,
3184 blendMode: "Multiply",
3185 fillAlpha,
3186 pointsCallback: (buffer, points) => {
3187 buffer.push(`${points[0].x} ${points[0].y} m`, `${points[1].x} ${points[1].y} l`, `${points[3].x} ${points[3].y} l`, `${points[2].x} ${points[2].y} l`, "f");
3188 return [points[0].x, points[1].x, points[3].y, points[1].y];
3189 }
3190 });
3191 }
3192 } else {
3193 this.data.hasPopup = false;
3194 }
3195 }
3196
3197}
3198
3199class UnderlineAnnotation extends MarkupAnnotation {
3200 constructor(parameters) {
3201 super(parameters);
3202 this.data.annotationType = _util.AnnotationType.UNDERLINE;
3203 const quadPoints = this.data.quadPoints = getQuadPoints(parameters.dict, null);
3204
3205 if (quadPoints) {
3206 if (!this.appearance) {
3207 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
3208 const strokeAlpha = parameters.dict.get("CA");
3209
3210 this._setDefaultAppearance({
3211 xref: parameters.xref,
3212 extra: "[] 0 d 1 w",
3213 strokeColor,
3214 strokeAlpha,
3215 pointsCallback: (buffer, points) => {
3216 buffer.push(`${points[2].x} ${points[2].y} m`, `${points[3].x} ${points[3].y} l`, "S");
3217 return [points[0].x, points[1].x, points[3].y, points[1].y];
3218 }
3219 });
3220 }
3221 } else {
3222 this.data.hasPopup = false;
3223 }
3224 }
3225
3226}
3227
3228class SquigglyAnnotation extends MarkupAnnotation {
3229 constructor(parameters) {
3230 super(parameters);
3231 this.data.annotationType = _util.AnnotationType.SQUIGGLY;
3232 const quadPoints = this.data.quadPoints = getQuadPoints(parameters.dict, null);
3233
3234 if (quadPoints) {
3235 if (!this.appearance) {
3236 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
3237 const strokeAlpha = parameters.dict.get("CA");
3238
3239 this._setDefaultAppearance({
3240 xref: parameters.xref,
3241 extra: "[] 0 d 1 w",
3242 strokeColor,
3243 strokeAlpha,
3244 pointsCallback: (buffer, points) => {
3245 const dy = (points[0].y - points[2].y) / 6;
3246 let shift = dy;
3247 let x = points[2].x;
3248 const y = points[2].y;
3249 const xEnd = points[3].x;
3250 buffer.push(`${x} ${y + shift} m`);
3251
3252 do {
3253 x += 2;
3254 shift = shift === 0 ? dy : 0;
3255 buffer.push(`${x} ${y + shift} l`);
3256 } while (x < xEnd);
3257
3258 buffer.push("S");
3259 return [points[2].x, xEnd, y - 2 * dy, y + 2 * dy];
3260 }
3261 });
3262 }
3263 } else {
3264 this.data.hasPopup = false;
3265 }
3266 }
3267
3268}
3269
3270class StrikeOutAnnotation extends MarkupAnnotation {
3271 constructor(parameters) {
3272 super(parameters);
3273 this.data.annotationType = _util.AnnotationType.STRIKEOUT;
3274 const quadPoints = this.data.quadPoints = getQuadPoints(parameters.dict, null);
3275
3276 if (quadPoints) {
3277 if (!this.appearance) {
3278 const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
3279 const strokeAlpha = parameters.dict.get("CA");
3280
3281 this._setDefaultAppearance({
3282 xref: parameters.xref,
3283 extra: "[] 0 d 1 w",
3284 strokeColor,
3285 strokeAlpha,
3286 pointsCallback: (buffer, points) => {
3287 buffer.push(`${(points[0].x + points[2].x) / 2} ` + `${(points[0].y + points[2].y) / 2} m`, `${(points[1].x + points[3].x) / 2} ` + `${(points[1].y + points[3].y) / 2} l`, "S");
3288 return [points[0].x, points[1].x, points[3].y, points[1].y];
3289 }
3290 });
3291 }
3292 } else {
3293 this.data.hasPopup = false;
3294 }
3295 }
3296
3297}
3298
3299class StampAnnotation extends MarkupAnnotation {
3300 constructor(parameters) {
3301 super(parameters);
3302 this.data.annotationType = _util.AnnotationType.STAMP;
3303 }
3304
3305}
3306
3307class FileAttachmentAnnotation extends MarkupAnnotation {
3308 constructor(parameters) {
3309 super(parameters);
3310 const file = new _file_spec.FileSpec(parameters.dict.get("FS"), parameters.xref);
3311 this.data.annotationType = _util.AnnotationType.FILEATTACHMENT;
3312 this.data.file = file.serializable;
3313 }
3314
3315}
\No newline at end of file