@muhammara/native-core 7.0.0-alpha.1 → 7.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/index.js +45 -0
- package/lib/Recipe.js +163 -56
- package/lib/recipe/annotation.js +53 -4
- package/lib/recipe/appendPage.js +38 -16
- package/lib/recipe/graphics-state.js +20 -0
- package/lib/recipe/image.js +4 -0
- package/lib/recipe/info.js +71 -73
- package/lib/recipe/insertPage.js +20 -17
- package/lib/recipe/page.js +808 -11
- package/lib/recipe/parameters.js +2 -0
- package/lib/recipe/replaceText.js +66 -0
- package/lib/recipe/shapes.js +22 -1
- package/lib/recipe/split.js +2 -6
- package/lib/recipe/text.helper.js +1 -1
- package/lib/recipe/text.js +106 -8
- package/lib/recipe/utils.js +31 -27
- package/lib/recipe/vector-polygon.js +11 -0
- package/lib/recipe/vector.helper.js +15 -3
- package/lib/recipe/vector.js +108 -10
- package/lib/recipe-info.js +10 -0
- package/muhammara.d.ts +543 -89
- package/package.json +1 -1
package/lib/recipe/parameters.js
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function escapePDFLiteralString(value) {
|
|
2
|
+
return value.replace(/([\\()])/g, "\\$1");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Replace literal text-showing operands in a page's single content stream.
|
|
7
|
+
*
|
|
8
|
+
* @name replaceText
|
|
9
|
+
* @function
|
|
10
|
+
* @memberof Recipe#
|
|
11
|
+
* @param {string} text Text to replace.
|
|
12
|
+
* @param {string} replacement Replacement text.
|
|
13
|
+
* @param {number} pageNumber One-based page number.
|
|
14
|
+
*/
|
|
15
|
+
exports.replaceText = function replaceText(text, replacement, pageNumber) {
|
|
16
|
+
if (typeof text !== "string" || typeof replacement !== "string") {
|
|
17
|
+
throw new TypeError("replaceText expects text and replacement strings");
|
|
18
|
+
}
|
|
19
|
+
if (!Number.isInteger(pageNumber) || pageNumber < 1) {
|
|
20
|
+
throw new TypeError("replaceText expects a positive integer page number");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var pageIndex = pageNumber - 1;
|
|
24
|
+
var page = this.pdfReader.parsePage(pageIndex).getDictionary();
|
|
25
|
+
var contents = page.queryObject("Contents");
|
|
26
|
+
|
|
27
|
+
if (
|
|
28
|
+
!contents ||
|
|
29
|
+
contents.getType() !== this.muhammara.ePDFObjectIndirectObjectReference
|
|
30
|
+
) {
|
|
31
|
+
throw new Error("replaceText supports pages with one content stream");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var contentsObjectId = contents.toPDFIndirectObjectReference().getObjectID();
|
|
35
|
+
var stream = this.pdfReader.parseNewObject(contentsObjectId).toPDFStream();
|
|
36
|
+
var streamReader = this.pdfReader.startReadingFromStream(stream);
|
|
37
|
+
var chunks = [];
|
|
38
|
+
|
|
39
|
+
while (streamReader.notEnded()) {
|
|
40
|
+
chunks.push(Buffer.from(streamReader.read(65536)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
var source = Buffer.concat(chunks).toString("latin1");
|
|
44
|
+
var textPattern = new RegExp(
|
|
45
|
+
"\\(" + escapePDFLiteralString(text) + "\\)(\\s+Tj\\b)",
|
|
46
|
+
"g",
|
|
47
|
+
);
|
|
48
|
+
var replaced = source.replace(
|
|
49
|
+
textPattern,
|
|
50
|
+
"(" + escapePDFLiteralString(replacement) + ")$1",
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (replaced === source) {
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var objectsContext = this.writer.getObjectsContext();
|
|
58
|
+
var replacementObjectId = objectsContext.startNewIndirectObject();
|
|
59
|
+
var replacementStream = objectsContext.startUnfilteredPDFStream();
|
|
60
|
+
|
|
61
|
+
replacementStream.getWriteStream().write(Array.from(Buffer.from(replaced)));
|
|
62
|
+
objectsContext.endPDFStream(replacementStream).endIndirectObject();
|
|
63
|
+
this.writer.replaceObject(pageIndex, contentsObjectId, replacementObjectId);
|
|
64
|
+
this.modifiedSourcePages.add(pageNumber);
|
|
65
|
+
return this;
|
|
66
|
+
};
|
package/lib/recipe/shapes.js
CHANGED
|
@@ -155,7 +155,10 @@ exports.n_gon = function n_gon(cx, cy, radius, sides = 3, options = {}) {
|
|
|
155
155
|
|
|
156
156
|
const ngon = _n_gon(sides, cx, cy, radius, options);
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
const drawOptions = Object.assign({}, options, { link: undefined });
|
|
159
|
+
this.polygon(ngon, drawOptions);
|
|
160
|
+
if (options.link)
|
|
161
|
+
this.link(options.link, cx - radius, cy - radius, radius * 2, radius * 2);
|
|
159
162
|
|
|
160
163
|
if (options.rotationVertice) {
|
|
161
164
|
delete options["rotationOrigin"]; // cleanup n-gon generated point
|
|
@@ -220,6 +223,7 @@ exports.star = function star(cx, cy, radius, points = 5, options = {}) {
|
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
const starOptions = Object.assign({}, options);
|
|
226
|
+
delete starOptions.link;
|
|
223
227
|
|
|
224
228
|
if (odd(points)) {
|
|
225
229
|
starPath = _oddStar(_n_gon(points, cx, cy, radius, options));
|
|
@@ -265,6 +269,9 @@ exports.star = function star(cx, cy, radius, points = 5, options = {}) {
|
|
|
265
269
|
|
|
266
270
|
this.polygon(starPath, starOptions);
|
|
267
271
|
|
|
272
|
+
if (options.link)
|
|
273
|
+
this.link(options.link, cx - radius, cy - radius, radius * 2, radius * 2);
|
|
274
|
+
|
|
268
275
|
if (options.debug) {
|
|
269
276
|
this.circle(cx, cy, radius, { width: 1, stroke: "#00ff00" });
|
|
270
277
|
this.circle(cx, cy, 2, { fill: "#ff0000" });
|
|
@@ -375,6 +382,7 @@ exports.triangle = function triangle(x, y, traits, options = {}) {
|
|
|
375
382
|
let traitID = options.traitID || options.traitsID || "sss";
|
|
376
383
|
let position = options.position ? options.position.toLowerCase() : "default";
|
|
377
384
|
let triopts = Object.assign({}, options);
|
|
385
|
+
delete triopts.link;
|
|
378
386
|
|
|
379
387
|
if (traits.length !== 3) {
|
|
380
388
|
throw new Error(
|
|
@@ -433,6 +441,19 @@ exports.triangle = function triangle(x, y, traits, options = {}) {
|
|
|
433
441
|
}
|
|
434
442
|
|
|
435
443
|
this.polygon(trigon, triopts);
|
|
444
|
+
if (options.link) {
|
|
445
|
+
const xs = trigon.map((point) => point[0]);
|
|
446
|
+
const ys = trigon.map((point) => point[1]);
|
|
447
|
+
const left = Math.min(...xs);
|
|
448
|
+
const top = Math.min(...ys);
|
|
449
|
+
this.link(
|
|
450
|
+
options.link,
|
|
451
|
+
left,
|
|
452
|
+
top,
|
|
453
|
+
Math.max(...xs) - left,
|
|
454
|
+
Math.max(...ys) - top,
|
|
455
|
+
);
|
|
456
|
+
}
|
|
436
457
|
|
|
437
458
|
if (options.debug) {
|
|
438
459
|
let angle = triopts.rotation || 0;
|
package/lib/recipe/split.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const muhammara = require("../muhammara");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const
|
|
3
|
+
const utils = require("./utils");
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Split the pdf
|
|
@@ -16,11 +16,7 @@ exports.split = function split(outputDir = "", prefix) {
|
|
|
16
16
|
for (let i = 0; i < this.metadata.pages; i++) {
|
|
17
17
|
const newPdf = path.join(outputDir, `${prefix}-${i + 1}.pdf`);
|
|
18
18
|
const pdfWriter = muhammara.createWriter(newPdf);
|
|
19
|
-
|
|
20
|
-
pdfWriter,
|
|
21
|
-
this.pdfReader,
|
|
22
|
-
i,
|
|
23
|
-
);
|
|
19
|
+
utils.appendPDFPageFromPDFWithAnnotations(pdfWriter, this._getReader(), i);
|
|
24
20
|
pdfWriter.end();
|
|
25
21
|
}
|
|
26
22
|
return this;
|
package/lib/recipe/text.js
CHANGED
|
@@ -2,6 +2,7 @@ const LineBreaker = require("linebreak");
|
|
|
2
2
|
const { Word, Line, Column } = require("./text.helper");
|
|
3
3
|
const { htmlToTextObjects } = require("./htmlToTextObjects");
|
|
4
4
|
const { Color, xObjectForm } = require("./xObjectForm");
|
|
5
|
+
const { linkPdf } = require("./annotation");
|
|
5
6
|
const muhammara = require("../muhammara");
|
|
6
7
|
const { UsedFont } = require("../muhammara");
|
|
7
8
|
|
|
@@ -258,6 +259,8 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
258
259
|
return this;
|
|
259
260
|
}
|
|
260
261
|
options = _initOptions(this, x, y, options);
|
|
262
|
+
const linkX = this.x;
|
|
263
|
+
const linkY = this.y;
|
|
261
264
|
|
|
262
265
|
const targetAnnotations = options;
|
|
263
266
|
const originCoord = this._calibrateCoordinate(
|
|
@@ -273,6 +276,7 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
273
276
|
originCoord.ny,
|
|
274
277
|
);
|
|
275
278
|
pathOptions.html = options.html;
|
|
279
|
+
pathOptions.link = options.link;
|
|
276
280
|
pathOptions.hilite = options.hilite;
|
|
277
281
|
|
|
278
282
|
// save text state for continued text?
|
|
@@ -294,6 +298,7 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
294
298
|
textBox,
|
|
295
299
|
pathOptions,
|
|
296
300
|
);
|
|
301
|
+
const linkAnnotations = [];
|
|
297
302
|
|
|
298
303
|
if (!textBox.width) {
|
|
299
304
|
textBox.width = toWriteTextObjects[0].lineWidth;
|
|
@@ -316,7 +321,7 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
316
321
|
toWriteTextObjects = clippedText.textObjects;
|
|
317
322
|
textBox.textHeight = getTextBoxHeight(toWriteTextObjects);
|
|
318
323
|
|
|
319
|
-
if (clippedText.
|
|
324
|
+
if (clippedText.clipped) {
|
|
320
325
|
clipResult = {
|
|
321
326
|
remainder: clippedText.remainder,
|
|
322
327
|
linesWritten: clippedText.linesWritten,
|
|
@@ -641,6 +646,15 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
641
646
|
const x = next_x || getStartX(content.startX, content);
|
|
642
647
|
const y = currentY;
|
|
643
648
|
next_x = writeText(context, x, y, content);
|
|
649
|
+
if (content.writeOptions.link) {
|
|
650
|
+
linkAnnotations.push({
|
|
651
|
+
url: content.writeOptions.link,
|
|
652
|
+
left: x,
|
|
653
|
+
bottom: y,
|
|
654
|
+
width: next_x ? next_x - x : content.lineWidth,
|
|
655
|
+
height: content.lineHeight,
|
|
656
|
+
});
|
|
657
|
+
}
|
|
644
658
|
});
|
|
645
659
|
// The line offset from the last line in the
|
|
646
660
|
// group determines Y positioning for next line.
|
|
@@ -739,6 +753,15 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
739
753
|
const x = next_x || getStartX(content.startX, content);
|
|
740
754
|
const y = currentY;
|
|
741
755
|
next_x = writeText(context, x, y, content);
|
|
756
|
+
if (content.writeOptions.link) {
|
|
757
|
+
linkAnnotations.push({
|
|
758
|
+
url: content.writeOptions.link,
|
|
759
|
+
left: x,
|
|
760
|
+
bottom: y,
|
|
761
|
+
width: next_x ? next_x - x : content.lineWidth,
|
|
762
|
+
height: content.lineHeight,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
742
765
|
}
|
|
743
766
|
|
|
744
767
|
// Flush any left over text objects.
|
|
@@ -754,6 +777,16 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
754
777
|
}
|
|
755
778
|
}
|
|
756
779
|
|
|
780
|
+
linkAnnotations.forEach((annotation) => {
|
|
781
|
+
linkPdf(
|
|
782
|
+
this,
|
|
783
|
+
annotation.url,
|
|
784
|
+
annotation.left,
|
|
785
|
+
annotation.bottom,
|
|
786
|
+
annotation.width,
|
|
787
|
+
annotation.height,
|
|
788
|
+
);
|
|
789
|
+
});
|
|
757
790
|
return this;
|
|
758
791
|
};
|
|
759
792
|
|
|
@@ -798,6 +831,13 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
798
831
|
|
|
799
832
|
let firstLineHeight;
|
|
800
833
|
let toWriteTextObjects = [];
|
|
834
|
+
/** Reports whether a layout node contains content other than the break sentinel. */
|
|
835
|
+
const hasRenderableContent = (textObject) =>
|
|
836
|
+
(textObject.value !== undefined &&
|
|
837
|
+
textObject.value !== null &&
|
|
838
|
+
textObject.value !== "" &&
|
|
839
|
+
textObject.value !== "[@@DONOT_RENDER_THIS@@]") ||
|
|
840
|
+
textObject.childs?.some(hasRenderableContent);
|
|
801
841
|
|
|
802
842
|
const writeValue = (textObject) => {
|
|
803
843
|
textObject.lineID = textObject.lineID || Date.now() * Math.random();
|
|
@@ -832,6 +872,7 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
832
872
|
textObject.layer++;
|
|
833
873
|
|
|
834
874
|
textObject.currentIndex = 0;
|
|
875
|
+
let prependValue = textObject.prependValue;
|
|
835
876
|
|
|
836
877
|
textObject.childs.forEach((child) => {
|
|
837
878
|
if (textObject.tag == "ul") {
|
|
@@ -852,11 +893,17 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
852
893
|
child.layer = textObject.layer - 1;
|
|
853
894
|
}
|
|
854
895
|
}
|
|
855
|
-
if (
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
896
|
+
if (
|
|
897
|
+
prependValue &&
|
|
898
|
+
!["ol", "ul"].includes(child.tag) &&
|
|
899
|
+
hasRenderableContent(child)
|
|
900
|
+
) {
|
|
901
|
+
child.prependValue = prependValue;
|
|
902
|
+
prependValue = null;
|
|
903
|
+
textObject.indent =
|
|
904
|
+
textObject.tag == "li"
|
|
905
|
+
? 2 * textObject.layer
|
|
906
|
+
: textObject.indent || 2 * textObject.layer;
|
|
860
907
|
}
|
|
861
908
|
if (textObject.indent) {
|
|
862
909
|
child.indent = child.indent || textObject.indent;
|
|
@@ -866,6 +913,7 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
866
913
|
child.sizeRatios = [...textObject.sizeRatios, child.sizeRatio];
|
|
867
914
|
}
|
|
868
915
|
child.styles = Object.assign(child.styles, textObject.styles);
|
|
916
|
+
child.link = child.link || textObject.link;
|
|
869
917
|
|
|
870
918
|
child.isBold = textObject.isBold ? textObject.isBold : child.isBold;
|
|
871
919
|
child.isItalic = textObject.isItalic
|
|
@@ -887,7 +935,55 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
887
935
|
writeValue(textObject);
|
|
888
936
|
});
|
|
889
937
|
|
|
890
|
-
|
|
938
|
+
const normalizedTextObjects = [];
|
|
939
|
+
let pendingBreaks = [];
|
|
940
|
+
const replacementLineIDs = new Map();
|
|
941
|
+
/** Converts pending break sentinels into line state without rendering their text. */
|
|
942
|
+
const appendPendingBreaks = (nextTextObject) => {
|
|
943
|
+
const previous = normalizedTextObjects[normalizedTextObjects.length - 1];
|
|
944
|
+
if (previous) previous.lineComplete = true;
|
|
945
|
+
const blankBreaks =
|
|
946
|
+
previous && nextTextObject ? pendingBreaks.slice(1) : pendingBreaks;
|
|
947
|
+
blankBreaks.forEach((breakObject, index) => {
|
|
948
|
+
normalizedTextObjects.push({
|
|
949
|
+
...breakObject,
|
|
950
|
+
lineID: previous ? pendingBreaks[index].lineID : breakObject.lineID,
|
|
951
|
+
text: "",
|
|
952
|
+
lineComplete: true,
|
|
953
|
+
lineWidth: 0,
|
|
954
|
+
textWidth: 0,
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
if (nextTextObject && previous) {
|
|
958
|
+
replacementLineIDs.set(
|
|
959
|
+
nextTextObject.lineID,
|
|
960
|
+
pendingBreaks[pendingBreaks.length - 1].lineID,
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
pendingBreaks = [];
|
|
964
|
+
};
|
|
965
|
+
toWriteTextObjects.forEach((textObject, index, objects) => {
|
|
966
|
+
const sentinel = textObject.text.trim() == "[@@DONOT_RENDER_THIS@@]";
|
|
967
|
+
const beforeSentinel =
|
|
968
|
+
textObject.text.trim() == "" &&
|
|
969
|
+
objects[index + 1]?.text.trim() == "[@@DONOT_RENDER_THIS@@]";
|
|
970
|
+
if (beforeSentinel) return;
|
|
971
|
+
if (sentinel) {
|
|
972
|
+
pendingBreaks.push(textObject);
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (pendingBreaks.length) appendPendingBreaks(textObject);
|
|
976
|
+
textObject.lineID =
|
|
977
|
+
replacementLineIDs.get(textObject.lineID) || textObject.lineID;
|
|
978
|
+
normalizedTextObjects.push(textObject);
|
|
979
|
+
});
|
|
980
|
+
if (pendingBreaks.length) appendPendingBreaks();
|
|
981
|
+
toWriteTextObjects = normalizedTextObjects;
|
|
982
|
+
|
|
983
|
+
return {
|
|
984
|
+
toWriteTextObjects: toWriteTextObjects,
|
|
985
|
+
textHeight: getTextBoxHeight(toWriteTextObjects) || totalHeight,
|
|
986
|
+
};
|
|
891
987
|
};
|
|
892
988
|
|
|
893
989
|
function getTextBoxHeight(textObjs) {
|
|
@@ -949,6 +1045,7 @@ function clipTextToBox(textObjs, availableHeight) {
|
|
|
949
1045
|
return {
|
|
950
1046
|
textObjects: visibleLines.flat(),
|
|
951
1047
|
linesWritten,
|
|
1048
|
+
clipped: remainderLines.length > 0,
|
|
952
1049
|
// Preserve line boundaries so a remainder can be written into another box.
|
|
953
1050
|
remainder: remainderLines
|
|
954
1051
|
.map((currentLine) => currentLine.map((textObj) => textObj.text).join(""))
|
|
@@ -1195,6 +1292,7 @@ function makeTextObjects(self, textObject = {}, pathOptions, textBox = {}) {
|
|
|
1195
1292
|
? textBox.textAlign.split(" ")
|
|
1196
1293
|
: [];
|
|
1197
1294
|
const writeOptions = Object.assign({}, pathOptions, {
|
|
1295
|
+
link: textObject.link || pathOptions.link,
|
|
1198
1296
|
color: textObject.styles.color,
|
|
1199
1297
|
opacity: parseFloat(textObject.styles.opacity || pathOptions.opacity || 1),
|
|
1200
1298
|
underline: textObject.underline || pathOptions.underline,
|
|
@@ -1216,7 +1314,7 @@ function makeTextObjects(self, textObject = {}, pathOptions, textBox = {}) {
|
|
|
1216
1314
|
const indent = textObject.indent || 0;
|
|
1217
1315
|
|
|
1218
1316
|
const lineMaxWidth = textBox.width
|
|
1219
|
-
? textBox.width - textBox.paddingLeft - textBox.paddingRight
|
|
1317
|
+
? textBox.width - textBox.paddingLeft - textBox.paddingRight
|
|
1220
1318
|
: null;
|
|
1221
1319
|
let remainderWidth = lineMaxWidth;
|
|
1222
1320
|
let newLine;
|
package/lib/recipe/utils.js
CHANGED
|
@@ -14,28 +14,30 @@ function appendPDFPageFromPDFWithAnnotations(
|
|
|
14
14
|
pageNumber,
|
|
15
15
|
) {
|
|
16
16
|
const cpyCxt = pdfWriter.createPDFCopyingContext(sourcePDFPath);
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
try {
|
|
18
|
+
const cpyCxtParser = cpyCxt.getSourceDocumentParser();
|
|
19
|
+
const pageDictionary = cpyCxtParser.parsePageDictionary(pageNumber);
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
if (!pageDictionary.exists(ANNOTATION_PREFIX)) {
|
|
22
|
+
cpyCxt.appendPDFPageFromPDF(pageNumber);
|
|
23
|
+
} else {
|
|
24
|
+
let reffedObjects;
|
|
25
|
+
pdfWriter.getEvents().once("OnPageWrite", (params) => {
|
|
26
|
+
params.pageDictionaryContext.writeKey(ANNOTATION_PREFIX);
|
|
27
|
+
reffedObjects = cpyCxt.copyDirectObjectWithDeepCopy(
|
|
28
|
+
pageDictionary.queryObject(ANNOTATION_PREFIX),
|
|
29
|
+
);
|
|
30
|
+
});
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
cpyCxt.appendPDFPageFromPDF(pageNumber);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
if (reffedObjects && reffedObjects.length > 0) {
|
|
35
|
+
cpyCxt.copyNewObjectsForDirectObject(reffedObjects);
|
|
36
|
+
}
|
|
35
37
|
}
|
|
38
|
+
} finally {
|
|
39
|
+
cpyCxt.end();
|
|
36
40
|
}
|
|
37
|
-
|
|
38
|
-
cpyCxt.end();
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
@@ -51,21 +53,23 @@ function appendPDFPagesFromPDFWithAnnotations(
|
|
|
51
53
|
options = {},
|
|
52
54
|
) {
|
|
53
55
|
const cpyCxt = pdfWriter.createPDFCopyingContext(sourcePDFPath);
|
|
54
|
-
|
|
56
|
+
try {
|
|
57
|
+
const cpyCxtParser = cpyCxt.getSourceDocumentParser();
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
if (options.specificRanges && options.specificRanges.length) {
|
|
60
|
+
for (const [start, end] of options.specificRanges) {
|
|
61
|
+
for (let i = start; i <= end; ++i) {
|
|
62
|
+
appendPDFPageFromPDFWithAnnotations(pdfWriter, sourcePDFPath, i);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
for (let i = 0; i < cpyCxtParser.getPagesCount(); ++i) {
|
|
59
67
|
appendPDFPageFromPDFWithAnnotations(pdfWriter, sourcePDFPath, i);
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
appendPDFPageFromPDFWithAnnotations(pdfWriter, sourcePDFPath, i);
|
|
65
|
-
}
|
|
70
|
+
} finally {
|
|
71
|
+
cpyCxt.end();
|
|
66
72
|
}
|
|
67
|
-
|
|
68
|
-
cpyCxt.end();
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
exports.ANNOTATION_PREFIX = ANNOTATION_PREFIX;
|
|
@@ -122,6 +122,7 @@ exports.polygon = function polygon(coordinates = [], options = {}) {
|
|
|
122
122
|
height,
|
|
123
123
|
pathOptions,
|
|
124
124
|
(ctx, xObject) => {
|
|
125
|
+
ctx.gs(xObject.getGsName(pathOptions.strokeGsId));
|
|
125
126
|
ctx.w(pathOptions.width);
|
|
126
127
|
setPathOptions(ctx);
|
|
127
128
|
xObject.stroke(colorModel);
|
|
@@ -131,5 +132,15 @@ exports.polygon = function polygon(coordinates = [], options = {}) {
|
|
|
131
132
|
);
|
|
132
133
|
}
|
|
133
134
|
|
|
135
|
+
if (options.link) {
|
|
136
|
+
this.link(
|
|
137
|
+
options.link,
|
|
138
|
+
boundBox[0] - margin,
|
|
139
|
+
boundBox[1] - margin,
|
|
140
|
+
width,
|
|
141
|
+
height,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
134
145
|
return this;
|
|
135
146
|
};
|
|
@@ -30,9 +30,21 @@ exports._getPathOptions = function _getPathOptions(
|
|
|
30
30
|
width: 2,
|
|
31
31
|
align: options.align,
|
|
32
32
|
};
|
|
33
|
+
const lineStyle = this.current.lineStyle || {};
|
|
34
|
+
|
|
35
|
+
if (lineStyle.width !== undefined) pathOptions.width = lineStyle.width;
|
|
36
|
+
if (lineStyle.cap !== undefined) pathOptions.lineCap = lineStyle.cap;
|
|
37
|
+
if (lineStyle.join !== undefined) pathOptions.lineJoin = lineStyle.join;
|
|
38
|
+
if (lineStyle.miterLimit !== undefined)
|
|
39
|
+
pathOptions.miterLimit = lineStyle.miterLimit;
|
|
40
|
+
if (lineStyle.dash !== undefined) pathOptions.dash = lineStyle.dash;
|
|
41
|
+
if (lineStyle.dashPhase !== undefined)
|
|
42
|
+
pathOptions.dashPhase = lineStyle.dashPhase;
|
|
43
|
+
if (pathOptions.dash === undefined) pathOptions.dash = [];
|
|
44
|
+
if (pathOptions.dashPhase === undefined) pathOptions.dashPhase = 0;
|
|
33
45
|
|
|
34
46
|
if (options.opacity == void 0 || isNaN(options.opacity)) {
|
|
35
|
-
options.opacity = 1;
|
|
47
|
+
options.opacity = this.current.opacity ?? 1;
|
|
36
48
|
} else {
|
|
37
49
|
options.opacity =
|
|
38
50
|
options.opacity < 0 ? 0 : options.opacity > 1 ? 1 : options.opacity;
|
|
@@ -95,8 +107,8 @@ exports._getPathOptions = function _getPathOptions(
|
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
// Page 127 of PDF 1.7 specification
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
if (Array.isArray(options.dash)) pathOptions.dash = options.dash;
|
|
111
|
+
if (!isNaN(options.dashPhase)) pathOptions.dashPhase = options.dashPhase;
|
|
100
112
|
if (pathOptions.dash[0] == 0 && pathOptions.dash[1] == 0) {
|
|
101
113
|
pathOptions.dash = []; // no dash, solid unbroken line
|
|
102
114
|
pathOptions.dashPhase = 0;
|