@muhammara/native-core 7.0.0-beta.3 → 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.
- 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 +86 -30
- 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 +482 -105
- package/lib/recipe/appendPage.js +8 -1
- 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 +154 -21
- package/lib/recipe/image.js +34 -9
- 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 +379 -275
- package/lib/recipe/parameters.js +11 -2
- 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 +260 -173
- package/lib/recipe/text.helper.js +176 -10
- package/lib/recipe/text.js +420 -115
- package/lib/recipe/utils.js +66 -3
- package/lib/recipe/vector-line.js +101 -39
- package/lib/recipe/vector-polygon.js +18 -6
- package/lib/recipe/vector.helper.js +150 -13
- package/lib/recipe/vector.js +150 -53
- package/lib/recipe/xObjectForm.js +73 -10
- package/lib/recipe-constants.js +373 -0
- package/muhammara.d.ts +3487 -152
- 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
|
|
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,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() ===
|
|
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
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
|
-
|
|
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) {
|
|
@@ -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
|
-
*
|
|
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 {
|
|
@@ -263,12 +299,18 @@ class Recipe {
|
|
|
263
299
|
/**
|
|
264
300
|
* End the pdfDoc. Finalization happens once; later calls do not rewrite the
|
|
265
301
|
* PDF and invoke the callback with the completed output when applicable.
|
|
266
|
-
*
|
|
267
|
-
*
|
|
302
|
+
* An active page is finished first, so a forgotten endPage() does not cost
|
|
303
|
+
* that page. A failed finalization retires the Recipe and later calls
|
|
304
|
+
* rethrow the original error.
|
|
268
305
|
* @function
|
|
269
306
|
* @memberof Recipe
|
|
270
|
-
* @param {function} [callback] -
|
|
271
|
-
*
|
|
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.
|
|
272
314
|
*/
|
|
273
315
|
endPDF(callback) {
|
|
274
316
|
if (this.endError) {
|
|
@@ -292,6 +334,10 @@ class Recipe {
|
|
|
292
334
|
if (deletingPages && this.page) {
|
|
293
335
|
throw new Error("Finish the current page before endPDF");
|
|
294
336
|
}
|
|
337
|
+
// The writer cannot finalize around an open content stream: it drops the
|
|
338
|
+
// page and emits a catalog with no page tree. Finish the page for the
|
|
339
|
+
// caller instead, before finalization retires the Recipe.
|
|
340
|
+
if (this.page) this.endPage();
|
|
295
341
|
try {
|
|
296
342
|
this._deletePages();
|
|
297
343
|
this._writeInfo();
|
|
@@ -405,6 +451,7 @@ class Recipe {
|
|
|
405
451
|
* @param {string|Function} key Name assigned to the callback. When a named function is
|
|
406
452
|
* registered, and its given name is what is to be used to access it, the key is unnecessary.
|
|
407
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.
|
|
408
455
|
* @throws {string} If the callback function is unnamed when no key is provided.
|
|
409
456
|
* @throws {string} If the key conflicts with an existing Recipe prototype member.
|
|
410
457
|
* @throws {string} If the callback is not a function.
|
|
@@ -433,8 +480,13 @@ class Recipe {
|
|
|
433
480
|
}
|
|
434
481
|
}
|
|
435
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
|
+
*/
|
|
436
488
|
function loadPrototypes() {
|
|
437
|
-
const ignores = ["utils.js", "xObjectForm.js"];
|
|
489
|
+
const ignores = ["utils.js", "xObjectForm.js", "page-references.js"];
|
|
438
490
|
fs.readdirSync(path.join(__dirname, "recipe"))
|
|
439
491
|
.filter((file) => {
|
|
440
492
|
return file[0] != "." && !ignores.includes(file);
|
|
@@ -451,4 +503,8 @@ function loadPrototypes() {
|
|
|
451
503
|
}
|
|
452
504
|
|
|
453
505
|
loadPrototypes();
|
|
506
|
+
|
|
507
|
+
// Named values for string options, for example Recipe.AnnotSubtype.HIGHLIGHT.
|
|
508
|
+
Object.assign(Recipe, require("./recipe-constants"));
|
|
509
|
+
|
|
454
510
|
module.exports = Recipe;
|