@muhammara/native-core 7.0.0-beta.4 → 7.0.0-rc.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 +57 -7
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/index.js +120 -1
- package/lib/PDFRStreamForBuffer.js +52 -7
- package/lib/PDFRStreamForFile.js +56 -7
- package/lib/PDFStreamForResponse.js +22 -5
- package/lib/PDFWStreamForBuffer.js +49 -10
- package/lib/PDFWStreamForFile.js +26 -5
- package/lib/Recipe.js +91 -38
- package/lib/content-stream.js +503 -0
- package/lib/css-colors.js +149 -0
- package/lib/font-text.js +1461 -0
- package/lib/glyph-list.js +1127 -0
- package/lib/recipe/annotation.js +464 -101
- package/lib/recipe/appendPage.js +2 -0
- package/lib/recipe/colors.js +132 -35
- package/lib/recipe/coordinate.js +82 -16
- package/lib/recipe/encrypt.js +32 -13
- package/lib/recipe/font.js +70 -25
- package/lib/recipe/graphics-state.js +3 -2
- package/lib/recipe/htmlToTextObjects.js +121 -24
- package/lib/recipe/image.js +385 -39
- package/lib/recipe/info.js +93 -22
- package/lib/recipe/insertPage.js +12 -3
- package/lib/recipe/overlay.js +4 -2
- package/lib/recipe/page-references.js +436 -0
- package/lib/recipe/page.js +363 -262
- package/lib/recipe/parameters.js +8 -1
- package/lib/recipe/replaceText.js +464 -30
- package/lib/recipe/shapes.js +418 -45
- package/lib/recipe/split.js +6 -2
- package/lib/recipe/table.js +41 -8
- package/lib/recipe/text.helper.js +172 -8
- package/lib/recipe/text.js +294 -56
- package/lib/recipe/utils.js +17 -6
- package/lib/recipe/vector-line.js +101 -39
- package/lib/recipe/vector-polygon.js +18 -6
- package/lib/recipe/vector.helper.js +156 -14
- package/lib/recipe/vector.js +156 -52
- package/lib/recipe/xObjectForm.js +73 -10
- package/lib/recipe-constants.js +373 -0
- package/lib/recipe-options.js +47 -0
- package/muhammara.d.ts +3501 -160
- package/package.json +2 -3
package/lib/recipe/vector.js
CHANGED
|
@@ -13,31 +13,38 @@
|
|
|
13
13
|
// PercentColor component values range from 1 to 100.
|
|
14
14
|
|
|
15
15
|
const { linkPdf } = require("./annotation");
|
|
16
|
+
const muhammara = require("../muhammara");
|
|
17
|
+
const { miterLimitOption } = require("../recipe-options");
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Draw a circle
|
|
19
21
|
* @name circle
|
|
20
22
|
* @function
|
|
21
23
|
* @memberof Recipe#
|
|
22
|
-
* @param {number} x - The coordinate x
|
|
23
|
-
* @param {number} y - The coordinate y
|
|
24
|
+
* @param {number|"center"} x - The coordinate x of the center
|
|
25
|
+
* @param {number|"center"} y - The coordinate y of the center
|
|
24
26
|
* @param {number} radius - The radius
|
|
25
27
|
* @param {Object} [options] - The options
|
|
26
28
|
* @param {string|number[]} [options.color] - HexColor, PercentColor or DecimalColor
|
|
27
29
|
* @param {string|number[]} [options.stroke] - HexColor, PercentColor or DecimalColor
|
|
28
|
-
* @param {string|number[]}[
|
|
30
|
+
* @param {string|number[]} [options.fill] - HexColor, PercentColor or DecimalColor
|
|
29
31
|
* @param {number} [options.lineWidth] - The line width
|
|
30
32
|
* @param {number} [options.opacity] - The opacity
|
|
31
33
|
* @param {number[]} [options.dash] - The dash style [number, number]
|
|
34
|
+
* @param {string} [options.link] - Make the circle's bounding square open this URL.
|
|
32
35
|
* @returns {Recipe} The recipe instance.
|
|
36
|
+
* @throws {TypeError} If no page is active.
|
|
33
37
|
*/
|
|
34
38
|
exports.circle = function circle(x, y, radius, options = {}) {
|
|
39
|
+
// null options act like omitted options.
|
|
40
|
+
if (options === null) options = {};
|
|
41
|
+
[x, y] = this._centrify(x, y);
|
|
35
42
|
const { nx, ny } = this._calibrateCoordinate(x, y);
|
|
36
43
|
const diameter = radius * 2;
|
|
37
44
|
|
|
38
45
|
if (options.fill) {
|
|
39
46
|
const pathOptions = this._getPathOptions(options, nx, ny);
|
|
40
|
-
pathOptions.type =
|
|
47
|
+
pathOptions.type = muhammara.DrawingPathType.FILL;
|
|
41
48
|
|
|
42
49
|
if (pathOptions.fill !== undefined) {
|
|
43
50
|
pathOptions.color = pathOptions.fill;
|
|
@@ -52,15 +59,20 @@ exports.circle = function circle(x, y, radius, options = {}) {
|
|
|
52
59
|
diameter,
|
|
53
60
|
pathOptions,
|
|
54
61
|
(ctx, xObject) => {
|
|
55
|
-
ctx
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
ctx.gs(xObject.getGsName(pathOptions.fillGsId));
|
|
63
|
+
this._setSeparationColor(xObject, pathOptions.fillModel, false);
|
|
64
|
+
ctx.drawCircle(
|
|
65
|
+
radius,
|
|
66
|
+
radius,
|
|
67
|
+
radius,
|
|
68
|
+
this._devicePathOptions(pathOptions),
|
|
69
|
+
);
|
|
58
70
|
},
|
|
59
71
|
);
|
|
60
72
|
}
|
|
61
73
|
if (options.stroke || options.color || !options.fill) {
|
|
62
74
|
const pathOptions = this._getPathOptions(options);
|
|
63
|
-
pathOptions.type =
|
|
75
|
+
pathOptions.type = muhammara.DrawingPathType.STROKE;
|
|
64
76
|
|
|
65
77
|
if (pathOptions.stroke !== undefined) {
|
|
66
78
|
pathOptions.color = pathOptions.stroke;
|
|
@@ -78,13 +90,18 @@ exports.circle = function circle(x, y, radius, options = {}) {
|
|
|
78
90
|
pathOptions,
|
|
79
91
|
(ctx, xObject) => {
|
|
80
92
|
ctx.gs(xObject.getGsName(pathOptions.strokeGsId));
|
|
93
|
+
this._setSeparationColor(
|
|
94
|
+
xObject,
|
|
95
|
+
pathOptions.strokeModel || pathOptions.colorModel,
|
|
96
|
+
true,
|
|
97
|
+
);
|
|
81
98
|
ctx
|
|
82
99
|
.d(pathOptions.dash, pathOptions.dashPhase)
|
|
83
100
|
.drawCircle(
|
|
84
101
|
radius,
|
|
85
102
|
radius,
|
|
86
|
-
radius - pathOptions.width / 2,
|
|
87
|
-
pathOptions,
|
|
103
|
+
Math.max(0, radius - pathOptions.width / 2),
|
|
104
|
+
this._devicePathOptions(pathOptions),
|
|
88
105
|
);
|
|
89
106
|
|
|
90
107
|
// ... requires adjusting the internal drawing to accomodate line thickness.
|
|
@@ -101,8 +118,8 @@ exports.circle = function circle(x, y, radius, options = {}) {
|
|
|
101
118
|
* @name rectangle
|
|
102
119
|
* @function
|
|
103
120
|
* @memberof Recipe#
|
|
104
|
-
* @param {number} x - The coordinate x
|
|
105
|
-
* @param {number} y - The coordinate y
|
|
121
|
+
* @param {number|"center"} x - The coordinate x of the top-left corner
|
|
122
|
+
* @param {number|"center"} y - The coordinate y of the top-left corner
|
|
106
123
|
* @param {number} width - The width
|
|
107
124
|
* @param {number} height - The height
|
|
108
125
|
* @param {Object} [options] - The options
|
|
@@ -118,9 +135,14 @@ exports.circle = function circle(x, y, radius, options = {}) {
|
|
|
118
135
|
* When a one to four number array can be used to give specific sizees to each corner.
|
|
119
136
|
* The numbering starts from the top, left corner, and goes clockwise around the text box.
|
|
120
137
|
* Missing values in the array are filled in by opposite corner values.
|
|
138
|
+
* @param {string} [options.link] - Make the rectangle open this URL.
|
|
139
|
+
* @param {boolean} [options.useGivenCoords] - Take x and y as PDF coordinates of the bottom-left corner.
|
|
121
140
|
* @returns {Recipe} The recipe instance.
|
|
141
|
+
* @throws {TypeError} If no page is active.
|
|
122
142
|
*/
|
|
123
143
|
exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
144
|
+
// null options act like omitted options.
|
|
145
|
+
if (options === null) options = {};
|
|
124
146
|
const { nx, ny } = options.useGivenCoords
|
|
125
147
|
? { nx: x, ny: y }
|
|
126
148
|
: this._calibrateCoordinate(x, y, 0, -height);
|
|
@@ -130,7 +152,7 @@ exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
|
130
152
|
pathOptions.useGivenCoords = options.useGivenCoords;
|
|
131
153
|
|
|
132
154
|
if (options.fill) {
|
|
133
|
-
pathOptions.type =
|
|
155
|
+
pathOptions.type = muhammara.DrawingPathType.FILL;
|
|
134
156
|
|
|
135
157
|
if (pathOptions.fill !== undefined) {
|
|
136
158
|
pathOptions.color = pathOptions.fill;
|
|
@@ -153,20 +175,28 @@ exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
|
153
175
|
drawRoundedRectangle(ctx, 0, 0, width, height, options.borderRadius);
|
|
154
176
|
ctx.f();
|
|
155
177
|
} else {
|
|
156
|
-
ctx.drawRectangle(
|
|
178
|
+
ctx.drawRectangle(
|
|
179
|
+
0,
|
|
180
|
+
0,
|
|
181
|
+
width,
|
|
182
|
+
height,
|
|
183
|
+
this._devicePathOptions(pathOptions),
|
|
184
|
+
);
|
|
157
185
|
}
|
|
158
186
|
},
|
|
159
187
|
);
|
|
160
188
|
}
|
|
161
189
|
|
|
162
190
|
if (options.stroke || options.color || !options.fill) {
|
|
163
|
-
pathOptions.type =
|
|
191
|
+
pathOptions.type = muhammara.DrawingPathType.STROKE;
|
|
164
192
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
pathOptions.
|
|
168
|
-
|
|
169
|
-
|
|
193
|
+
// The fill above may have replaced the color; stroke with `color`.
|
|
194
|
+
colorModel =
|
|
195
|
+
pathOptions.stroke !== undefined
|
|
196
|
+
? pathOptions.strokeModel
|
|
197
|
+
: pathOptions.colorModel;
|
|
198
|
+
pathOptions.color = colorModel.color;
|
|
199
|
+
pathOptions.colorspace = colorModel.colorspace;
|
|
170
200
|
|
|
171
201
|
// To honor the given width and height of the rectangle ...
|
|
172
202
|
|
|
@@ -186,24 +216,26 @@ exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
|
186
216
|
if (options.borderRadius) {
|
|
187
217
|
ctx.w(pathOptions.width).d(pathOptions.dash, pathOptions.dashPhase);
|
|
188
218
|
|
|
219
|
+
const inset = Math.min(margin / 2, width / 2, height / 2);
|
|
189
220
|
drawRoundedRectangle(
|
|
190
221
|
ctx,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
width -
|
|
194
|
-
height -
|
|
222
|
+
inset,
|
|
223
|
+
inset,
|
|
224
|
+
width - inset * 2,
|
|
225
|
+
height - inset * 2,
|
|
195
226
|
options.borderRadius,
|
|
227
|
+
inset,
|
|
196
228
|
);
|
|
197
229
|
ctx.S();
|
|
198
230
|
} else {
|
|
199
231
|
ctx
|
|
200
232
|
.d(pathOptions.dash, pathOptions.dashPhase)
|
|
201
233
|
.drawRectangle(
|
|
202
|
-
margin / 2,
|
|
203
|
-
margin / 2,
|
|
204
|
-
width - margin,
|
|
205
|
-
height - margin,
|
|
206
|
-
pathOptions,
|
|
234
|
+
Math.min(margin / 2, width / 2),
|
|
235
|
+
Math.min(margin / 2, height / 2),
|
|
236
|
+
Math.max(0, width - margin),
|
|
237
|
+
Math.max(0, height - margin),
|
|
238
|
+
this._devicePathOptions(pathOptions),
|
|
207
239
|
);
|
|
208
240
|
}
|
|
209
241
|
},
|
|
@@ -218,7 +250,28 @@ exports.rectangle = function rectangle(x, y, width, height, options = {}) {
|
|
|
218
250
|
return this;
|
|
219
251
|
};
|
|
220
252
|
|
|
221
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Append a rectangle path with rounded corners.
|
|
255
|
+
* @private
|
|
256
|
+
* @param {Object} ctx - The content context.
|
|
257
|
+
* @param {number} left - The left edge.
|
|
258
|
+
* @param {number} bottom - The bottom edge.
|
|
259
|
+
* @param {number} width - The width.
|
|
260
|
+
* @param {number} height - The height.
|
|
261
|
+
* @param {number|number[]} radii - One radius, or up to four clockwise from
|
|
262
|
+
* the top-left corner; missing ones come from the opposite corner.
|
|
263
|
+
* @param {number} [inset=0] - How far to move the path inside the rectangle.
|
|
264
|
+
* @returns {void}
|
|
265
|
+
*/
|
|
266
|
+
function drawRoundedRectangle(
|
|
267
|
+
ctx,
|
|
268
|
+
left,
|
|
269
|
+
bottom,
|
|
270
|
+
width,
|
|
271
|
+
height,
|
|
272
|
+
radii,
|
|
273
|
+
inset = 0,
|
|
274
|
+
) {
|
|
222
275
|
let radius = [];
|
|
223
276
|
|
|
224
277
|
// populate radius array accordingly.
|
|
@@ -244,6 +297,8 @@ function drawRoundedRectangle(ctx, left, bottom, width, height, radii) {
|
|
|
244
297
|
break;
|
|
245
298
|
}
|
|
246
299
|
}
|
|
300
|
+
// Keep inset corners concentric with the nominal corners.
|
|
301
|
+
radius = radius.map((value) => Math.max(0, value - inset));
|
|
247
302
|
const K = 0.551784;
|
|
248
303
|
const right = left + width;
|
|
249
304
|
const top = bottom + height;
|
|
@@ -296,8 +351,8 @@ function drawRoundedRectangle(ctx, left, bottom, width, height, radii) {
|
|
|
296
351
|
* @name ellipse
|
|
297
352
|
* @function
|
|
298
353
|
* @memberof Recipe#
|
|
299
|
-
* @param {number} cx x-coordinate of center point of ellipse
|
|
300
|
-
* @param {number} cy y-coordinate of center point of ellipse
|
|
354
|
+
* @param {number|"center"} cx x-coordinate of center point of ellipse
|
|
355
|
+
* @param {number|"center"} cy y-coordinate of center point of ellipse
|
|
301
356
|
* @param {number} rx radius length from the center point along x-axis
|
|
302
357
|
* @param {number} ry radius length from the center point along y-axis
|
|
303
358
|
* @param {Object} [options]
|
|
@@ -310,8 +365,12 @@ function drawRoundedRectangle(ctx, left, bottom, width, height, radii) {
|
|
|
310
365
|
* @param {number} [options.rotation] - Accept: +/- 0 through 360. Default: 0
|
|
311
366
|
* @param {number[]} [options.rotationOrigin] - [originX, originY] Default: x, y
|
|
312
367
|
* @returns {Recipe} The recipe instance.
|
|
368
|
+
* @throws {TypeError} If no page is active.
|
|
313
369
|
*/
|
|
314
370
|
exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
371
|
+
// null options act like omitted options.
|
|
372
|
+
if (options === null) options = {};
|
|
373
|
+
[cx, cy] = this._centrify(cx, cy);
|
|
315
374
|
const { nx, ny } = this._calibrateCoordinate(cx, cy);
|
|
316
375
|
|
|
317
376
|
const pathOptions = this._getPathOptions(options, nx, ny);
|
|
@@ -322,8 +381,8 @@ exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
|
322
381
|
|
|
323
382
|
const drawEllipse = (ctx, x, y, w, h) => {
|
|
324
383
|
const magic = 0.551784; // from https://www.tinaja.com/glib/ellipse4.pdf
|
|
325
|
-
const ox =
|
|
326
|
-
const oy =
|
|
384
|
+
const ox = (w / 2) * magic; // control point offset horizontal
|
|
385
|
+
const oy = (h / 2) * magic; // control point offset vertical
|
|
327
386
|
const xe = x + w; // x-end, opposite corner from origin
|
|
328
387
|
const ye = y + h; // y-end, opposite corner from origin
|
|
329
388
|
const xm = rx; // x-middle of enclosing rectangle
|
|
@@ -359,9 +418,11 @@ exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
|
359
418
|
}
|
|
360
419
|
|
|
361
420
|
if (options.stroke || options.color || !options.fill) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
421
|
+
// The fill above may have replaced colorModel; stroke with `color`.
|
|
422
|
+
colorModel =
|
|
423
|
+
pathOptions.stroke !== undefined
|
|
424
|
+
? pathOptions.strokeModel
|
|
425
|
+
: pathOptions.colorModel;
|
|
365
426
|
|
|
366
427
|
// To honor the given width and height of the enclosing rectangle ...
|
|
367
428
|
|
|
@@ -381,10 +442,10 @@ exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
|
381
442
|
// ... requires adjusting the internal drawing to accomodate line thickness.
|
|
382
443
|
drawEllipse(
|
|
383
444
|
ctx,
|
|
384
|
-
margin,
|
|
385
|
-
margin,
|
|
386
|
-
width - pathOptions.width,
|
|
387
|
-
height - pathOptions.width,
|
|
445
|
+
Math.min(margin, rx),
|
|
446
|
+
Math.min(margin, ry),
|
|
447
|
+
Math.max(0, width - pathOptions.width),
|
|
448
|
+
Math.max(0, height - pathOptions.width),
|
|
388
449
|
);
|
|
389
450
|
ctx.S();
|
|
390
451
|
},
|
|
@@ -394,6 +455,18 @@ exports.ellipse = function ellipse(cx, cy, rx, ry, options = {}) {
|
|
|
394
455
|
return this;
|
|
395
456
|
};
|
|
396
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Append a circular arc path built from Bézier segments.
|
|
460
|
+
* @private
|
|
461
|
+
* @param {Object} ctx - The content context.
|
|
462
|
+
* @param {number} x - The center x.
|
|
463
|
+
* @param {number} y - The center y.
|
|
464
|
+
* @param {number} radius - The radius.
|
|
465
|
+
* @param {number} startAngle - The start angle in degrees.
|
|
466
|
+
* @param {number} endAngle - The end angle in degrees.
|
|
467
|
+
* @param {boolean} [fromCenter=false] - Start the path at the center, for a sector.
|
|
468
|
+
* @returns {void}
|
|
469
|
+
*/
|
|
397
470
|
function drawArc(ctx, x, y, radius, startAngle, endAngle, fromCenter = false) {
|
|
398
471
|
const TWO_PI = 2.0 * Math.PI;
|
|
399
472
|
const HALF_PI = 0.5 * Math.PI;
|
|
@@ -459,8 +532,8 @@ function drawArc(ctx, x, y, radius, startAngle, endAngle, fromCenter = false) {
|
|
|
459
532
|
* @name arc
|
|
460
533
|
* @function
|
|
461
534
|
* @memberof Recipe#
|
|
462
|
-
* @param {number} x - the x coordinate of the arc center point
|
|
463
|
-
* @param {number} y - the y coordinate of the arc center point
|
|
535
|
+
* @param {number|"center"} x - the x coordinate of the arc center point
|
|
536
|
+
* @param {number|"center"} y - the y coordinate of the arc center point
|
|
464
537
|
* @param {number} radius - the distance from the given x,y coordinates from which to produce the arc
|
|
465
538
|
* @param {number} [startAngle=0] - the start of the arc in degree units +/- 0 through 360. Positive values go clockwise, Negative values, counterclockwise.
|
|
466
539
|
* @param {number} [endAngle=360] - the end of the arc in degree units +/- 0 through 360. Positive values go clockwise, Negative values, counterclockwise.
|
|
@@ -474,6 +547,7 @@ function drawArc(ctx, x, y, radius, startAngle, endAngle, fromCenter = false) {
|
|
|
474
547
|
* @param {number} [options.rotation=0] - Accept: +/- 0 through 360.
|
|
475
548
|
* @param {number[]} [options.rotationOrigin] - [originX, originY] Default: x, y
|
|
476
549
|
* @returns {Recipe} The recipe instance.
|
|
550
|
+
* @throws {TypeError} If no page is active.
|
|
477
551
|
*/
|
|
478
552
|
exports.arc = function arc(
|
|
479
553
|
x,
|
|
@@ -483,6 +557,9 @@ exports.arc = function arc(
|
|
|
483
557
|
endAngle = 360,
|
|
484
558
|
options = {},
|
|
485
559
|
) {
|
|
560
|
+
// null options act like omitted options.
|
|
561
|
+
if (options === null) options = {};
|
|
562
|
+
[x, y] = this._centrify(x, y);
|
|
486
563
|
const { nx, ny } = this._calibrateCoordinate(x, y);
|
|
487
564
|
const diameter = radius * 2;
|
|
488
565
|
const pathOptions = this._getPathOptions(options, nx, ny);
|
|
@@ -517,9 +594,11 @@ exports.arc = function arc(
|
|
|
517
594
|
}
|
|
518
595
|
|
|
519
596
|
if (options.stroke || options.color || !options.fill) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
597
|
+
// The fill above may have replaced colorModel; stroke with `color`.
|
|
598
|
+
colorModel =
|
|
599
|
+
pathOptions.stroke !== undefined
|
|
600
|
+
? pathOptions.strokeModel
|
|
601
|
+
: pathOptions.colorModel;
|
|
523
602
|
|
|
524
603
|
// To honor the given width and height of the enclosing rectangle ...
|
|
525
604
|
|
|
@@ -537,7 +616,15 @@ exports.arc = function arc(
|
|
|
537
616
|
ctx.w(pathOptions.width).d(pathOptions.dash, pathOptions.dashPhase);
|
|
538
617
|
|
|
539
618
|
// ... requires adjusting the internal drawing to accomodate line thickness.
|
|
540
|
-
drawArc(
|
|
619
|
+
drawArc(
|
|
620
|
+
ctx,
|
|
621
|
+
radius,
|
|
622
|
+
radius,
|
|
623
|
+
Math.max(0, radius - margin),
|
|
624
|
+
sAng,
|
|
625
|
+
eAng,
|
|
626
|
+
sector,
|
|
627
|
+
);
|
|
541
628
|
if (sector) {
|
|
542
629
|
ctx.h();
|
|
543
630
|
} // close off path to create a circle sector.
|
|
@@ -556,13 +643,14 @@ exports.arc = function arc(
|
|
|
556
643
|
* @name pie
|
|
557
644
|
* @function
|
|
558
645
|
* @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
|
|
646
|
+
* @param {number|"center"} x - the x coordinate of the pie center point
|
|
647
|
+
* @param {number|"center"} y - the y coordinate of the pie center point
|
|
561
648
|
* @param {number} radius - the distance from the center point to the arc
|
|
562
649
|
* @param {number} [startAngle=0] - the start of the arc in degree units
|
|
563
650
|
* @param {number} [endAngle=360] - the end of the arc in degree units
|
|
564
651
|
* @param {Object} [options] - The path options.
|
|
565
652
|
* @returns {Recipe} The recipe instance.
|
|
653
|
+
* @throws {TypeError} If no page is active.
|
|
566
654
|
*/
|
|
567
655
|
exports.pie = function pie(x, y, radius, startAngle, endAngle, options = {}) {
|
|
568
656
|
return this.arc(x, y, radius, startAngle, endAngle, {
|
|
@@ -579,14 +667,21 @@ exports.pie = function pie(x, y, radius, startAngle, endAngle, options = {}) {
|
|
|
579
667
|
* @param {Recipe.LineStyleOptions} [options] - The line style options.
|
|
580
668
|
* @param {number} [options.width] - The line width.
|
|
581
669
|
* @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.
|
|
670
|
+
* @param {number} [options.cap] - The PDF line cap style, a `LineCapStyle` value.
|
|
671
|
+
* @param {number} [options.join] - The PDF line join style: 0 miter, 1 round, 2 bevel.
|
|
672
|
+
* @param {number} [options.miterLimit] - The miter limit, at least 1.
|
|
585
673
|
* @param {number[]} [options.dash] - The dash pattern.
|
|
586
674
|
* @param {number} [options.dashPhase] - The dash pattern phase.
|
|
587
675
|
* @returns {Recipe} The recipe instance.
|
|
676
|
+
* @throws {TypeError} If no page is active.
|
|
677
|
+
* @throws {RangeError} If `miterLimit` is not a number of at least 1.
|
|
588
678
|
*/
|
|
589
679
|
exports.lineStyle = function lineStyle(options = {}) {
|
|
680
|
+
// null options act like omitted options.
|
|
681
|
+
if (options === null) options = {};
|
|
682
|
+
// Validate before anything is written.
|
|
683
|
+
const miterLimit = miterLimitOption(options.miterLimit);
|
|
684
|
+
if (miterLimit !== undefined) options = { ...options, miterLimit };
|
|
590
685
|
this.current = this.current || {};
|
|
591
686
|
this.current.lineStyle = this.current.lineStyle || {};
|
|
592
687
|
|
|
@@ -617,6 +712,13 @@ exports.lineStyle = function lineStyle(options = {}) {
|
|
|
617
712
|
return this._setLineStyle(options);
|
|
618
713
|
};
|
|
619
714
|
|
|
715
|
+
/**
|
|
716
|
+
* Write the given line style options to the page content context.
|
|
717
|
+
* @private
|
|
718
|
+
* @param {Object} [options] - The lineStyle() options.
|
|
719
|
+
* @returns {Recipe} The recipe instance.
|
|
720
|
+
* @throws {TypeError} If no page is active.
|
|
721
|
+
*/
|
|
620
722
|
exports._setLineStyle = function _setLineStyle(options = {}) {
|
|
621
723
|
if (options.width !== undefined || options.lineWidth !== undefined)
|
|
622
724
|
this.pageContext.w(options.width ?? options.lineWidth);
|
|
@@ -639,6 +741,7 @@ exports._setLineStyle = function _setLineStyle(options = {}) {
|
|
|
639
741
|
* @memberof Recipe#
|
|
640
742
|
* @param {number} width - The line width.
|
|
641
743
|
* @returns {Recipe} The recipe instance.
|
|
744
|
+
* @throws {TypeError} If no page is active.
|
|
642
745
|
*/
|
|
643
746
|
exports.lineWidth = function lineWidth(width) {
|
|
644
747
|
return this.lineStyle({ width });
|
|
@@ -652,6 +755,7 @@ exports.lineWidth = function lineWidth(width) {
|
|
|
652
755
|
* @memberof Recipe#
|
|
653
756
|
* @param {number} value - The requested opacity from 0 (transparent) to 1 (opaque).
|
|
654
757
|
* @returns {Recipe} The recipe instance.
|
|
758
|
+
* @throws {RangeError} If the value is not a finite number from 0 to 1.
|
|
655
759
|
*/
|
|
656
760
|
exports.opacity = function opacity(value) {
|
|
657
761
|
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
@@ -1,33 +1,52 @@
|
|
|
1
|
+
const { Colorspace } = require("../recipe-constants");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Writes device color operators for a color model.
|
|
5
|
+
* @private
|
|
6
|
+
*/
|
|
1
7
|
const Color = class Color {
|
|
8
|
+
/** Not meant to be instantiated; use the static methods. */
|
|
2
9
|
constructor() {}
|
|
3
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Set the fill color.
|
|
13
|
+
* @param {Object} ctx - The content context.
|
|
14
|
+
* @param {Object} colorModel - The color model from _transformColor().
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
4
17
|
static fill(ctx, colorModel) {
|
|
5
18
|
switch (colorModel.colorspace) {
|
|
6
|
-
case
|
|
19
|
+
case Colorspace.RGB:
|
|
7
20
|
ctx.rg(colorModel.r, colorModel.g, colorModel.b);
|
|
8
21
|
break;
|
|
9
22
|
|
|
10
|
-
case
|
|
23
|
+
case Colorspace.CMYK:
|
|
11
24
|
ctx.k(colorModel.c, colorModel.m, colorModel.y, colorModel.k);
|
|
12
25
|
break;
|
|
13
26
|
|
|
14
|
-
case
|
|
27
|
+
case Colorspace.GRAY:
|
|
15
28
|
ctx.g(colorModel.gray);
|
|
16
29
|
break;
|
|
17
30
|
}
|
|
18
31
|
}
|
|
19
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Set the stroke color.
|
|
35
|
+
* @param {Object} ctx - The content context.
|
|
36
|
+
* @param {Object} colorModel - The color model from _transformColor().
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
20
39
|
static stroke(ctx, colorModel) {
|
|
21
40
|
switch (colorModel.colorspace) {
|
|
22
|
-
case
|
|
41
|
+
case Colorspace.RGB:
|
|
23
42
|
ctx.RG(colorModel.r, colorModel.g, colorModel.b);
|
|
24
43
|
break;
|
|
25
44
|
|
|
26
|
-
case
|
|
45
|
+
case Colorspace.CMYK:
|
|
27
46
|
ctx.K(colorModel.c, colorModel.m, colorModel.y, colorModel.k);
|
|
28
47
|
break;
|
|
29
48
|
|
|
30
|
-
case
|
|
49
|
+
case Colorspace.GRAY:
|
|
31
50
|
ctx.G(colorModel.gray);
|
|
32
51
|
break;
|
|
33
52
|
}
|
|
@@ -36,7 +55,17 @@ const Color = class Color {
|
|
|
36
55
|
|
|
37
56
|
exports.Color = Color;
|
|
38
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Creates a writer form XObject extended with Recipe helpers; the
|
|
60
|
+
* constructor returns the form itself.
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
39
63
|
exports.xObjectForm = class xObjectForm {
|
|
64
|
+
/**
|
|
65
|
+
* @param {Object} pdfWriter - The PDF writer.
|
|
66
|
+
* @param {number} [width=100] - The form width.
|
|
67
|
+
* @param {number} [height=100] - The form height.
|
|
68
|
+
*/
|
|
40
69
|
constructor(pdfWriter, width = 100, height = 100) {
|
|
41
70
|
const xObject = pdfWriter.createFormXObject(0, 0, width, height);
|
|
42
71
|
xObject.pdfWriter = pdfWriter;
|
|
@@ -50,32 +79,61 @@ exports.xObjectForm = class xObjectForm {
|
|
|
50
79
|
return xObject;
|
|
51
80
|
}
|
|
52
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Store a value on the form, such as its Recipe cache key.
|
|
84
|
+
* @param {string} key - The key.
|
|
85
|
+
* @param {*} value - The value.
|
|
86
|
+
* @returns {void}
|
|
87
|
+
*/
|
|
53
88
|
set(key, value) {
|
|
54
89
|
this._values = this._values || {};
|
|
55
90
|
this._values[key] = value;
|
|
56
91
|
}
|
|
57
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} key - The key.
|
|
95
|
+
* @returns {*} The stored value, or undefined.
|
|
96
|
+
*/
|
|
58
97
|
get(key) {
|
|
59
98
|
this._values = this._values || {};
|
|
60
99
|
return this._values[key];
|
|
61
100
|
}
|
|
62
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Map a graphics state into the form resources.
|
|
104
|
+
* @param {number} gsId - The ExtGState object ID.
|
|
105
|
+
* @returns {string} The resource name.
|
|
106
|
+
*/
|
|
63
107
|
getGsName(gsId) {
|
|
64
|
-
const resourcesDict = this.
|
|
108
|
+
const resourcesDict = this.getResourcesDictionary();
|
|
65
109
|
const gsName = resourcesDict.addExtGStateMapping(gsId);
|
|
66
110
|
return gsName;
|
|
67
111
|
}
|
|
68
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Map a color space into the form resources.
|
|
115
|
+
* @param {number} csId - The color space object ID.
|
|
116
|
+
* @returns {string} The resource name.
|
|
117
|
+
*/
|
|
69
118
|
getCsName(csId) {
|
|
70
|
-
const resourcesDict = this.
|
|
119
|
+
const resourcesDict = this.getResourcesDictionary();
|
|
71
120
|
const csName = resourcesDict.addColorSpaceMapping(csId);
|
|
72
121
|
return csName;
|
|
73
122
|
}
|
|
74
123
|
|
|
124
|
+
/**
|
|
125
|
+
* End the form so it can be placed.
|
|
126
|
+
* @returns {void}
|
|
127
|
+
*/
|
|
75
128
|
end() {
|
|
76
129
|
this.pdfWriter.endFormXObject(this);
|
|
77
130
|
}
|
|
78
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Set the fill color in the form, including separation colors.
|
|
134
|
+
* @param {Object} colorModel - The color model from _transformColor().
|
|
135
|
+
* @returns {Object} The form.
|
|
136
|
+
*/
|
|
79
137
|
fill(colorModel) {
|
|
80
138
|
const ctx = this.getContentContext();
|
|
81
139
|
switch (colorModel.colorspace) {
|
|
@@ -83,7 +141,7 @@ exports.xObjectForm = class xObjectForm {
|
|
|
83
141
|
Color.fill(ctx, colorModel);
|
|
84
142
|
break;
|
|
85
143
|
|
|
86
|
-
case
|
|
144
|
+
case Colorspace.SEPARATION:
|
|
87
145
|
ctx.cs(this.getCsName(colorModel.colorspaceId));
|
|
88
146
|
ctx.scn(1);
|
|
89
147
|
break;
|
|
@@ -91,6 +149,11 @@ exports.xObjectForm = class xObjectForm {
|
|
|
91
149
|
return this;
|
|
92
150
|
}
|
|
93
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Set the stroke color in the form, including separation colors.
|
|
154
|
+
* @param {Object} colorModel - The color model from _transformColor().
|
|
155
|
+
* @returns {Object} The form.
|
|
156
|
+
*/
|
|
94
157
|
stroke(colorModel) {
|
|
95
158
|
const ctx = this.getContentContext();
|
|
96
159
|
switch (colorModel.colorspace) {
|
|
@@ -98,7 +161,7 @@ exports.xObjectForm = class xObjectForm {
|
|
|
98
161
|
Color.stroke(ctx, colorModel);
|
|
99
162
|
break;
|
|
100
163
|
|
|
101
|
-
case
|
|
164
|
+
case Colorspace.SEPARATION:
|
|
102
165
|
ctx.CS(this.getCsName(colorModel.colorspaceId));
|
|
103
166
|
ctx.SCN(1);
|
|
104
167
|
break;
|