@muhammara/native-core 7.0.0-beta.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/lib/Recipe.js +119 -57
- package/lib/recipe/annotation.js +53 -4
- package/lib/recipe/appendPage.js +22 -9
- package/lib/recipe/graphics-state.js +20 -0
- package/lib/recipe/image.js +4 -0
- package/lib/recipe/info.js +14 -19
- package/lib/recipe/insertPage.js +15 -10
- package/lib/recipe/page.js +808 -11
- package/lib/recipe/parameters.js +2 -0
- package/lib/recipe/replaceText.js +5 -3
- package/lib/recipe/shapes.js +22 -1
- package/lib/recipe/text.helper.js +1 -1
- package/lib/recipe/text.js +106 -8
- 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 +472 -84
- package/package.json +1 -1
package/lib/recipe/parameters.js
CHANGED
|
@@ -10,14 +10,15 @@ function escapePDFLiteralString(value) {
|
|
|
10
10
|
* @memberof Recipe#
|
|
11
11
|
* @param {string} text Text to replace.
|
|
12
12
|
* @param {string} replacement Replacement text.
|
|
13
|
-
* @param {number}
|
|
13
|
+
* @param {number} pageNumber One-based page number.
|
|
14
14
|
*/
|
|
15
15
|
exports.replaceText = function replaceText(text, replacement, pageNumber) {
|
|
16
|
-
pageNumber = pageNumber || 1;
|
|
17
|
-
|
|
18
16
|
if (typeof text !== "string" || typeof replacement !== "string") {
|
|
19
17
|
throw new TypeError("replaceText expects text and replacement strings");
|
|
20
18
|
}
|
|
19
|
+
if (!Number.isInteger(pageNumber) || pageNumber < 1) {
|
|
20
|
+
throw new TypeError("replaceText expects a positive integer page number");
|
|
21
|
+
}
|
|
21
22
|
|
|
22
23
|
var pageIndex = pageNumber - 1;
|
|
23
24
|
var page = this.pdfReader.parsePage(pageIndex).getDictionary();
|
|
@@ -60,5 +61,6 @@ exports.replaceText = function replaceText(text, replacement, pageNumber) {
|
|
|
60
61
|
replacementStream.getWriteStream().write(Array.from(Buffer.from(replaced)));
|
|
61
62
|
objectsContext.endPDFStream(replacementStream).endIndirectObject();
|
|
62
63
|
this.writer.replaceObject(pageIndex, contentsObjectId, replacementObjectId);
|
|
64
|
+
this.modifiedSourcePages.add(pageNumber);
|
|
63
65
|
return this;
|
|
64
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/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;
|
|
@@ -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;
|
package/lib/recipe/vector.js
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
// DecimalColor component values range from 0 to 255.
|
|
13
13
|
// PercentColor component values range from 1 to 100.
|
|
14
14
|
|
|
15
|
+
const { linkPdf } = require("./annotation");
|
|
16
|
+
|
|
15
17
|
/**
|
|
16
18
|
* Draw a circle
|
|
17
19
|
* @name circle
|
|
@@ -74,7 +76,8 @@ exports.circle = function circle(x, y, radius, options = {}) {
|
|
|
74
76
|
diameter,
|
|
75
77
|
diameter,
|
|
76
78
|
pathOptions,
|
|
77
|
-
(ctx) => {
|
|
79
|
+
(ctx, xObject) => {
|
|
80
|
+
ctx.gs(xObject.getGsName(pathOptions.strokeGsId));
|
|
78
81
|
ctx
|
|
79
82
|
.d(pathOptions.dash, pathOptions.dashPhase)
|
|
80
83
|
.drawCircle(
|
|
@@ -88,6 +91,8 @@ exports.circle = function circle(x, y, radius, options = {}) {
|
|
|
88
91
|
},
|
|
89
92
|
);
|
|
90
93
|
}
|
|
94
|
+
if (options.link)
|
|
95
|
+
this.link(options.link, x - radius, y - radius, diameter, diameter);
|
|
91
96
|
return this;
|
|
92
97
|
};
|
|
93
98
|
|
|
@@ -173,6 +178,7 @@ exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
|
173
178
|
height,
|
|
174
179
|
pathOptions,
|
|
175
180
|
(ctx, xObject) => {
|
|
181
|
+
ctx.gs(xObject.getGsName(pathOptions.strokeGsId));
|
|
176
182
|
// ... requires adjusting the internal drawing to accomodate line thickness.
|
|
177
183
|
const margin = pathOptions.width;
|
|
178
184
|
xObject.stroke(colorModel);
|
|
@@ -203,6 +209,11 @@ exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
|
203
209
|
},
|
|
204
210
|
);
|
|
205
211
|
}
|
|
212
|
+
if (options.link) {
|
|
213
|
+
if (options.useGivenCoords)
|
|
214
|
+
linkPdf(this, options.link, x, y, width, height);
|
|
215
|
+
else this.link(options.link, x, y, width, height);
|
|
216
|
+
}
|
|
206
217
|
|
|
207
218
|
return this;
|
|
208
219
|
};
|
|
@@ -362,6 +373,7 @@ exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
|
362
373
|
height,
|
|
363
374
|
pathOptions,
|
|
364
375
|
(ctx, xObject) => {
|
|
376
|
+
ctx.gs(xObject.getGsName(pathOptions.strokeGsId));
|
|
365
377
|
const margin = pathOptions.width / 2;
|
|
366
378
|
xObject.stroke(colorModel);
|
|
367
379
|
ctx.w(pathOptions.width).d(pathOptions.dash, pathOptions.dashPhase);
|
|
@@ -378,6 +390,7 @@ exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
|
378
390
|
},
|
|
379
391
|
);
|
|
380
392
|
}
|
|
393
|
+
if (options.link) this.link(options.link, cx - rx, cy - ry, width, height);
|
|
381
394
|
return this;
|
|
382
395
|
};
|
|
383
396
|
|
|
@@ -518,6 +531,7 @@ exports.arc = function arc(
|
|
|
518
531
|
diameter,
|
|
519
532
|
pathOptions,
|
|
520
533
|
(ctx, xObject) => {
|
|
534
|
+
ctx.gs(xObject.getGsName(pathOptions.strokeGsId));
|
|
521
535
|
const margin = pathOptions.width / 2;
|
|
522
536
|
xObject.stroke(colorModel);
|
|
523
537
|
ctx.w(pathOptions.width).d(pathOptions.dash, pathOptions.dashPhase);
|
|
@@ -532,34 +546,118 @@ exports.arc = function arc(
|
|
|
532
546
|
);
|
|
533
547
|
}
|
|
534
548
|
|
|
549
|
+
if (options.link)
|
|
550
|
+
this.link(options.link, x - radius, y - radius, diameter, diameter);
|
|
551
|
+
return this;
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Draw a closed sector of a circle.
|
|
556
|
+
* @name pie
|
|
557
|
+
* @function
|
|
558
|
+
* @memberof Recipe#
|
|
559
|
+
* @param {number} x - the x coordinate of the pie center point
|
|
560
|
+
* @param {number} y - the y coordinate of the pie center point
|
|
561
|
+
* @param {number} radius - the distance from the center point to the arc
|
|
562
|
+
* @param {number} [startAngle=0] - the start of the arc in degree units
|
|
563
|
+
* @param {number} [endAngle=360] - the end of the arc in degree units
|
|
564
|
+
* @param {Object} [options] - The path options.
|
|
565
|
+
* @returns {Recipe} The recipe instance.
|
|
566
|
+
*/
|
|
567
|
+
exports.pie = function pie(x, y, radius, startAngle, endAngle, options = {}) {
|
|
568
|
+
return this.arc(x, y, radius, startAngle, endAngle, {
|
|
569
|
+
...options,
|
|
570
|
+
sector: true,
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Set the line style for the current page content context.
|
|
576
|
+
* @name lineStyle
|
|
577
|
+
* @function
|
|
578
|
+
* @memberof Recipe#
|
|
579
|
+
* @param {Recipe.LineStyleOptions} [options] - The line style options.
|
|
580
|
+
* @param {number} [options.width] - The line width.
|
|
581
|
+
* @param {number} [options.lineWidth] - Alias for width.
|
|
582
|
+
* @param {number} [options.cap] - The PDF line cap style.
|
|
583
|
+
* @param {number} [options.join] - The PDF line join style.
|
|
584
|
+
* @param {number} [options.miterLimit] - The miter limit.
|
|
585
|
+
* @param {number[]} [options.dash] - The dash pattern.
|
|
586
|
+
* @param {number} [options.dashPhase] - The dash pattern phase.
|
|
587
|
+
* @returns {Recipe} The recipe instance.
|
|
588
|
+
*/
|
|
589
|
+
exports.lineStyle = function lineStyle(options = {}) {
|
|
590
|
+
this.current = this.current || {};
|
|
591
|
+
this.current.lineStyle = this.current.lineStyle || {};
|
|
592
|
+
|
|
593
|
+
if (options.width !== undefined || options.lineWidth !== undefined) {
|
|
594
|
+
const width = options.width ?? options.lineWidth;
|
|
595
|
+
this.current.lineStyle.width = width;
|
|
596
|
+
this.pageContext.w(width);
|
|
597
|
+
}
|
|
598
|
+
if (options.cap !== undefined) {
|
|
599
|
+
this.current.lineStyle.cap = options.cap;
|
|
600
|
+
this.pageContext.J(options.cap);
|
|
601
|
+
}
|
|
602
|
+
if (options.join !== undefined) {
|
|
603
|
+
this.current.lineStyle.join = options.join;
|
|
604
|
+
this.pageContext.j(options.join);
|
|
605
|
+
}
|
|
606
|
+
if (options.miterLimit !== undefined) {
|
|
607
|
+
this.current.lineStyle.miterLimit = options.miterLimit;
|
|
608
|
+
this.pageContext.M(options.miterLimit);
|
|
609
|
+
}
|
|
610
|
+
if (options.dash !== undefined || options.dashPhase !== undefined) {
|
|
611
|
+
const dash = options.dash ?? this.current.lineStyle.dash ?? [];
|
|
612
|
+
const dashPhase =
|
|
613
|
+
options.dashPhase ?? this.current.lineStyle.dashPhase ?? 0;
|
|
614
|
+
this.current.lineStyle.dash = dash;
|
|
615
|
+
this.current.lineStyle.dashPhase = dashPhase;
|
|
616
|
+
}
|
|
617
|
+
return this._setLineStyle(options);
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
exports._setLineStyle = function _setLineStyle(options = {}) {
|
|
621
|
+
if (options.width !== undefined || options.lineWidth !== undefined)
|
|
622
|
+
this.pageContext.w(options.width ?? options.lineWidth);
|
|
623
|
+
if (options.cap !== undefined) this.pageContext.J(options.cap);
|
|
624
|
+
if (options.join !== undefined) this.pageContext.j(options.join);
|
|
625
|
+
if (options.miterLimit !== undefined) this.pageContext.M(options.miterLimit);
|
|
626
|
+
if (options.dash !== undefined || options.dashPhase !== undefined)
|
|
627
|
+
this.pageContext.d(
|
|
628
|
+
options.dash ?? this.current.lineStyle.dash ?? [],
|
|
629
|
+
options.dashPhase ?? this.current.lineStyle.dashPhase ?? 0,
|
|
630
|
+
);
|
|
535
631
|
return this;
|
|
536
632
|
};
|
|
537
633
|
|
|
538
634
|
/**
|
|
539
635
|
* Set the line width.
|
|
540
636
|
*
|
|
541
|
-
* This compatibility method currently has no effect.
|
|
542
637
|
* @name lineWidth
|
|
543
638
|
* @function
|
|
544
639
|
* @memberof Recipe#
|
|
545
|
-
* @param {number} width - The
|
|
640
|
+
* @param {number} width - The line width.
|
|
546
641
|
* @returns {Recipe} The recipe instance.
|
|
547
642
|
*/
|
|
548
|
-
exports.lineWidth = function lineWidth() {
|
|
549
|
-
return this;
|
|
643
|
+
exports.lineWidth = function lineWidth(width) {
|
|
644
|
+
return this.lineStyle({ width });
|
|
550
645
|
};
|
|
551
646
|
|
|
552
647
|
/**
|
|
553
|
-
* Set
|
|
648
|
+
* Set fill and stroke opacity.
|
|
554
649
|
*
|
|
555
|
-
*
|
|
556
|
-
* @name fillOpacity
|
|
650
|
+
* @name opacity
|
|
557
651
|
* @function
|
|
558
652
|
* @memberof Recipe#
|
|
559
|
-
* @param {number}
|
|
653
|
+
* @param {number} value - The requested opacity from 0 (transparent) to 1 (opaque).
|
|
560
654
|
* @returns {Recipe} The recipe instance.
|
|
561
655
|
*/
|
|
562
|
-
exports.
|
|
656
|
+
exports.opacity = function opacity(value) {
|
|
657
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
658
|
+
throw new RangeError("Opacity must be a finite number between 0 and 1");
|
|
659
|
+
}
|
|
660
|
+
this.current.opacity = value;
|
|
563
661
|
return this;
|
|
564
662
|
};
|
|
565
663
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Metadata fields accepted by Recipe constructor options and info().
|
|
2
|
+
var recipeInfoKeys = Object.freeze({
|
|
3
|
+
author: "author",
|
|
4
|
+
title: "title",
|
|
5
|
+
subject: "subject",
|
|
6
|
+
keywords: "keywords",
|
|
7
|
+
});
|
|
8
|
+
var standardInfoKeys = Object.freeze(Object.values(recipeInfoKeys));
|
|
9
|
+
|
|
10
|
+
module.exports = { recipeInfoKeys, standardInfoKeys };
|