@muhammara/native-core 7.0.0-beta.4 → 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 +79 -28
  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 +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 +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 +148 -6
  39. package/lib/recipe/vector.js +150 -51
  40. package/lib/recipe/xObjectForm.js +73 -10
  41. package/lib/recipe-constants.js +373 -0
  42. package/muhammara.d.ts +3469 -155
  43. package/package.json +1 -1
@@ -1,18 +1,51 @@
1
1
  const fs = require("fs");
2
2
  const muhammara = require("../muhammara");
3
3
  var { recipeInfoKeys, standardInfoKeys } = require("../recipe-info");
4
+
5
+ // Source Info dictionary keys that _readInfo() keeps as raw values.
6
+ var PdfInfoKey = Object.freeze({
7
+ TRAPPED: "Trapped",
8
+ CREATION_DATE: "CreationDate",
9
+ MOD_DATE: "ModDate",
10
+ CREATOR: "Creator",
11
+ PRODUCER: "Producer",
12
+ });
13
+
14
+ // Names _readInfo() caches those entries under in this.infoDictionary.
15
+ var CachedInfoKey = Object.freeze({
16
+ TRAPPED: "trapped",
17
+ CREATION_DATE: "creationDate",
18
+ MOD_DATE: "modDate",
19
+ CREATOR: "creator",
20
+ PRODUCER: "producer",
21
+ });
22
+
23
+ // How _writeInfo() converts each standard info option.
24
+ var InfoFieldType = Object.freeze({
25
+ STRING: "string",
26
+ DATE: "date",
27
+ ARRAY: "array",
28
+ });
29
+
30
+ var trappedValues = {
31
+ True: muhammara.EInfoTrappedTrue,
32
+ False: muhammara.EInfoTrappedFalse,
33
+ Unknown: muhammara.EInfoTrappedUnknown,
34
+ };
35
+
4
36
  /**
5
37
  * @name info
6
38
  * @desc Add standard and custom PDF information, or retrieve existing PDF information. Custom keys retain their spelling when written; array values are joined with a comma and space.
7
39
  * @memberof Recipe#
8
40
  * @function
9
41
  * @param {Object} [options] - The options (when missing obtains existing PDF information)
10
- * @param {number} [options.version] - The pdf version
11
42
  * @param {string} [options.author] - The author
12
43
  * @param {string} [options.title] - The title
13
44
  * @param {string} [options.subject] - The subject
14
45
  * @param {string[]} [options.keywords] - The array of keywords
15
46
  * @returns {Object|Recipe} The existing information dictionary when options are omitted, otherwise the recipe instance.
47
+ * A new PDF has no existing information, so the call without options returns undefined.
48
+ * @throws {Error} If the source information cannot be read.
16
49
  */
17
50
  exports.info = function info(options) {
18
51
  let result;
@@ -36,6 +69,15 @@ exports.info = function info(options) {
36
69
  return result;
37
70
  };
38
71
 
72
+ /**
73
+ * Read the Info dictionary of the source PDF once and cache it as
74
+ * `this.infoDictionary`, with Trapped, CreationDate and ModDate as raw values
75
+ * and the other entries as text keyed by their lower-cased names.
76
+ * @private
77
+ * @returns {Object|undefined} The cached information, or undefined for a new
78
+ * PDF or a source without an Info dictionary.
79
+ * @throws {Error} If the source cannot be read.
80
+ */
39
81
  exports._readInfo = function _readInfo() {
40
82
  if (!this.isNewPDF && !this.infoDictionary) {
41
83
  const copyFrom = this.isBufferSrc
@@ -64,29 +106,32 @@ exports._readInfo = function _readInfo() {
64
106
  return;
65
107
  }
66
108
  switch (key) {
67
- case "Trapped":
109
+ case PdfInfoKey.TRAPPED:
68
110
  if (oldInforSrc && oldInforSrc.value) {
69
- this.infoDictionary.trapped = oldInforSrc.value;
111
+ this.infoDictionary[CachedInfoKey.TRAPPED] = oldInforSrc.value;
70
112
  }
71
113
  break;
72
- case "CreationDate":
114
+ case PdfInfoKey.CREATION_DATE:
73
115
  if (oldInforSrc && oldInforSrc.value) {
74
- this.infoDictionary.creationDate = oldInforSrc.value;
116
+ this.infoDictionary[CachedInfoKey.CREATION_DATE] =
117
+ oldInforSrc.value;
75
118
  }
76
119
  break;
77
- case "ModDate":
120
+ case PdfInfoKey.MOD_DATE:
78
121
  if (oldInforSrc && oldInforSrc.value) {
79
- this.infoDictionary.modDate = oldInforSrc.value;
122
+ this.infoDictionary[CachedInfoKey.MOD_DATE] = oldInforSrc.value;
80
123
  }
81
124
  break;
82
- case "Creator":
125
+ case PdfInfoKey.CREATOR:
83
126
  if (oldInforSrc && oldInforSrc.toText) {
84
- this.infoDictionary.creator = oldInforSrc.toText();
127
+ this.infoDictionary[CachedInfoKey.CREATOR] =
128
+ oldInforSrc.toText();
85
129
  }
86
130
  break;
87
- case "Producer":
131
+ case PdfInfoKey.PRODUCER:
88
132
  if (oldInforSrc && oldInforSrc.toText) {
89
- this.infoDictionary.producer = oldInforSrc.toText();
133
+ this.infoDictionary[CachedInfoKey.PRODUCER] =
134
+ oldInforSrc.toText();
90
135
  }
91
136
  break;
92
137
  default:
@@ -104,6 +149,13 @@ exports._readInfo = function _readInfo() {
104
149
  return this.infoDictionary;
105
150
  };
106
151
 
152
+ /**
153
+ * Write the Info dictionary: the preserved source entries, creation and
154
+ * modification dates, producer and creator, and the info() options.
155
+ * @private
156
+ * @returns {Recipe} The recipe instance.
157
+ * @throws {Error} If the source information cannot be read.
158
+ */
107
159
  exports._writeInfo = function _writeInfo() {
108
160
  const options = this.toWriteInfo_ || {};
109
161
  const oldInfo = this._readInfo();
@@ -116,7 +168,10 @@ exports._writeInfo = function _writeInfo() {
116
168
  const infoDictionary = this.writer.getDocumentContext().getInfoDictionary();
117
169
  var fields = standardInfoKeys.map((key) => ({
118
170
  key,
119
- type: key === recipeInfoKeys.keywords ? "array" : "string",
171
+ type:
172
+ key === recipeInfoKeys.keywords
173
+ ? InfoFieldType.ARRAY
174
+ : InfoFieldType.STRING,
120
175
  }));
121
176
  // const ignores = [
122
177
  // 'CreationDate', 'Creator', 'ModDate', 'Producer'
@@ -129,25 +184,28 @@ exports._writeInfo = function _writeInfo() {
129
184
  }
130
185
 
131
186
  switch (key) {
132
- case "trapped":
133
- infoDictionary.trapped = oldInfo.trapped;
187
+ case CachedInfoKey.TRAPPED:
188
+ if (trappedValues[oldInfo[CachedInfoKey.TRAPPED]] !== undefined) {
189
+ infoDictionary.trapped =
190
+ trappedValues[oldInfo[CachedInfoKey.TRAPPED]];
191
+ }
134
192
  break;
135
- case "creationDate":
193
+ case CachedInfoKey.CREATION_DATE:
136
194
  infoDictionary.setCreationDate(oldInfo.creationDate);
137
195
  break;
138
- case "modDate":
196
+ case CachedInfoKey.MOD_DATE:
139
197
  infoDictionary.addAdditionalInfoEntry(
140
198
  "source-ModDate",
141
199
  oldInfo.modDate,
142
200
  );
143
201
  break;
144
- case "creator":
202
+ case CachedInfoKey.CREATOR:
145
203
  infoDictionary.addAdditionalInfoEntry(
146
204
  "source-Creator",
147
205
  oldInfo.creator,
148
206
  );
149
207
  break;
150
- case "producer":
208
+ case CachedInfoKey.PRODUCER:
151
209
  infoDictionary.addAdditionalInfoEntry(
152
210
  "source-Producer",
153
211
  oldInfo.producer,
@@ -174,13 +232,13 @@ exports._writeInfo = function _writeInfo() {
174
232
  return;
175
233
  } else {
176
234
  switch (item.type) {
177
- case "string":
235
+ case InfoFieldType.STRING:
178
236
  value = value.toString();
179
237
  break;
180
- case "date":
238
+ case InfoFieldType.DATE:
181
239
  value = new Date(value);
182
240
  break;
183
- case "array":
241
+ case InfoFieldType.ARRAY:
184
242
  value = Array.isArray(value) ? value : [value];
185
243
  break;
186
244
  default:
@@ -201,8 +259,9 @@ exports._writeInfo = function _writeInfo() {
201
259
  * @memberof Recipe#
202
260
  * @function
203
261
  * @param {string} key - The key
204
- * @param {string} value - The value
262
+ * @param {string} value - The value; other values are converted with toString().
205
263
  * @returns {Recipe} The recipe instance.
264
+ * @throws {TypeError} If the key or value is null or undefined.
206
265
  */
207
266
  exports.custom = function custom(key, value) {
208
267
  const infoDictionary = this.writer.getDocumentContext().getInfoDictionary();
@@ -217,6 +276,8 @@ exports.custom = function custom(key, value) {
217
276
  * @memberof Recipe#
218
277
  * @param {string} output - The output file path.
219
278
  * @returns {Recipe} The recipe instance.
279
+ * @throws {Error} If the source reader was released by endPDF(), or the
280
+ * output file cannot be written.
220
281
  */
221
282
  exports.structure = function structure(output) {
222
283
  // PDF file format http://lotabout.me/orgwiki/pdf.html
@@ -324,6 +385,16 @@ exports.structure = function structure(output) {
324
385
  return this;
325
386
  };
326
387
 
388
+ /**
389
+ * Resolve a source PDF object: follow references, recurse into arrays,
390
+ * dictionaries and stream dictionaries, and record leaf objects in
391
+ * `this.pdfStructure`.
392
+ * @private
393
+ * @param {Object} [inObject] - The parsed PDF object.
394
+ * @returns {Object|undefined} The resolved leaf object, or undefined for
395
+ * containers and a missing object.
396
+ * @throws {Error} If the source reader was released by endPDF().
397
+ */
327
398
  exports._parseObjectByType = function _parseObjectByType(inObject) {
328
399
  if (!inObject) {
329
400
  return;
@@ -6,10 +6,12 @@ const utils = require("./utils");
6
6
  * @name insertPage
7
7
  * @function
8
8
  * @memberof Recipe#
9
- * @param {number} afterPageNumber - The page number for insertion.
9
+ * @param {number} afterPageNumber - The one-based page number to insert after; 0 inserts before the first page.
10
10
  * @param {string} pdfSrc - The path for the other pdf
11
- * @param {number} srcPageNumber - The page number to be insterted from the other pdf.
12
- * @returns {Recipe} The recipe instance.
11
+ * @param {number} srcPageNumber - The one-based page number to be inserted from the other pdf.
12
+ * @returns {Recipe} The recipe instance. Pages are inserted by `endPDF()`;
13
+ * Buffer sources do not support insertion.
14
+ * @throws {Error} If pages were deleted with deletePage() on this Recipe.
13
15
  * @throws {Error} If afterPageNumber is not a number.
14
16
  * @throws {TypeError} If pdfSrc or srcPageNumber is missing.
15
17
  */
@@ -39,6 +41,13 @@ exports.insertPage = function insertPage(
39
41
  return this;
40
42
  };
41
43
 
44
+ /**
45
+ * Rebuild the output file with the pages queued by insertPage() inserted
46
+ * after their target pages.
47
+ * @private
48
+ * @returns {Recipe} The recipe instance.
49
+ * @throws {Error} If nothing was queued, or a file cannot be read, renamed or written.
50
+ */
42
51
  exports._insertPages = function _insertPages() {
43
52
  if (!this.insertInformation) {
44
53
  throw new Error("No insertInformation");
@@ -6,8 +6,8 @@ const { xObjectForm } = require("./xObjectForm");
6
6
  * @function
7
7
  * @memberof Recipe#
8
8
  * @param {string} pdfSrc - The path for the overlay pdf
9
- * @param {number|Object} [x=0] - The x coordinate, or options when using the two-argument form.
10
- * @param {number} [y=0] - The y coordinate.
9
+ * @param {number|Object} [x=0] - The PDF x offset from the left edge, or options when using the two-argument form.
10
+ * @param {number} [y=0] - The offset from the top edge.
11
11
  * @param {Object} [options] - The options.
12
12
  * @param {number} [options.scale] - Scale the overlay pdf, default is 1
13
13
  * @param {number} [options.page] - Page of the overlay pdf, default is 1
@@ -15,6 +15,8 @@ const { xObjectForm } = require("./xObjectForm");
15
15
  * @param {boolean} [options.fitWidth] - To set the width to 100% (use with keepAspectRatio=true)
16
16
  * @param {boolean} [options.fitHeight] - To set the height to 100% (use with keepAspectRatio=true)
17
17
  * @returns {Recipe} The recipe instance.
18
+ * @throws {TypeError} If no page is active.
19
+ * @throws {Error} If the overlay PDF cannot be read.
18
20
  */
19
21
  exports.overlay = function overlay(pdfSrc, x = 0, y = 0, options = {}) {
20
22
  // allow to have only 2 arguments input