@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
package/lib/Recipe.js CHANGED
@@ -3,26 +3,33 @@ const path = require("path");
3
3
  const fs = require("fs");
4
4
  const streams = require("memory-streams");
5
5
  var { standardInfoKeys } = require("./recipe-info");
6
+ var { AnnotSubtype, PageLayout, Source } = require("./recipe-constants");
6
7
 
7
8
  /**
8
9
  * @name Recipe
9
- * @desc Create a pdfDoc
10
+ * @desc Create a new PDF, or open an existing one for editing.
10
11
  * @namespace
11
12
  * @constructor
12
- * @param {string|Buffer} src - The file path or Buffer of the source file.
13
- * @param {string} [output] - The path of the output file uses src if its not a buffer.
13
+ * @param {string|Buffer} src - `Recipe.Source.NEW` ("new", or `Buffer.from("new")`) for a new PDF,
14
+ * otherwise the path or Buffer of the PDF to edit.
15
+ * @param {string} [output] - The output path. For a path source it defaults to
16
+ * the source path; for a Buffer source the result is only returned by
17
+ * `endPDF()` unless an output path is given.
14
18
  * @param {Object} [options] - The options for pdfDoc
15
- * @param {number} [options.version] - The pdf version: 1.0 through 1.7 or 2.0, defaults to 1.7
19
+ * @param {number} [options.version] - The PDF version of a new PDF: 1.0 through
20
+ * 1.7 or 2.0. Other values fall back to 1.7.
16
21
  * @param {string} [options.author] - The author
17
22
  * @param {string} [options.title] - The title
18
23
  * @param {string} [options.subject] - The subject
19
- * @param {string} [options.colorspace] - The default colorspace: rgb, cmyk, gray, separation
20
24
  * @param {string[]} [options.keywords] - The array of keywords
21
- * @param {string} [options.password] - permission password
22
- * @param {string} [options.userPassword] - this 'view' password also enables encryption
23
- * @param {string} [options.ownerPassword] - this allows owner to 'edit' file
24
- * @param {string} [options.userProtectionFlag] - encryption security level (see permissions)
25
- * @param {string|string[]} [options.fontSrcPath] - directory location(s) of additional fonts
25
+ * @param {Recipe.Colorspace} [options.colorspace] - The default colorspace, one
26
+ * of the `Recipe.Colorspace` values.
27
+ * @param {string} [options.password] - Owner password; also opens a protected source.
28
+ * @param {string} [options.userPassword] - The 'view' password; also enables encryption.
29
+ * @param {string} [options.ownerPassword] - The 'edit' password.
30
+ * @param {number} [options.userProtectionFlag] - Encryption permission flags, see `permission()`.
31
+ * @param {string|string[]} [options.fontSrcPath] - Directory location(s) of additional fonts.
32
+ * @throws {Error} If an existing source PDF cannot be read or opened for editing.
26
33
  */
27
34
  class Recipe {
28
35
  constructor(src, output, options = {}) {
@@ -30,12 +37,20 @@ class Recipe {
30
37
  // detect the src is Buffer or not
31
38
  this.isBufferSrc = this.src instanceof Buffer;
32
39
  this.isNewPDF =
33
- (!this.isBufferSrc && src.toLowerCase() === "new") ||
34
- (this.isBufferSrc && this.src.equals(Buffer.from("new")));
40
+ (!this.isBufferSrc && src.toLowerCase() === Source.NEW) ||
41
+ (this.isBufferSrc && this.src.equals(Buffer.from(Source.NEW)));
35
42
  this.encryptOptions = this._getEncryptOptions(options, this.isNewPDF);
36
43
  this.options = Object.assign({}, options, this.encryptOptions);
37
44
  this.current = {};
38
45
  this.current.defaultFontSize = 14;
46
+ // Each Recipe registers colors in its own copy of the defaults, as Wasm
47
+ // does; the prototype keeps the defaults unchanged.
48
+ this.knownColors = Object.fromEntries(
49
+ Object.entries(this.knownColors).map(([space, colors]) => [
50
+ space,
51
+ { ...colors },
52
+ ]),
53
+ );
39
54
 
40
55
  if (this.isBufferSrc) {
41
56
  this.outStream = new streams.WritableStream();
@@ -50,10 +65,10 @@ class Recipe {
50
65
  this.logFile = "muhammara-error.log";
51
66
 
52
67
  this.textMarkupAnnotations = [
53
- "Highlight",
54
- "Underline",
55
- "StrikeOut",
56
- "Squiggly",
68
+ AnnotSubtype.HIGHLIGHT,
69
+ AnnotSubtype.UNDERLINE,
70
+ AnnotSubtype.STRIKE_OUT,
71
+ AnnotSubtype.SQUIGGLY,
57
72
  ];
58
73
 
59
74
  this.annotationsToWrite = [];
@@ -76,6 +91,13 @@ class Recipe {
76
91
  this._createWriter();
77
92
  }
78
93
 
94
+ /**
95
+ * Create the writer for a new PDF, or open the source PDF for editing, and
96
+ * apply the info options.
97
+ * @private
98
+ * @returns {void}
99
+ * @throws {Error} If the source PDF cannot be read or opened for editing.
100
+ */
79
101
  _createWriter() {
80
102
  if (this.isNewPDF) {
81
103
  if (!this.isBufferSrc) {
@@ -89,6 +111,7 @@ class Recipe {
89
111
  this.writer = muhammara.createWriter(
90
112
  new muhammara.PDFStreamForResponse(this.outStream),
91
113
  Object.assign({}, this.encryptOptions, {
114
+ version: this._getVersion(this.options.version),
92
115
  log: this.logFile,
93
116
  }),
94
117
  );
@@ -125,6 +148,12 @@ class Recipe {
125
148
  this.info(info);
126
149
  }
127
150
 
151
+ /**
152
+ * Map a Recipe version option to a writer PDF version constant.
153
+ * @private
154
+ * @param {number} [version] - 1.0 through 1.7 or 2.0; other values use 1.7.
155
+ * @returns {number} The matching `ePDFVersion*` constant.
156
+ */
128
157
  _getVersion(version) {
129
158
  const supportedVersions = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 2.0];
130
159
  if (!supportedVersions.includes(version)) {
@@ -135,6 +164,11 @@ class Recipe {
135
164
  return version;
136
165
  }
137
166
 
167
+ /**
168
+ * The current drawing position in Recipe coordinates, where y grows downward
169
+ * from the top edge of the page.
170
+ * @returns {{x: number, y: number}} The current position.
171
+ */
138
172
  get position() {
139
173
  const { ox, oy } = this._reverseCoordinate(
140
174
  this._position.x,
@@ -147,10 +181,12 @@ class Recipe {
147
181
  }
148
182
 
149
183
  /**
150
- * Read PDF metadata.
151
- * @param {string|Buffer} [inSrc] - An optional PDF source to read instead of the recipe source.
184
+ * Read PDF metadata: the page count and, keyed by one-based page number,
185
+ * each page's media box, rotation, layout and size.
186
+ * @param {string|Buffer} [inSrc] - A PDF path or Buffer to read instead of
187
+ * the recipe source. Reading another PDF does not change the recipe state.
152
188
  * @returns {Object} The PDF metadata.
153
- * @throws {Error} If the PDF cannot be read.
189
+ * @throws {Error} If the PDF cannot be read or has no pages.
154
190
  */
155
191
  read(inSrc) {
156
192
  const isForExternal = inSrc ? true : false;
@@ -158,8 +194,8 @@ class Recipe {
158
194
  let isAdopted = false;
159
195
  try {
160
196
  let src = isForExternal ? inSrc : this.src;
161
- if (this.isBufferSrc) {
162
- src = new muhammara.PDFRStreamForBuffer(this.src);
197
+ if (src instanceof Buffer) {
198
+ src = new muhammara.PDFRStreamForBuffer(src);
163
199
  }
164
200
  pdfReader = muhammara.createReader(src, this.encryptOptions);
165
201
  const pages = pdfReader.getPagesCount();
@@ -179,14 +215,14 @@ class Recipe {
179
215
  let side1 = Math.abs(dimensions[2] - dimensions[0]);
180
216
  let side2 = Math.abs(dimensions[3] - dimensions[1]);
181
217
  if (side1 > side2 && rotate % 180 === 0) {
182
- layout = "landscape";
218
+ layout = PageLayout.LANDSCAPE;
183
219
  } else if (side1 < side2 && rotate % 180 !== 0) {
184
- layout = "landscape";
220
+ layout = PageLayout.LANDSCAPE;
185
221
  } else {
186
- layout = "portrait";
222
+ layout = PageLayout.PORTRAIT;
187
223
  }
188
224
 
189
- if (layout === "landscape") {
225
+ if (layout === PageLayout.LANDSCAPE) {
190
226
  width = side1 > side2 ? side1 : side2;
191
227
  height = side1 > side2 ? side2 : side1;
192
228
  } else {
@@ -268,8 +304,13 @@ class Recipe {
268
304
  * rethrow the original error.
269
305
  * @function
270
306
  * @memberof Recipe
271
- * @param {function} [callback] - The callback function.
272
- * @returns {*} The callback result, if a callback is provided.
307
+ * @param {function((Buffer|string)=): *} [callback] - Called when the PDF is
308
+ * finished: with the output Buffer for a Buffer source without an output
309
+ * path, with the output path for a Buffer source with one, and without an
310
+ * argument for a path source.
311
+ * @returns {*} The callback result, or undefined without a callback.
312
+ * @throws {Error} If pages are being deleted while a page is still open.
313
+ * @throws {Error} If finalization fails; later calls rethrow the same error.
273
314
  */
274
315
  endPDF(callback) {
275
316
  if (this.endError) {
@@ -410,6 +451,7 @@ class Recipe {
410
451
  * @param {string|Function} key Name assigned to the callback. When a named function is
411
452
  * registered, and its given name is what is to be used to access it, the key is unnecessary.
412
453
  * @param {Function} [callback] Callback procedure that can be accessed through MuhammaraJS.
454
+ * It is added to the shared Recipe prototype, so every Recipe instance gets it.
413
455
  * @throws {string} If the callback function is unnamed when no key is provided.
414
456
  * @throws {string} If the key conflicts with an existing Recipe prototype member.
415
457
  * @throws {string} If the callback is not a function.
@@ -438,8 +480,13 @@ class Recipe {
438
480
  }
439
481
  }
440
482
 
483
+ /**
484
+ * Install every method exported by lib/recipe/*.js on the Recipe prototype.
485
+ * @returns {void}
486
+ * @throws {string} If two recipe modules export the same member.
487
+ */
441
488
  function loadPrototypes() {
442
- const ignores = ["utils.js", "xObjectForm.js"];
489
+ const ignores = ["utils.js", "xObjectForm.js", "page-references.js"];
443
490
  fs.readdirSync(path.join(__dirname, "recipe"))
444
491
  .filter((file) => {
445
492
  return file[0] != "." && !ignores.includes(file);
@@ -456,4 +503,8 @@ function loadPrototypes() {
456
503
  }
457
504
 
458
505
  loadPrototypes();
506
+
507
+ // Named values for string options, for example Recipe.AnnotSubtype.HIGHLIGHT.
508
+ Object.assign(Recipe, require("./recipe-constants"));
509
+
459
510
  module.exports = Recipe;