@muhammara/native-core 7.0.0-beta.3 → 7.0.0-rc.1

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 (43) 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 +86 -30
  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 +482 -105
  15. package/lib/recipe/appendPage.js +8 -1
  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 +154 -21
  22. package/lib/recipe/image.js +34 -9
  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 +379 -275
  28. package/lib/recipe/parameters.js +11 -2
  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 +260 -173
  33. package/lib/recipe/text.helper.js +176 -10
  34. package/lib/recipe/text.js +420 -115
  35. package/lib/recipe/utils.js +66 -3
  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 +150 -13
  39. package/lib/recipe/vector.js +150 -53
  40. package/lib/recipe/xObjectForm.js +73 -10
  41. package/lib/recipe-constants.js +373 -0
  42. package/muhammara.d.ts +3487 -152
  43. package/package.json +1 -1
@@ -1,11 +1,29 @@
1
+ var { cloneOptions, resolveFontSize } = require("./utils");
2
+ var { HorizontalAlign, VerticalAlign } = require("../recipe-constants");
3
+
4
+ /**
5
+ * The width character spacing adds between the characters of a text.
6
+ * @private
7
+ * @param {string} text - The text.
8
+ * @param {number} charSpace - The spacing added after each character but the last.
9
+ * @returns {number} The added width.
10
+ */
1
11
  const charSpacing = function charSpacing(text, charSpace) {
2
- let txt = text.replace(/^(?:(?!\u00a0)\s)+|(?:(?!\u00a0)\s)+$/g, "");
3
- return txt.length ? (txt.length - 1) * charSpace : 0;
12
+ var characterCount = Array.from(String(text)).length;
13
+ return characterCount ? (characterCount - 1) * charSpace : 0;
4
14
  };
5
15
 
6
16
  // Have to set up word as a constant, then export it below
7
17
  // so that Line can see it. Otherwise, an error is thrown.
8
18
 
19
+ /**
20
+ * A word used by Recipe text layout.
21
+ * @name Word
22
+ * @class
23
+ * @memberof Recipe#
24
+ * @param {string} word - The word, a single space measured as "o".
25
+ * @param {Object} pathOptions - The resolved text options: font, size and charSpace.
26
+ */
9
27
  const Word = class Word {
10
28
  constructor(word, pathOptions) {
11
29
  this._value = word;
@@ -14,10 +32,17 @@ const Word = class Word {
14
32
  this._text = word === " " ? "o" : word; // allows space to get an actual dimension
15
33
  }
16
34
 
35
+ /**
36
+ * @returns {string} The word text.
37
+ */
17
38
  get value() {
18
39
  return this._value;
19
40
  }
20
41
 
42
+ /**
43
+ * The measured text box, including character spacing; cached.
44
+ * @returns {Object} xMin, yMin, xMax, yMax, width and height.
45
+ */
21
46
  get dimensions() {
22
47
  if (this._dimensions) {
23
48
  return this._dimensions;
@@ -30,14 +55,26 @@ const Word = class Word {
30
55
  return this._dimensions;
31
56
  }
32
57
 
58
+ /**
59
+ * @returns {boolean} Whether this is the last word of its line.
60
+ */
33
61
  get last() {
34
62
  return this._last;
35
63
  }
36
64
 
65
+ /**
66
+ * @returns {number} The width character spacing adds to the word.
67
+ */
37
68
  get charSpacing() {
38
69
  return charSpacing(this._text, this._pathOptions.charSpace);
39
70
  }
40
71
 
72
+ /**
73
+ * Mark the word as the last of its line, trimming trailing space and
74
+ * measuring it again.
75
+ * @param {boolean} [value=true] - Whether the word is last.
76
+ * @returns {void}
77
+ */
41
78
  lastWord(value = true) {
42
79
  // indicate last word in line (for justification)
43
80
  this._last = value;
@@ -73,9 +110,19 @@ exports.Word = Word; // ... now export Word to the rest of the library.
73
110
  * @param {number} size - The font size.
74
111
  * @param {Object} pathOptions - The resolved text options.
75
112
  */
113
+ // The width of a line without a text box. No text reaches it, so such a line
114
+ // accepts every word without measuring.
115
+ const UNBOUNDED_LINE_WIDTH = 999999999;
116
+
76
117
  exports.Line = class Line {
118
+ /**
119
+ * @param {number} [width] - The available width; unlimited when omitted.
120
+ * @param {number} [height] - A fixed line height.
121
+ * @param {number} [size] - The font size; defaults to the text options size.
122
+ * @param {Object} pathOptions - The resolved text options.
123
+ */
77
124
  constructor(width, height, size, pathOptions) {
78
- this._width = width || 999999999;
125
+ this._width = width || UNBOUNDED_LINE_WIDTH;
79
126
  this._height = height;
80
127
  this._pathOptions = pathOptions;
81
128
  this.size = size || pathOptions.size;
@@ -83,41 +130,75 @@ exports.Line = class Line {
83
130
  this.wordObjects = [];
84
131
  }
85
132
 
133
+ /**
134
+ * Replace the line ID; falsy values are ignored.
135
+ * @param {number} id - The new ID.
136
+ */
86
137
  set lineID(id) {
87
138
  if (id) {
88
139
  this._lineID = id;
89
140
  }
90
141
  }
91
142
 
143
+ /**
144
+ * @returns {number} The line ID, grouping objects laid out on one line.
145
+ */
92
146
  get lineID() {
93
147
  return this._lineID;
94
148
  }
95
149
 
150
+ /**
151
+ * @param {Word} wordObject - The word to append.
152
+ * @returns {void}
153
+ */
96
154
  addWord(wordObject) {
97
155
  this.wordObjects.push(wordObject);
98
156
  }
99
157
 
158
+ /**
159
+ * Append leading spaces.
160
+ * @param {number} amount - The number of spaces.
161
+ * @returns {void}
162
+ */
100
163
  indent(amount) {
101
164
  for (let i = 0; i < amount; i++) {
102
165
  this.addWord(new Word(" ", this._pathOptions));
103
166
  }
104
167
  }
105
168
 
169
+ /**
170
+ * Mark the final word as the last of the line.
171
+ * @returns {void}
172
+ */
106
173
  markLastWord() {
107
174
  if (this.wordObjects.length > 0) {
108
175
  this.wordObjects[this.wordObjects.length - 1].lastWord();
109
176
  }
110
177
  }
111
178
 
179
+ /**
180
+ * @returns {Word|undefined} The final word, or undefined for an empty line.
181
+ */
112
182
  get lastWord() {
113
183
  return this.wordObjects[this.wordObjects.length - 1];
114
184
  }
115
185
 
186
+ /**
187
+ * @param {string} text - The text.
188
+ * @returns {number} The width character spacing adds to the text.
189
+ */
116
190
  charSpacing(text) {
117
191
  return charSpacing(text, this._pathOptions.charSpace);
118
192
  }
119
193
 
194
+ /**
195
+ * @param {Word} wordObject - The word to test.
196
+ * @returns {boolean} Whether the line still fits its width with the word appended.
197
+ */
120
198
  canFit(wordObject) {
199
+ // Measuring the whole line for every word is quadratic in its length, and
200
+ // a line without a text box never wraps.
201
+ if (this._width >= UNBOUNDED_LINE_WIDTH) return true;
121
202
  const tempValue = this.value + wordObject.value;
122
203
  const toWidth =
123
204
  this._pathOptions.font.calculateTextDimensions(tempValue, this.size)
@@ -125,6 +206,11 @@ exports.Line = class Line {
125
206
  return toWidth <= this.width;
126
207
  }
127
208
 
209
+ /**
210
+ * Replace the final word and mark the new one as last.
211
+ * @param {Word|string} wordObject - The replacement word.
212
+ * @returns {void}
213
+ */
128
214
  replaceLastWord(wordObject) {
129
215
  if (typeof wordObject === "string") {
130
216
  wordObject = new Word(wordObject, this._pathOptions);
@@ -134,14 +220,23 @@ exports.Line = class Line {
134
220
  wordObject.lastWord();
135
221
  }
136
222
 
223
+ /**
224
+ * @returns {Word[]} The words of the line.
225
+ */
137
226
  get words() {
138
227
  return this.wordObjects;
139
228
  }
140
229
 
230
+ /**
231
+ * @returns {number} The width of one space, measured as "o".
232
+ */
141
233
  get spaceWidth() {
142
234
  return this._pathOptions.font.calculateTextDimensions("o", this.size).width;
143
235
  }
144
236
 
237
+ /**
238
+ * @returns {string} The text of the line.
239
+ */
145
240
  get value() {
146
241
  const value = this.wordObjects.reduce((string, word) => {
147
242
  string += word.value;
@@ -150,6 +245,9 @@ exports.Line = class Line {
150
245
  return value;
151
246
  }
152
247
 
248
+ /**
249
+ * @returns {number} The measured width of the line text.
250
+ */
153
251
  get currentWidth() {
154
252
  return (
155
253
  this._pathOptions.font.calculateTextDimensions(this.value, this.size)
@@ -157,6 +255,9 @@ exports.Line = class Line {
157
255
  );
158
256
  }
159
257
 
258
+ /**
259
+ * @returns {number} The sum of the measured word widths.
260
+ */
160
261
  get textWidth() {
161
262
  return this.wordObjects.reduce((width, word) => {
162
263
  width += word.dimensions.xMax;
@@ -164,11 +265,17 @@ exports.Line = class Line {
164
265
  }, 0);
165
266
  }
166
267
 
268
+ /**
269
+ * @returns {number} The available width.
270
+ */
167
271
  get width() {
168
272
  return this._width;
169
273
  }
170
274
 
171
275
  // dynamic adjust height based on word height?
276
+ /**
277
+ * @returns {number} The fixed height, or the measured text height plus 20.
278
+ */
172
279
  get height() {
173
280
  if (this._height) {
174
281
  return this._height;
@@ -185,6 +292,12 @@ exports.Line = class Line {
185
292
  * Get the offset for a text box.
186
293
  * @private
187
294
  * @todo handle page margin and padding
295
+ * @param {Object} textBox - The laid-out text box: width, height, textHeight,
296
+ * firstLineHeight and isSimpleText.
297
+ * @param {Object} [options] - The text options.
298
+ * @param {string} [options.align] - A `Recipe.HorizontalAlign` value,
299
+ * optionally followed by a space and a `Recipe.VerticalAlign` value.
300
+ * @returns {{offsetX: number, offsetY: number}} The offset from the placement point.
188
301
  */
189
302
  exports._getTextBoxOffset = function _getTextBoxOffset(textBox, options = {}) {
190
303
  let offsetX = 0;
@@ -194,10 +307,10 @@ exports._getTextBoxOffset = function _getTextBoxOffset(textBox, options = {}) {
194
307
  const alignments = options.align.split(" ");
195
308
  if (alignments[0]) {
196
309
  switch (alignments[0]) {
197
- case "center":
310
+ case HorizontalAlign.CENTER:
198
311
  offsetX = (-1 * width) / 2;
199
312
  break;
200
- case "right":
313
+ case HorizontalAlign.RIGHT:
201
314
  offsetX = -width;
202
315
  break;
203
316
  default:
@@ -206,12 +319,12 @@ exports._getTextBoxOffset = function _getTextBoxOffset(textBox, options = {}) {
206
319
  if (alignments[1]) {
207
320
  height = height || textHeight;
208
321
  switch (alignments[1]) {
209
- case "center":
322
+ case VerticalAlign.CENTER:
210
323
  offsetY = textBox.isSimpleText
211
324
  ? -textBox.firstLineHeight / 2
212
325
  : height / 2 + offsetY;
213
326
  break;
214
- case "bottom":
327
+ case VerticalAlign.BOTTOM:
215
328
  offsetY = height + offsetY;
216
329
  break;
217
330
  default:
@@ -235,9 +348,14 @@ exports._getTextBoxOffset = function _getTextBoxOffset(textBox, options = {}) {
235
348
  * @param {string} [options.font='helvetica'] - name of font from which measurements are to be taken
236
349
  * @param {number} [options.size=14] - size of font to be used in taking measurements
237
350
  * @param {number} [options.charSpace=0] - character spacing being applied to the given text.
351
+ * @param {boolean} [options.bold] - Measure with the bold style of the font.
352
+ * @param {boolean} [options.italic] - Measure with the italic style of the font.
238
353
  * @returns {Object} measurement components of given text: width, height, xMin, xMax, yMin, yMax
354
+ * @throws {Error} If the font file cannot be loaded.
239
355
  */
240
356
  exports.textDimensions = function textDimensions(text, options = {}) {
357
+ // null options act like omitted options.
358
+ if (options === null) options = {};
241
359
  const font = this._getFont(options);
242
360
  let dimensions = {};
243
361
  let charSpaces = 0;
@@ -246,7 +364,7 @@ exports.textDimensions = function textDimensions(text, options = {}) {
246
364
  if (options.charSpace) {
247
365
  charSpaces = charSpacing(text, options.charSpace);
248
366
  }
249
- const fontSize = options.size || this.current.defaultFontSize;
367
+ const fontSize = resolveFontSize(options, this.current.defaultFontSize);
250
368
  dimensions = font.calculateTextDimensions(text, fontSize);
251
369
  dimensions.xMax += charSpaces;
252
370
  }
@@ -268,6 +386,15 @@ exports.textDimensions = function textDimensions(text, options = {}) {
268
386
  * @param {Object} [options] - The column options.
269
387
  */
270
388
  exports.Column = class Column {
389
+ /**
390
+ * @param {number} x - The x coordinate.
391
+ * @param {number} y - The y coordinate.
392
+ * @param {number} width - The column width.
393
+ * @param {number} [height] - The column height; unlimited when omitted.
394
+ * @param {string} [text=''] - The column heading; defaults to the field.
395
+ * @param {string} [field=''] - The associated data field.
396
+ * @param {Object} [options] - The column options; `cell` becomes the text box.
397
+ */
271
398
  constructor(x, y, width, height, text = "", field = "", options = {}) {
272
399
  this._x = x;
273
400
  this._y = y;
@@ -276,7 +403,7 @@ exports.Column = class Column {
276
403
  this._field = field; // associated data field
277
404
  this._text = text || field; // for column title
278
405
  this._gap = 0;
279
- this._options = JSON.parse(JSON.stringify(options)); // cloning
406
+ this._options = cloneOptions(options);
280
407
 
281
408
  this._options.textBox = this._options.cell;
282
409
 
@@ -292,7 +419,10 @@ exports.Column = class Column {
292
419
  if (!this._options.header || typeof this._options.header === "boolean") {
293
420
  this._options.header = {
294
421
  bold: true,
295
- textBox: { padding: 2, textAlign: "center center" },
422
+ textBox: {
423
+ padding: 2,
424
+ textAlign: `${HorizontalAlign.CENTER} ${VerticalAlign.CENTER}`,
425
+ },
296
426
  };
297
427
  }
298
428
 
@@ -301,39 +431,75 @@ exports.Column = class Column {
301
431
  }
302
432
  }
303
433
 
434
+ /**
435
+ * @returns {number} The column width.
436
+ */
304
437
  get width() {
305
438
  return this._width;
306
439
  }
440
+ /**
441
+ * @returns {number} The column height.
442
+ */
307
443
  get height() {
308
444
  return this._height;
309
445
  }
446
+ /**
447
+ * @returns {number} The column x coordinate.
448
+ */
310
449
  get x() {
311
450
  return this._x;
312
451
  }
452
+ /**
453
+ * @param {number} x - The new x coordinate.
454
+ */
313
455
  set x(x) {
314
456
  this._x = x;
315
457
  }
458
+ /**
459
+ * @returns {number} The column y coordinate.
460
+ */
316
461
  get y() {
317
462
  return this._y;
318
463
  }
464
+ /**
465
+ * @returns {number[]} The column [x, y] position.
466
+ */
319
467
  get position() {
320
468
  return [this._x, this._y];
321
469
  }
470
+ /**
471
+ * @param {number[]} pos - The new [x, y] position.
472
+ */
322
473
  set position(pos) {
323
474
  [this._x, this._y] = pos;
324
475
  }
476
+ /**
477
+ * @returns {number} The gap after the column.
478
+ */
325
479
  get gap() {
326
480
  return this._gap;
327
481
  }
482
+ /**
483
+ * @param {number} gap - The new gap after the column.
484
+ */
328
485
  set gap(gap) {
329
486
  this._gap = gap;
330
487
  }
488
+ /**
489
+ * @returns {string} The associated data field.
490
+ */
331
491
  get field() {
332
492
  return this._field;
333
493
  }
494
+ /**
495
+ * @returns {string} The column heading.
496
+ */
334
497
  get text() {
335
498
  return this._text;
336
499
  }
500
+ /**
501
+ * @returns {Object} The column text options.
502
+ */
337
503
  get options() {
338
504
  return this._options;
339
505
  }