@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 +230 -32
- package/lib/Recipe.js +30 -14
- package/lib/recipe/appendPage.js +1 -1
- package/lib/recipe/colors.js +1 -2
- package/lib/recipe/encrypt.js +1 -2
- package/lib/recipe/graphics-state.js +2 -1
- package/lib/recipe/image.js +9 -6
- package/lib/recipe/info.js +17 -10
- package/lib/recipe/insertPage.js +1 -2
- package/lib/recipe/page.js +54 -11
- package/lib/recipe/shapes.js +3 -3
- package/lib/recipe/split.js +1 -2
- package/lib/recipe/table.js +2 -0
- package/lib/recipe/text.helper.js +6 -4
- package/lib/recipe/text.js +403 -40
- package/lib/recipe/utils.js +12 -0
- package/lib/recipe/vector-line.js +1 -2
- package/lib/recipe/vector-polygon.js +1 -1
- package/lib/recipe/vector.js +7 -8
- package/lib/recipe-constants.js +1 -1
- package/licenses/AdobeGlyphList.txt +29 -0
- package/muhammara.d.ts +59 -9
- package/package.json +2 -1
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
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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 };
|
package/lib/recipe/appendPage.js
CHANGED
|
@@ -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
|
}
|
package/lib/recipe/colors.js
CHANGED
|
@@ -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);
|
package/lib/recipe/encrypt.js
CHANGED
|
@@ -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 -
|
|
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.
|
package/lib/recipe/image.js
CHANGED
|
@@ -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] -
|
|
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:
|
|
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);
|
package/lib/recipe/info.js
CHANGED
|
@@ -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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 (
|
|
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:
|
package/lib/recipe/insertPage.js
CHANGED
|
@@ -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;
|