@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.
Files changed (44) hide show
  1. package/README.md +57 -7
  2. package/THIRD_PARTY_NOTICES.md +39 -0
  3. package/index.js +120 -1
  4. package/lib/PDFRStreamForBuffer.js +52 -7
  5. package/lib/PDFRStreamForFile.js +56 -7
  6. package/lib/PDFStreamForResponse.js +22 -5
  7. package/lib/PDFWStreamForBuffer.js +49 -10
  8. package/lib/PDFWStreamForFile.js +26 -5
  9. package/lib/Recipe.js +91 -38
  10. package/lib/content-stream.js +503 -0
  11. package/lib/css-colors.js +149 -0
  12. package/lib/font-text.js +1461 -0
  13. package/lib/glyph-list.js +1127 -0
  14. package/lib/recipe/annotation.js +464 -101
  15. package/lib/recipe/appendPage.js +2 -0
  16. package/lib/recipe/colors.js +132 -35
  17. package/lib/recipe/coordinate.js +82 -16
  18. package/lib/recipe/encrypt.js +32 -13
  19. package/lib/recipe/font.js +70 -25
  20. package/lib/recipe/graphics-state.js +3 -2
  21. package/lib/recipe/htmlToTextObjects.js +121 -24
  22. package/lib/recipe/image.js +385 -39
  23. package/lib/recipe/info.js +93 -22
  24. package/lib/recipe/insertPage.js +12 -3
  25. package/lib/recipe/overlay.js +4 -2
  26. package/lib/recipe/page-references.js +436 -0
  27. package/lib/recipe/page.js +363 -262
  28. package/lib/recipe/parameters.js +8 -1
  29. package/lib/recipe/replaceText.js +464 -30
  30. package/lib/recipe/shapes.js +418 -45
  31. package/lib/recipe/split.js +6 -2
  32. package/lib/recipe/table.js +41 -8
  33. package/lib/recipe/text.helper.js +172 -8
  34. package/lib/recipe/text.js +294 -56
  35. package/lib/recipe/utils.js +17 -6
  36. package/lib/recipe/vector-line.js +101 -39
  37. package/lib/recipe/vector-polygon.js +18 -6
  38. package/lib/recipe/vector.helper.js +156 -14
  39. package/lib/recipe/vector.js +156 -52
  40. package/lib/recipe/xObjectForm.js +73 -10
  41. package/lib/recipe-constants.js +373 -0
  42. package/lib/recipe-options.js +47 -0
  43. package/muhammara.d.ts +3501 -160
  44. package/package.json +2 -3
@@ -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[]}[ options.fill] - HexColor, PercentColor or DecimalColor
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 = "fill";
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
- .gs(xObject.getGsName(pathOptions.fillGsId))
57
- .drawCircle(radius, radius, radius, pathOptions);
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 = "stroke";
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 = "fill";
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(0, 0, width, height, pathOptions);
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 = "stroke";
191
+ pathOptions.type = muhammara.DrawingPathType.STROKE;
164
192
 
165
- if (pathOptions.stroke !== undefined) {
166
- pathOptions.color = pathOptions.stroke;
167
- pathOptions.colorspace = pathOptions.strokeModel.colorspace;
168
- colorModel = pathOptions.strokeModel;
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
- margin / 2,
192
- margin / 2,
193
- width - margin,
194
- height - margin,
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
- function drawRoundedRectangle(ctx, left, bottom, width, height, radii) {
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 = rx * magic; // control point offset horizontal
326
- const oy = ry * magic; // control point offset horizontal
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
- if (pathOptions.stroke !== undefined) {
363
- colorModel = pathOptions.strokeModel;
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
- if (pathOptions.stroke !== undefined) {
521
- colorModel = pathOptions.strokeModel;
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(ctx, radius, radius, radius - margin, sAng, eAng, sector);
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 "rgb":
19
+ case Colorspace.RGB:
7
20
  ctx.rg(colorModel.r, colorModel.g, colorModel.b);
8
21
  break;
9
22
 
10
- case "cmyk":
23
+ case Colorspace.CMYK:
11
24
  ctx.k(colorModel.c, colorModel.m, colorModel.y, colorModel.k);
12
25
  break;
13
26
 
14
- case "gray":
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 "rgb":
41
+ case Colorspace.RGB:
23
42
  ctx.RG(colorModel.r, colorModel.g, colorModel.b);
24
43
  break;
25
44
 
26
- case "cmyk":
45
+ case Colorspace.CMYK:
27
46
  ctx.K(colorModel.c, colorModel.m, colorModel.y, colorModel.k);
28
47
  break;
29
48
 
30
- case "gray":
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.getResourcesDictinary();
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.getResourcesDictinary();
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 "separation":
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 "separation":
164
+ case Colorspace.SEPARATION:
102
165
  ctx.CS(this.getCsName(colorModel.colorspaceId));
103
166
  ctx.SCN(1);
104
167
  break;