@muhammara/native-core 7.0.0 → 7.1.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.
package/index.js CHANGED
@@ -1,11 +1,41 @@
1
1
  "use strict";
2
2
 
3
+ var fs = require("fs");
3
4
  var path = require("path");
4
5
  var fontText = require("./lib/font-text");
6
+ var { createRecipe } = require("./lib/Recipe");
5
7
 
6
- // Addons whose reader already decodes text, so a second createMuhammara call
7
- // on the same addon does not wrap it twice.
8
- var decodingAddons = new WeakSet();
8
+ // The addon's own functions, kept on the addon so every createMuhammara call
9
+ // wraps the originals, never an earlier wrapper. Module state would not do:
10
+ // Jest loads this module again for every test file while the addon stays
11
+ // loaded. Symbol.for keys are shared by those module registries.
12
+ var nativeReaderClass = Symbol.for("@muhammara/native-core:PDFReader");
13
+ var nativeExtractPageText = Symbol.for(
14
+ "@muhammara/native-core:extractPageText",
15
+ );
16
+ var nativeRecryptAsync = Symbol.for("@muhammara/native-core:recryptAsync");
17
+ var nativeRegisterBufferReadStream = Symbol.for(
18
+ "@muhammara/native-core:registerBufferReadStream",
19
+ );
20
+ // The addon rejects with the same message for a path it cannot open.
21
+ var pathTooLong =
22
+ "A path is too long for this system; recryptAsync() opens its files by " +
23
+ "absolute path, so use a shorter path or working directory";
24
+
25
+ /**
26
+ * Keep an addon's own function under a hidden key, unless it is already kept.
27
+ *
28
+ * @param {object} target The object holding the function.
29
+ * @param {symbol} key The hidden key.
30
+ * @param {*} value The function to keep.
31
+ * @returns {*} The kept function.
32
+ */
33
+ function keepNative(target, key, value) {
34
+ if (!Object.prototype.hasOwnProperty.call(target, key)) {
35
+ Object.defineProperty(target, key, { value: value });
36
+ }
37
+ return target[key];
38
+ }
9
39
 
10
40
  /**
11
41
  * Make `extractPageText()` add decoded Unicode `text` to each element. The
@@ -18,17 +48,19 @@ var decodingAddons = new WeakSet();
18
48
  * text would silently be missing.
19
49
  */
20
50
  function decodeExtractedText(muhammara) {
21
- if (decodingAddons.has(muhammara)) return;
22
- var PDFReader = muhammara.PDFReader;
51
+ var PDFReader = keepNative(muhammara, nativeReaderClass, muhammara.PDFReader);
23
52
  if (typeof PDFReader !== "function") {
24
53
  throw new Error(
25
54
  "The muhammara native addon does not export PDFReader; rebuild it from this version's sources",
26
55
  );
27
56
  }
28
57
  delete muhammara.PDFReader;
29
- decodingAddons.add(muhammara);
30
58
 
31
- var extractPageText = PDFReader.prototype.extractPageText;
59
+ var extractPageText = keepNative(
60
+ PDFReader.prototype,
61
+ nativeExtractPageText,
62
+ PDFReader.prototype.extractPageText,
63
+ );
32
64
  /**
33
65
  * Extract a page's text operations with their decoded Unicode `text`.
34
66
  *
@@ -59,6 +91,192 @@ function decodeExtractedText(muhammara) {
59
91
  };
60
92
  }
61
93
 
94
+ /**
95
+ * Let readers read a `PDFRStreamForBuffer` through its fields instead of
96
+ * calling `read()` for every byte the parser takes. The addon checks that a
97
+ * stream's prototype and methods are the registered ones, so subclasses and
98
+ * patched streams keep their calls. The hook is removed from the public API
99
+ * and kept under a hidden key, as each module registry registers its own
100
+ * class.
101
+ *
102
+ * @param {object} muhammara The native addon, with `PDFRStreamForBuffer` set.
103
+ * @returns {void}
104
+ */
105
+ function registerBufferReadStream(muhammara) {
106
+ var register = keepNative(
107
+ muhammara,
108
+ nativeRegisterBufferReadStream,
109
+ muhammara.registerBufferReadStream,
110
+ );
111
+ delete muhammara.registerBufferReadStream;
112
+ if (typeof register === "function") {
113
+ register(muhammara.PDFRStreamForBuffer.prototype);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Make a path absolute, naming the file that opening it now would. POSIX
119
+ * follows a symlink before it applies a later `..`, so `link/..` is the parent
120
+ * of the link's target, and `path.resolve()` would drop both instead; only the
121
+ * working directory is put in front. Windows removes `..` from the text, as
122
+ * `path.resolve()` does, which also fixes the drive of `\file` and `C:file`.
123
+ *
124
+ * @param {string} file A path.
125
+ * @returns {string} An absolute path naming the same file.
126
+ */
127
+ function resolveLikeOpen(file) {
128
+ if (process.platform === "win32") return path.resolve(file);
129
+ if (path.isAbsolute(file)) return file;
130
+ var directory = workingDirectory();
131
+ return directory.endsWith("/") ? directory + file : directory + "/" + file;
132
+ }
133
+
134
+ /**
135
+ * Read the working directory as it is now. `process.cwd()` keeps the path it
136
+ * read until the next `process.chdir()`, so it still names the old place
137
+ * after the directory is renamed, while a relative path opens in the
138
+ * directory itself.
139
+ *
140
+ * @returns {string} The working directory.
141
+ * @throws {Error} If the working directory has no path, as when it was
142
+ * removed, or its path is too long for the system.
143
+ */
144
+ function workingDirectory() {
145
+ try {
146
+ return fs.realpathSync.native(".");
147
+ } catch (error) {
148
+ // Node's permission model may deny reading the directory, while
149
+ // process.cwd() still reports it. Other failures leave no path to name
150
+ // the directory by, as when it was removed, and process.cwd() would only
151
+ // repeat a path it read before.
152
+ if (error.code === "ERR_ACCESS_DENIED") return process.cwd();
153
+ // musl and macOS cannot name a working directory whose path is too long.
154
+ if (error.code === "ENAMETOOLONG" || error.code === "ERANGE") {
155
+ throw new Error(pathTooLong);
156
+ }
157
+ throw error;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Reject the way the addon settles: with a built-in promise, even when
163
+ * `globalThis.Promise` is replaced.
164
+ *
165
+ * @async
166
+ * @param {Error} error The rejection reason.
167
+ * @returns {Promise<never>} A promise rejected with `error`.
168
+ */
169
+ async function rejected(error) {
170
+ throw error;
171
+ }
172
+
173
+ /**
174
+ * Make `recryptAsync()` resolve relative paths, including `options.log`, when
175
+ * it is called. A queued job opens its files later, and the working directory
176
+ * may change in between.
177
+ *
178
+ * @param {object} muhammara The native addon.
179
+ * @returns {void}
180
+ */
181
+ function resolveRecryptAsyncPaths(muhammara) {
182
+ if (typeof muhammara.recryptAsync !== "function") return;
183
+ var recryptAsync = keepNative(
184
+ muhammara,
185
+ nativeRecryptAsync,
186
+ muhammara.recryptAsync,
187
+ );
188
+ /**
189
+ * Re-encrypt a PDF on libuv's thread pool.
190
+ *
191
+ * @param {string|object} source The source path or read stream.
192
+ * @param {string|object} target The output path or write stream.
193
+ * @param {object|null} [options] The source password and encryption
194
+ * settings; `null` means none.
195
+ * @returns {Promise<void>} Settles once the output is written.
196
+ * @throws {TypeError} If the arguments are wrong.
197
+ */
198
+ muhammara.recryptAsync = function (source, target, options) {
199
+ var args = Array.prototype.slice.call(arguments);
200
+ // Read `log` as the addon does: only when the options have one, and from
201
+ // functions too.
202
+ var log =
203
+ options &&
204
+ (typeof options === "object" || typeof options === "function") &&
205
+ "log" in options
206
+ ? options.log
207
+ : undefined;
208
+ if (
209
+ typeof source === "string" &&
210
+ typeof target === "string" &&
211
+ source !== "" &&
212
+ target !== ""
213
+ ) {
214
+ try {
215
+ args[0] = resolveLikeOpen(source);
216
+ args[1] = resolveLikeOpen(target);
217
+ } catch (error) {
218
+ // The working directory was removed, so recrypt() could not open
219
+ // these paths either. A job given them unresolved would open them in
220
+ // whatever directory is current when it runs.
221
+ return rejected(error);
222
+ }
223
+ }
224
+ if (typeof log === "string" && log !== "") {
225
+ var resolved;
226
+ try {
227
+ resolved = resolveLikeOpen(log);
228
+ } catch (error) {
229
+ // recrypt() cannot create its log in a removed working directory, so
230
+ // it logs nothing and still writes the PDF. The job does the same.
231
+ if (error.code !== "ENOENT") return rejected(error);
232
+ }
233
+ args[2] = withLog(options, resolved);
234
+ }
235
+ return recryptAsync.apply(this, args);
236
+ };
237
+ }
238
+
239
+ /**
240
+ * View options with `log` replaced, reading every other property from the
241
+ * original with the original as `this`. Copying would lose getters, inherited
242
+ * properties, and Proxy-backed values that the addon reads with a plain
243
+ * property get. Inheriting from the options would run getters against the
244
+ * wrong object, and a Proxy targeting the options would throw when `log` is
245
+ * read-only and non-configurable, so the Proxy targets an empty object.
246
+ *
247
+ * @param {object} options The caller's options.
248
+ * @param {string|undefined} log The log path to report instead, or
249
+ * `undefined` for no log.
250
+ * @returns {object} A Proxy reading through to `options`.
251
+ */
252
+ function withLog(options, log) {
253
+ return new Proxy(
254
+ {},
255
+ {
256
+ /**
257
+ * Report `log` and every key the options have.
258
+ *
259
+ * @param {object} target The empty Proxy target.
260
+ * @param {string|symbol} key The property name.
261
+ * @returns {boolean} Whether the property exists.
262
+ */
263
+ has: function (target, key) {
264
+ return key === "log" || Reflect.has(options, key);
265
+ },
266
+ /**
267
+ * Read `log` as the replacement and every other key from the options.
268
+ *
269
+ * @param {object} target The empty Proxy target.
270
+ * @param {string|symbol} key The property name.
271
+ * @returns {*} The property value.
272
+ */
273
+ get: function (target, key) {
274
+ return key === "log" ? log : Reflect.get(options, key);
275
+ },
276
+ },
277
+ );
278
+ }
279
+
62
280
  /**
63
281
  * Attach the shared JavaScript API to an implementation package's loaded addon.
64
282
  *
@@ -66,9 +284,6 @@ function decodeExtractedText(muhammara) {
66
284
  * @returns {object} The public MuhammaraJS API.
67
285
  */
68
286
  exports.createMuhammara = function createMuhammara(muhammara) {
69
- var bindingModule = require.resolve("./lib/muhammara");
70
- var recipeDirectory = path.join(__dirname, "lib", "recipe") + path.sep;
71
-
72
287
  /**
73
288
  * Returns the writer's event emitter, created on first use.
74
289
  * @returns {import("events").EventEmitter} The emitter for writer events.
@@ -152,10 +367,12 @@ exports.createMuhammara = function createMuhammara(muhammara) {
152
367
  return this;
153
368
  };
154
369
  decodeExtractedText(muhammara);
370
+ resolveRecryptAsyncPaths(muhammara);
155
371
  muhammara.PDFStreamForResponse = require("./lib/PDFStreamForResponse");
156
372
  muhammara.PDFWStreamForFile = require("./lib/PDFWStreamForFile");
157
373
  muhammara.PDFRStreamForFile = require("./lib/PDFRStreamForFile");
158
374
  muhammara.PDFRStreamForBuffer = require("./lib/PDFRStreamForBuffer");
375
+ registerBufferReadStream(muhammara);
159
376
  muhammara.PDFWStreamForBuffer = require("./lib/PDFWStreamForBuffer");
160
377
  muhammara.DrawingPathType = Object.freeze({
161
378
  STROKE: "stroke",
@@ -203,28 +420,9 @@ exports.createMuhammara = function createMuhammara(muhammara) {
203
420
  eTokenSeparatorNone: muhammara.eTokenSeparatorNone,
204
421
  });
205
422
 
206
- Object.keys(require.cache).forEach(function (filename) {
207
- if (
208
- filename === require.resolve("./lib/Recipe") ||
209
- filename.startsWith(recipeDirectory)
210
- ) {
211
- delete require.cache[filename];
212
- }
213
- });
214
-
215
- // Recipe modules historically import lib/muhammara directly. Supply this
216
- // factory's addon while they initialize, without retaining global addon state.
217
- require.cache[bindingModule] = {
218
- id: bindingModule,
219
- filename: bindingModule,
220
- loaded: true,
221
- exports: muhammara,
222
- };
223
- try {
224
- muhammara.Recipe = require("./lib/Recipe");
225
- } finally {
226
- delete require.cache[bindingModule];
227
- }
423
+ // A factory instead of module state, so module systems without Node's
424
+ // require.cache (Jest, bundlers) load Recipe too.
425
+ muhammara.Recipe = createRecipe(muhammara);
228
426
 
229
427
  return muhammara;
230
428
  };
package/lib/Recipe.js CHANGED
@@ -1,4 +1,3 @@
1
- const muhammara = require("./muhammara");
2
1
  const path = require("path");
3
2
  const fs = require("fs");
4
3
  const PDFWStreamForBuffer = require("./PDFWStreamForBuffer");
@@ -61,7 +60,6 @@ class Recipe {
61
60
  this.filename = path.basename(this.src);
62
61
  }
63
62
  }
64
- this.muhammara = muhammara;
65
63
  this.logFile = "muhammara-error.log";
66
64
 
67
65
  this.textMarkupAnnotations = [
@@ -101,14 +99,14 @@ class Recipe {
101
99
  _createWriter() {
102
100
  if (this.isNewPDF) {
103
101
  if (!this.isBufferSrc) {
104
- this.writer = muhammara.createWriter(
102
+ this.writer = this.muhammara.createWriter(
105
103
  this.output,
106
104
  Object.assign({}, this.encryptOptions, {
107
105
  version: this._getVersion(this.options.version),
108
106
  }),
109
107
  );
110
108
  } else {
111
- this.writer = muhammara.createWriter(
109
+ this.writer = this.muhammara.createWriter(
112
110
  this.outStream,
113
111
  Object.assign({}, this.encryptOptions, {
114
112
  version: this._getVersion(this.options.version),
@@ -120,15 +118,15 @@ class Recipe {
120
118
  this.read();
121
119
  try {
122
120
  if (this.isBufferSrc) {
123
- this.writer = muhammara.createWriterToModify(
124
- new muhammara.PDFRStreamForBuffer(this.src),
121
+ this.writer = this.muhammara.createWriterToModify(
122
+ new this.muhammara.PDFRStreamForBuffer(this.src),
125
123
  this.outStream,
126
124
  Object.assign({}, this.encryptOptions, {
127
125
  log: this.logFile,
128
126
  }),
129
127
  );
130
128
  } else {
131
- this.writer = muhammara.createWriterToModify(
129
+ this.writer = this.muhammara.createWriterToModify(
132
130
  this.src,
133
131
  Object.assign({}, this.encryptOptions, {
134
132
  modifiedFilePath: this.output,
@@ -159,7 +157,7 @@ class Recipe {
159
157
  if (!supportedVersions.includes(version)) {
160
158
  version = 1.7;
161
159
  }
162
- version = muhammara[`ePDFVersion${version * 10}`];
160
+ version = this.muhammara[`ePDFVersion${version * 10}`];
163
161
 
164
162
  return version;
165
163
  }
@@ -195,9 +193,9 @@ class Recipe {
195
193
  try {
196
194
  let src = isForExternal ? inSrc : this.src;
197
195
  if (src instanceof Buffer) {
198
- src = new muhammara.PDFRStreamForBuffer(src);
196
+ src = new this.muhammara.PDFRStreamForBuffer(src);
199
197
  }
200
- pdfReader = muhammara.createReader(src, this.encryptOptions);
198
+ pdfReader = this.muhammara.createReader(src, this.encryptOptions);
201
199
  const pages = pdfReader.getPagesCount();
202
200
  if (pages == 0) {
203
201
  // broken or modify password protected
@@ -352,8 +350,8 @@ class Recipe {
352
350
  const oldStream = this.outStream;
353
351
  this.outStream = new PDFWStreamForBuffer();
354
352
 
355
- this.writer = muhammara.createWriterToModify(
356
- new muhammara.PDFRStreamForBuffer(
353
+ this.writer = this.muhammara.createWriterToModify(
354
+ new this.muhammara.PDFRStreamForBuffer(
357
355
  oldStream.buffer || Buffer.alloc(0),
358
356
  ),
359
357
  this.outStream,
@@ -362,7 +360,7 @@ class Recipe {
362
360
  }),
363
361
  );
364
362
  } else {
365
- this.writer = muhammara.createWriterToModify(
363
+ this.writer = this.muhammara.createWriterToModify(
366
364
  this.output,
367
365
  Object.assign({}, this.encryptOptions, {
368
366
  modifiedFilePath: this.output,
@@ -509,4 +507,22 @@ loadPrototypes();
509
507
  // Named values for string options, for example Recipe.AnnotSubtype.HIGHLIGHT.
510
508
  Object.assign(Recipe, require("./recipe-constants"));
511
509
 
512
- module.exports = Recipe;
510
+ const SharedRecipe = Recipe;
511
+
512
+ /**
513
+ * Create the Recipe class for one loaded addon. Every call returns a new
514
+ * subclass, so each implementation package keeps its own addon and its own
515
+ * `register()`ed callbacks; the Recipe methods reach the addon through
516
+ * `this.muhammara`.
517
+ * @private
518
+ * @param {Object} muhammara - The public API built by createMuhammara().
519
+ * @returns {Function} The Recipe class bound to that addon.
520
+ */
521
+ function createRecipe(muhammara) {
522
+ class Recipe extends SharedRecipe {}
523
+ Recipe.prototype.muhammara = muhammara;
524
+ Object.assign(Recipe, require("./recipe-constants"));
525
+ return Recipe;
526
+ }
527
+
528
+ module.exports = { createRecipe };
@@ -1,4 +1,3 @@
1
- const muhammara = require("../muhammara");
2
1
  const utils = require("./utils");
3
2
 
4
3
  /**
@@ -18,6 +17,7 @@ const utils = require("./utils");
18
17
  * @throws {Error} If the source PDF cannot be read.
19
18
  */
20
19
  exports.appendPage = function appendPage(pdfSrc, pages = []) {
20
+ const muhammara = this.muhammara;
21
21
  if (this.deletedPages?.size) {
22
22
  throw new Error("appendPage cannot be combined with deletePage");
23
23
  }
@@ -1,4 +1,3 @@
1
- const muhammara = require("../muhammara");
2
1
  const fs = require("fs");
3
2
  const { Colorspace, ChromaCommand } = require("../recipe-constants");
4
3
 
@@ -162,7 +161,7 @@ function createColorSpaces(self, colorName, color) {
162
161
  .writeName(colorName)
163
162
  .writeName(altCS)
164
163
  .writeIndirectObjectReference(transformFunction)
165
- .endArray(muhammara.eTokenSeparatorEndLine)
164
+ .endArray(self.muhammara.eTokenSeparatorEndLine)
166
165
  .endIndirectObject();
167
166
  self.resumeContext();
168
167
  self.colorSpaces.set(key, colorSpaceID);
@@ -1,4 +1,3 @@
1
- const muhammara = require("../muhammara");
2
1
  const fs = require("fs");
3
2
  const { Permission } = require("../recipe-constants");
4
3
 
@@ -126,6 +125,6 @@ exports._encrypt = function _encrypt() {
126
125
 
127
126
  const tmp = this.output + ".tmp.pdf";
128
127
  fs.renameSync(this.output, tmp);
129
- muhammara.recrypt(tmp, this.output, this.encryption_);
128
+ this.muhammara.recrypt(tmp, this.output, this.encryption_);
130
129
  fs.unlinkSync(tmp);
131
130
  };
@@ -3,7 +3,8 @@
3
3
  * @name rotateContent
4
4
  * @function
5
5
  * @memberof Recipe#
6
- * @param {number} degrees - Clockwise rotation in degrees.
6
+ * @param {number} degrees - Counter-clockwise rotation in degrees. The
7
+ * `rotation` option of shapes, text, and images turns clockwise instead.
7
8
  * @param {number|"center"} [x=0] - Rotation origin x coordinate.
8
9
  * @param {number|"center"} [y=0] - Rotation origin y coordinate.
9
10
  * @returns {Recipe} The recipe instance.
@@ -1,5 +1,4 @@
1
1
  const fs = require("fs");
2
- const muhammara = require("../muhammara");
3
2
  const { HorizontalAlign, VerticalAlign } = require("../recipe-constants");
4
3
 
5
4
  /**
@@ -25,7 +24,7 @@ const { HorizontalAlign, VerticalAlign } = require("../recipe-constants");
25
24
  * "center center". Horizontal center moves the image left by half its width
26
25
  * and right moves it right by half; vertical center moves it up by half its
27
26
  * height and bottom moves it down by half from its top-left placement.
28
- * @param {number} [options.rotation] - Rotate the image, in degrees.
27
+ * @param {number} [options.rotation] - Clockwise rotation of the image, in degrees.
29
28
  * @param {number[]} [options.rotationOrigin] - [x, y] of the rotation origin;
30
29
  * the bottom-left corner of the drawn image when omitted.
31
30
  * @param {number} [options.skewX] - Skew angle off the x axis, in degrees.
@@ -67,7 +66,7 @@ exports.image = function image(imgSrc, x, y, options = {}) {
67
66
  const imgOptions = {
68
67
  index,
69
68
  transformation: {
70
- fit: muhammara.ImageFit.ALWAYS,
69
+ fit: this.muhammara.ImageFit.ALWAYS,
71
70
  // proportional: true,
72
71
  width,
73
72
  height,
@@ -208,14 +207,16 @@ function pdfPage(recipe, imgSrc, index) {
208
207
  let source = recipe._pdfSources.get(key);
209
208
  if (!source) {
210
209
  source = {
211
- pdf: recipe.writer.getImageType(imgSrc) === muhammara.PDFImageType.PDF,
210
+ pdf:
211
+ recipe.writer.getImageType(imgSrc) ===
212
+ recipe.muhammara.PDFImageType.PDF,
212
213
  pages: new Map(),
213
214
  };
214
215
  recipe._pdfSources.set(key, source);
215
216
  }
216
217
  if (!source.pdf) return null;
217
218
  if (!source.pages.has(index)) {
218
- source.pages.set(index, readPdfPage(imgSrc, index));
219
+ source.pages.set(index, readPdfPage(recipe.muhammara, imgSrc, index));
219
220
  }
220
221
  const page = source.pages.get(index);
221
222
  if (!page) throw new Error(`Unknown image: ${imgSrc}`);
@@ -225,12 +226,13 @@ function pdfPage(recipe, imgSrc, index) {
225
226
  /**
226
227
  * Read the media box and rotation of one page of a PDF file.
227
228
  * @private
229
+ * @param {Object} muhammara - The addon API the Recipe was created for.
228
230
  * @param {string} imgSrc - The PDF path.
229
231
  * @param {number} index - The zero-based page.
230
232
  * @returns {?{mediaBox: number[], rotate: number}} The page geometry, or null
231
233
  * when the PDF cannot be read or has no such page.
232
234
  */
233
- function readPdfPage(imgSrc, index) {
235
+ function readPdfPage(muhammara, imgSrc, index) {
234
236
  let reader;
235
237
  try {
236
238
  reader = muhammara.createReader(imgSrc);
@@ -319,6 +321,7 @@ const DEBUG_ANCHOR = "#ff0000";
319
321
  * @returns {void}
320
322
  */
321
323
  function drawFrame(recipe, nx, ny, width, height, options) {
324
+ const muhammara = recipe.muhammara;
322
325
  const pathOptions = recipe._getPathOptions(options, nx, ny);
323
326
  // A negative size mirrors the image into the other side of its corner.
324
327
  const left = Math.min(0, width);
@@ -1,5 +1,4 @@
1
1
  const fs = require("fs");
2
- const muhammara = require("../muhammara");
3
2
  var { recipeInfoKeys, standardInfoKeys } = require("../recipe-info");
4
3
 
5
4
  // Source Info dictionary keys that _readInfo() keeps as raw values.
@@ -27,11 +26,19 @@ var InfoFieldType = Object.freeze({
27
26
  ARRAY: "array",
28
27
  });
29
28
 
30
- var trappedValues = {
31
- True: muhammara.EInfoTrappedTrue,
32
- False: muhammara.EInfoTrappedFalse,
33
- Unknown: muhammara.EInfoTrappedUnknown,
34
- };
29
+ /**
30
+ * Map the Trapped names in cached info to the addon's EInfoTrapped values.
31
+ * @private
32
+ * @param {Object} muhammara - The addon API the Recipe was created for.
33
+ * @returns {Object<string, number>} The Trapped values by name.
34
+ */
35
+ function trappedValues(muhammara) {
36
+ return {
37
+ True: muhammara.EInfoTrappedTrue,
38
+ False: muhammara.EInfoTrappedFalse,
39
+ Unknown: muhammara.EInfoTrappedUnknown,
40
+ };
41
+ }
35
42
 
36
43
  /**
37
44
  * @name info
@@ -81,7 +88,7 @@ exports.info = function info(options) {
81
88
  exports._readInfo = function _readInfo() {
82
89
  if (!this.isNewPDF && !this.infoDictionary) {
83
90
  const copyFrom = this.isBufferSrc
84
- ? new muhammara.PDFRStreamForBuffer(this.src)
91
+ ? new this.muhammara.PDFRStreamForBuffer(this.src)
85
92
  : this.src;
86
93
  const copyCtx = this.writer.createPDFCopyingContext(copyFrom);
87
94
  try {
@@ -157,6 +164,7 @@ exports._readInfo = function _readInfo() {
157
164
  * @throws {Error} If the source information cannot be read.
158
165
  */
159
166
  exports._writeInfo = function _writeInfo() {
167
+ const trapped = trappedValues(this.muhammara);
160
168
  const options = this.toWriteInfo_ || {};
161
169
  const oldInfo = this._readInfo();
162
170
  /*
@@ -185,9 +193,8 @@ exports._writeInfo = function _writeInfo() {
185
193
 
186
194
  switch (key) {
187
195
  case CachedInfoKey.TRAPPED:
188
- if (trappedValues[oldInfo[CachedInfoKey.TRAPPED]] !== undefined) {
189
- infoDictionary.trapped =
190
- trappedValues[oldInfo[CachedInfoKey.TRAPPED]];
196
+ if (trapped[oldInfo[CachedInfoKey.TRAPPED]] !== undefined) {
197
+ infoDictionary.trapped = trapped[oldInfo[CachedInfoKey.TRAPPED]];
191
198
  }
192
199
  break;
193
200
  case CachedInfoKey.CREATION_DATE:
@@ -1,4 +1,3 @@
1
- const muhammara = require("../muhammara");
2
1
  const fs = require("fs");
3
2
  const utils = require("./utils");
4
3
  /**
@@ -61,7 +60,7 @@ exports._insertPages = function _insertPages() {
61
60
  const tmp = this.output + ".tmp.pdf";
62
61
  fs.renameSync(this.output, tmp);
63
62
 
64
- const pdfWriter = muhammara.createWriter(this.output);
63
+ const pdfWriter = this.muhammara.createWriter(this.output);
65
64
  let lastInsertedOriginal = 0;
66
65
  pagesForInsert.forEach((pageNumber) => {
67
66
  const toAppendPage = pageNumber - 1;