@muhammara/native-core 7.0.0-beta.4 → 7.0.0-rc.2
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/README.md +57 -7
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/index.js +120 -1
- package/lib/PDFRStreamForBuffer.js +52 -7
- package/lib/PDFRStreamForFile.js +56 -7
- package/lib/PDFStreamForResponse.js +22 -5
- package/lib/PDFWStreamForBuffer.js +49 -10
- package/lib/PDFWStreamForFile.js +26 -5
- package/lib/Recipe.js +91 -38
- package/lib/content-stream.js +503 -0
- package/lib/css-colors.js +149 -0
- package/lib/font-text.js +1461 -0
- package/lib/glyph-list.js +1127 -0
- package/lib/recipe/annotation.js +464 -101
- package/lib/recipe/appendPage.js +2 -0
- package/lib/recipe/colors.js +132 -35
- package/lib/recipe/coordinate.js +82 -16
- package/lib/recipe/encrypt.js +32 -13
- package/lib/recipe/font.js +70 -25
- package/lib/recipe/graphics-state.js +3 -2
- package/lib/recipe/htmlToTextObjects.js +121 -24
- package/lib/recipe/image.js +385 -39
- package/lib/recipe/info.js +93 -22
- package/lib/recipe/insertPage.js +12 -3
- package/lib/recipe/overlay.js +4 -2
- package/lib/recipe/page-references.js +436 -0
- package/lib/recipe/page.js +363 -262
- package/lib/recipe/parameters.js +8 -1
- package/lib/recipe/replaceText.js +464 -30
- package/lib/recipe/shapes.js +418 -45
- package/lib/recipe/split.js +6 -2
- package/lib/recipe/table.js +41 -8
- package/lib/recipe/text.helper.js +172 -8
- package/lib/recipe/text.js +294 -56
- package/lib/recipe/utils.js +17 -6
- package/lib/recipe/vector-line.js +101 -39
- package/lib/recipe/vector-polygon.js +18 -6
- package/lib/recipe/vector.helper.js +156 -14
- package/lib/recipe/vector.js +156 -52
- package/lib/recipe/xObjectForm.js +73 -10
- package/lib/recipe-constants.js +373 -0
- package/lib/recipe-options.js +47 -0
- package/muhammara.d.ts +3501 -160
- package/package.json +2 -3
package/lib/Recipe.js
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
const muhammara = require("./muhammara");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const fs = require("fs");
|
|
4
|
-
const
|
|
4
|
+
const PDFWStreamForBuffer = require("./PDFWStreamForBuffer");
|
|
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
|
|
10
|
+
* @desc Create a new PDF, or open an existing one for editing.
|
|
10
11
|
* @namespace
|
|
11
12
|
* @constructor
|
|
12
|
-
* @param {string|Buffer} src -
|
|
13
|
-
*
|
|
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
|
|
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 {
|
|
22
|
-
*
|
|
23
|
-
* @param {string} [options.
|
|
24
|
-
* @param {string} [options.
|
|
25
|
-
* @param {string
|
|
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,15 +37,23 @@ 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() ===
|
|
34
|
-
(this.isBufferSrc && this.src.equals(Buffer.from(
|
|
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
|
-
this.outStream = new
|
|
56
|
+
this.outStream = new PDFWStreamForBuffer();
|
|
42
57
|
this.output = output;
|
|
43
58
|
} else {
|
|
44
59
|
this.output = output || src;
|
|
@@ -50,10 +65,10 @@ class Recipe {
|
|
|
50
65
|
this.logFile = "muhammara-error.log";
|
|
51
66
|
|
|
52
67
|
this.textMarkupAnnotations = [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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) {
|
|
@@ -87,8 +109,9 @@ class Recipe {
|
|
|
87
109
|
);
|
|
88
110
|
} else {
|
|
89
111
|
this.writer = muhammara.createWriter(
|
|
90
|
-
|
|
112
|
+
this.outStream,
|
|
91
113
|
Object.assign({}, this.encryptOptions, {
|
|
114
|
+
version: this._getVersion(this.options.version),
|
|
92
115
|
log: this.logFile,
|
|
93
116
|
}),
|
|
94
117
|
);
|
|
@@ -99,7 +122,7 @@ class Recipe {
|
|
|
99
122
|
if (this.isBufferSrc) {
|
|
100
123
|
this.writer = muhammara.createWriterToModify(
|
|
101
124
|
new muhammara.PDFRStreamForBuffer(this.src),
|
|
102
|
-
|
|
125
|
+
this.outStream,
|
|
103
126
|
Object.assign({}, this.encryptOptions, {
|
|
104
127
|
log: this.logFile,
|
|
105
128
|
}),
|
|
@@ -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
|
-
*
|
|
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 (
|
|
162
|
-
src = new muhammara.PDFRStreamForBuffer(
|
|
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 =
|
|
218
|
+
layout = PageLayout.LANDSCAPE;
|
|
183
219
|
} else if (side1 < side2 && rotate % 180 !== 0) {
|
|
184
|
-
layout =
|
|
220
|
+
layout = PageLayout.LANDSCAPE;
|
|
185
221
|
} else {
|
|
186
|
-
layout =
|
|
222
|
+
layout = PageLayout.PORTRAIT;
|
|
187
223
|
}
|
|
188
224
|
|
|
189
|
-
if (layout ===
|
|
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] -
|
|
272
|
-
*
|
|
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) {
|
|
@@ -278,7 +319,7 @@ class Recipe {
|
|
|
278
319
|
if (this.ended) {
|
|
279
320
|
if (!callback) return;
|
|
280
321
|
if (!this.isBufferSrc) return callback();
|
|
281
|
-
return callback(this.output || this.outStream.
|
|
322
|
+
return callback(this.output || this.outStream.buffer || Buffer.alloc(0));
|
|
282
323
|
}
|
|
283
324
|
var deletingPages = Boolean(this.deletedPages?.size);
|
|
284
325
|
var deletionState = deletingPages
|
|
@@ -309,11 +350,13 @@ class Recipe {
|
|
|
309
350
|
) {
|
|
310
351
|
if (this.isBufferSrc) {
|
|
311
352
|
const oldStream = this.outStream;
|
|
312
|
-
this.outStream = new
|
|
353
|
+
this.outStream = new PDFWStreamForBuffer();
|
|
313
354
|
|
|
314
355
|
this.writer = muhammara.createWriterToModify(
|
|
315
|
-
new muhammara.PDFRStreamForBuffer(
|
|
316
|
-
|
|
356
|
+
new muhammara.PDFRStreamForBuffer(
|
|
357
|
+
oldStream.buffer || Buffer.alloc(0),
|
|
358
|
+
),
|
|
359
|
+
this.outStream,
|
|
317
360
|
Object.assign({}, this.encryptOptions, {
|
|
318
361
|
log: this.logFile,
|
|
319
362
|
}),
|
|
@@ -360,7 +403,7 @@ class Recipe {
|
|
|
360
403
|
}
|
|
361
404
|
|
|
362
405
|
if (this.isBufferSrc && this.output) {
|
|
363
|
-
fs.writeFileSync(this.output, this.outStream.
|
|
406
|
+
fs.writeFileSync(this.output, this.outStream.buffer || Buffer.alloc(0));
|
|
364
407
|
}
|
|
365
408
|
|
|
366
409
|
this.ended = true;
|
|
@@ -395,7 +438,7 @@ class Recipe {
|
|
|
395
438
|
if (this.output) {
|
|
396
439
|
return callback(this.output);
|
|
397
440
|
} else {
|
|
398
|
-
return callback(this.outStream.
|
|
441
|
+
return callback(this.outStream.buffer || Buffer.alloc(0));
|
|
399
442
|
}
|
|
400
443
|
} else {
|
|
401
444
|
return callback();
|
|
@@ -410,6 +453,7 @@ class Recipe {
|
|
|
410
453
|
* @param {string|Function} key Name assigned to the callback. When a named function is
|
|
411
454
|
* registered, and its given name is what is to be used to access it, the key is unnecessary.
|
|
412
455
|
* @param {Function} [callback] Callback procedure that can be accessed through MuhammaraJS.
|
|
456
|
+
* It is added to the shared Recipe prototype, so every Recipe instance gets it.
|
|
413
457
|
* @throws {string} If the callback function is unnamed when no key is provided.
|
|
414
458
|
* @throws {string} If the key conflicts with an existing Recipe prototype member.
|
|
415
459
|
* @throws {string} If the callback is not a function.
|
|
@@ -438,8 +482,13 @@ class Recipe {
|
|
|
438
482
|
}
|
|
439
483
|
}
|
|
440
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Install every method exported by lib/recipe/*.js on the Recipe prototype.
|
|
487
|
+
* @returns {void}
|
|
488
|
+
* @throws {string} If two recipe modules export the same member.
|
|
489
|
+
*/
|
|
441
490
|
function loadPrototypes() {
|
|
442
|
-
const ignores = ["utils.js", "xObjectForm.js"];
|
|
491
|
+
const ignores = ["utils.js", "xObjectForm.js", "page-references.js"];
|
|
443
492
|
fs.readdirSync(path.join(__dirname, "recipe"))
|
|
444
493
|
.filter((file) => {
|
|
445
494
|
return file[0] != "." && !ignores.includes(file);
|
|
@@ -456,4 +505,8 @@ function loadPrototypes() {
|
|
|
456
505
|
}
|
|
457
506
|
|
|
458
507
|
loadPrototypes();
|
|
508
|
+
|
|
509
|
+
// Named values for string options, for example Recipe.AnnotSubtype.HIGHLIGHT.
|
|
510
|
+
Object.assign(Recipe, require("./recipe-constants"));
|
|
511
|
+
|
|
459
512
|
module.exports = Recipe;
|