@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/README.md
CHANGED
|
@@ -1,9 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @muhammara/native-core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@muhammara/native)
|
|
4
|
+
[](https://github.com/julianhille/MuhammaraJS/blob/develop/LICENSE)
|
|
5
|
+
[](https://muhammarajs.readthedocs.io/)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
**This is an internal package. Do not install it directly.**
|
|
8
|
+
|
|
9
|
+
`@muhammara/native-core` holds the JavaScript half of the MuhammaraJS native
|
|
10
|
+
packages: the low-level API wrapper, the Recipe API, and the TypeScript
|
|
11
|
+
declarations. It contains no native binary and does nothing on its own. Both
|
|
12
|
+
user-facing native packages depend on it and install it for you:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @muhammara/native # prebuilt binary
|
|
16
|
+
npm install @muhammara/native-with-source # prebuilt or local source build
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Its version always matches the native package that depends on it, so do not
|
|
20
|
+
pin or upgrade it separately.
|
|
21
|
+
|
|
22
|
+
## How The Packages Fit Together
|
|
23
|
+
|
|
24
|
+
MuhammaraJS is one PDF engine shipped in two ways. The same C++ library,
|
|
25
|
+
[PDF-Writer](https://github.com/galkahana/PDF-Writer), sits at the bottom of
|
|
26
|
+
every package; what differs is how it reaches your JavaScript.
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
PDF-Writer (C++)
|
|
30
|
+
┌────────────┴────────────┐
|
|
31
|
+
Node.js native addon WebAssembly build
|
|
32
|
+
│ │
|
|
33
|
+
@muhammara/native-core @muhammara/wasm
|
|
34
|
+
(shared JS, internal) (browsers, Workers, Node.js)
|
|
35
|
+
┌───────┴────────┐
|
|
36
|
+
@muhammara/native @muhammara/native-with-source
|
|
37
|
+
(prebuilt only) (prebuilt or source build)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Package | Install it when |
|
|
41
|
+
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
42
|
+
| [`@muhammara/native`](https://www.npmjs.com/package/@muhammara/native) | You run Node.js on a platform with a prebuilt binary. This is the default choice. |
|
|
43
|
+
| [`@muhammara/native-with-source`](https://www.npmjs.com/package/@muhammara/native-with-source) | No prebuilt matches your platform, or Electron must rebuild the addon. |
|
|
44
|
+
| [`@muhammara/wasm`](https://www.npmjs.com/package/@muhammara/wasm) | You work in a browser or Web Worker, or cannot load native addons (for example some serverless runtimes). |
|
|
45
|
+
| [`@muhammara/native-core`](https://www.npmjs.com/package/@muhammara/native-core) | Never directly. Both native packages depend on it and install it for you. |
|
|
46
|
+
|
|
47
|
+
Both native packages expose the identical API, so switching between them is a
|
|
48
|
+
dependency change, not a code change. The Wasm package produces the same PDFs,
|
|
49
|
+
but it is byte-first (`Uint8Array`, `ArrayBuffer`, `Blob`, `File`) instead of
|
|
50
|
+
path-, stream-, and `Buffer`-oriented, so its JavaScript API is separate.
|
|
51
|
+
|
|
52
|
+
## Links
|
|
53
|
+
|
|
54
|
+
- [Documentation](https://muhammarajs.readthedocs.io/)
|
|
55
|
+
- [Recipe API invariants](https://muhammarajs.readthedocs.io/en/latest/recipe/invariants.html) —
|
|
56
|
+
behavior not obvious from the method signatures, such as the direct-write
|
|
57
|
+
behavior of file-backed `endPDF()`
|
|
58
|
+
- [Changelog](https://github.com/julianhille/MuhammaraJS/blob/develop/CHANGELOG.md)
|
|
59
|
+
- [Issues](https://github.com/julianhille/MuhammaraJS/issues)
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -197,3 +197,42 @@ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
197
197
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
198
198
|
specific language governing permissions and limitations under the License.
|
|
199
199
|
```
|
|
200
|
+
|
|
201
|
+
## Adobe Glyph List
|
|
202
|
+
|
|
203
|
+
`lib/glyph-list.js` holds a table derived from the Adobe Glyph List 2.0
|
|
204
|
+
(`glyphlist.txt`, September 20, 2002), which maps glyph names to Unicode. The
|
|
205
|
+
table re-encodes the list and normalizes its values to NFC; it is not a copy or
|
|
206
|
+
version of the Adobe document.
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
Copyright (c) 1997,1998,2002,2007 Adobe Systems Incorporated
|
|
210
|
+
|
|
211
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
212
|
+
copy of this documentation file to use, copy, publish, distribute,
|
|
213
|
+
sublicense, and/or sell copies of the documentation, and to permit
|
|
214
|
+
others to do the same, provided that:
|
|
215
|
+
- No modification, editing or other alteration of this document is
|
|
216
|
+
allowed; and
|
|
217
|
+
- The above copyright notice and this permission notice shall be
|
|
218
|
+
included in all copies of the documentation.
|
|
219
|
+
|
|
220
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
221
|
+
copy of this documentation file, to create their own derivative works
|
|
222
|
+
from the content of this document to use, copy, publish, distribute,
|
|
223
|
+
sublicense, and/or sell the derivative works, and to permit others to do
|
|
224
|
+
the same, provided that the derived work is not represented as being a
|
|
225
|
+
copy or version of this document.
|
|
226
|
+
|
|
227
|
+
Adobe shall not be liable to any party for any loss of revenue or profit
|
|
228
|
+
or for indirect, incidental, special, consequential, or other similar
|
|
229
|
+
damages, whether based on tort (including without limitation negligence
|
|
230
|
+
or strict liability), contract or other legal or equitable grounds even
|
|
231
|
+
if Adobe has been advised or had reason to know of the possibility of
|
|
232
|
+
such damages. The Adobe materials are provided on an "AS IS" basis.
|
|
233
|
+
Adobe specifically disclaims all express, statutory, or implied
|
|
234
|
+
warranties relating to the Adobe materials, including but not limited to
|
|
235
|
+
those concerning merchantability or fitness for a particular purpose or
|
|
236
|
+
non-infringement of any third party rights regarding the Adobe
|
|
237
|
+
materials.
|
|
238
|
+
```
|
package/index.js
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var path = require("path");
|
|
4
|
+
var fontText = require("./lib/font-text");
|
|
5
|
+
|
|
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();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Make `extractPageText()` add decoded Unicode `text` to each element. The
|
|
12
|
+
* addon exports its reader class for this step only; the export is removed
|
|
13
|
+
* again so `PDFReader` stays out of the public API.
|
|
14
|
+
*
|
|
15
|
+
* @param {object} muhammara The native addon.
|
|
16
|
+
* @returns {void}
|
|
17
|
+
* @throws {Error} If the addon does not export its reader class, so decoded
|
|
18
|
+
* text would silently be missing.
|
|
19
|
+
*/
|
|
20
|
+
function decodeExtractedText(muhammara) {
|
|
21
|
+
if (decodingAddons.has(muhammara)) return;
|
|
22
|
+
var PDFReader = muhammara.PDFReader;
|
|
23
|
+
if (typeof PDFReader !== "function") {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"The muhammara native addon does not export PDFReader; rebuild it from this version's sources",
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
delete muhammara.PDFReader;
|
|
29
|
+
decodingAddons.add(muhammara);
|
|
30
|
+
|
|
31
|
+
var extractPageText = PDFReader.prototype.extractPageText;
|
|
32
|
+
/**
|
|
33
|
+
* Extract a page's text operations with their decoded Unicode `text`.
|
|
34
|
+
*
|
|
35
|
+
* @param {number} pageIndex Zero-based page index.
|
|
36
|
+
* @param {object} [limits] Extraction limits.
|
|
37
|
+
* @param {{decodeText?: boolean}} [options] Extraction options;
|
|
38
|
+
* `decodeText: false` skips decoding and leaves out `text`.
|
|
39
|
+
* @returns {Array<object>} Text elements.
|
|
40
|
+
* @throws {TypeError} If `options` is not an object or `decodeText` is not a
|
|
41
|
+
* boolean.
|
|
42
|
+
*/
|
|
43
|
+
PDFReader.prototype.extractPageText = function (pageIndex, limits, options) {
|
|
44
|
+
var decode = fontText.decodeTextOption(options);
|
|
45
|
+
// Forward only the arguments the addon accepts, exactly as given.
|
|
46
|
+
var elements = extractPageText.apply(
|
|
47
|
+
this,
|
|
48
|
+
Array.prototype.slice.call(arguments, 0, 2),
|
|
49
|
+
);
|
|
50
|
+
return decode
|
|
51
|
+
? fontText.decodeTextElements(
|
|
52
|
+
this,
|
|
53
|
+
muhammara,
|
|
54
|
+
pageIndex,
|
|
55
|
+
elements,
|
|
56
|
+
limits,
|
|
57
|
+
)
|
|
58
|
+
: elements;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
4
61
|
|
|
5
62
|
/**
|
|
6
63
|
* Attach the shared JavaScript API to an implementation package's loaded addon.
|
|
@@ -12,10 +69,22 @@ exports.createMuhammara = function createMuhammara(muhammara) {
|
|
|
12
69
|
var bindingModule = require.resolve("./lib/muhammara");
|
|
13
70
|
var recipeDirectory = path.join(__dirname, "lib", "recipe") + path.sep;
|
|
14
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Returns the writer's event emitter, created on first use.
|
|
74
|
+
* @returns {import("events").EventEmitter} The emitter for writer events.
|
|
75
|
+
*/
|
|
15
76
|
muhammara.PDFWriter.prototype.getEvents = function () {
|
|
16
77
|
if (!this.events) this.events = new (require("events").EventEmitter)();
|
|
17
78
|
return this.events;
|
|
18
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Emits an event on the writer's emitter after setting `eventParams.writer`
|
|
82
|
+
* to this writer.
|
|
83
|
+
* @param {string|symbol} eventName - The event name.
|
|
84
|
+
* @param {Object} eventParams - The event parameters; gains a `writer` key.
|
|
85
|
+
* @returns {void}
|
|
86
|
+
* @throws {TypeError} If eventParams is not an object.
|
|
87
|
+
*/
|
|
19
88
|
muhammara.PDFWriter.prototype.triggerDocumentExtensionEvent = function (
|
|
20
89
|
eventName,
|
|
21
90
|
eventParams,
|
|
@@ -23,13 +92,27 @@ exports.createMuhammara = function createMuhammara(muhammara) {
|
|
|
23
92
|
eventParams.writer = this;
|
|
24
93
|
this.getEvents().emit(eventName, eventParams);
|
|
25
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Replaces direct references to an object in a page dictionary. Available
|
|
97
|
+
* only when modifying an existing PDF.
|
|
98
|
+
* @param {number} pageIndex - The zero-based page index; ignored for the
|
|
99
|
+
* global scope.
|
|
100
|
+
* @param {number} sourceObjectId - The object ID to stop referencing.
|
|
101
|
+
* @param {number} replacementObjectId - The object ID to reference instead.
|
|
102
|
+
* @param {Object} [options] - The options.
|
|
103
|
+
* @param {string} [options.scope] - ObjectReplacementScope.GLOBAL to
|
|
104
|
+
* replace on every page.
|
|
105
|
+
* @returns {Object} This writer.
|
|
106
|
+
* @throws {Error} If the writer does not modify a PDF or the page does not
|
|
107
|
+
* exist.
|
|
108
|
+
*/
|
|
26
109
|
muhammara.PDFWriter.prototype.replaceObject = function (
|
|
27
110
|
pageIndex,
|
|
28
111
|
sourceObjectId,
|
|
29
112
|
replacementObjectId,
|
|
30
113
|
options,
|
|
31
114
|
) {
|
|
32
|
-
if (options && options.scope ===
|
|
115
|
+
if (options && options.scope === muhammara.ObjectReplacementScope.GLOBAL) {
|
|
33
116
|
var copyingContext = this.createPDFCopyingContextForModifiedFile();
|
|
34
117
|
var pageCount = copyingContext.getSourceDocumentParser().getPagesCount();
|
|
35
118
|
|
|
@@ -68,11 +151,47 @@ exports.createMuhammara = function createMuhammara(muhammara) {
|
|
|
68
151
|
copyingContext.end();
|
|
69
152
|
return this;
|
|
70
153
|
};
|
|
154
|
+
decodeExtractedText(muhammara);
|
|
71
155
|
muhammara.PDFStreamForResponse = require("./lib/PDFStreamForResponse");
|
|
72
156
|
muhammara.PDFWStreamForFile = require("./lib/PDFWStreamForFile");
|
|
73
157
|
muhammara.PDFRStreamForFile = require("./lib/PDFRStreamForFile");
|
|
74
158
|
muhammara.PDFRStreamForBuffer = require("./lib/PDFRStreamForBuffer");
|
|
75
159
|
muhammara.PDFWStreamForBuffer = require("./lib/PDFWStreamForBuffer");
|
|
160
|
+
muhammara.DrawingPathType = Object.freeze({
|
|
161
|
+
STROKE: "stroke",
|
|
162
|
+
FILL: "fill",
|
|
163
|
+
CLIP: "clip",
|
|
164
|
+
});
|
|
165
|
+
muhammara.ImageFit = Object.freeze({
|
|
166
|
+
ALWAYS: "always",
|
|
167
|
+
OVERFLOW: "overflow",
|
|
168
|
+
});
|
|
169
|
+
muhammara.DeviceColorSpace = Object.freeze({
|
|
170
|
+
RGB: "rgb",
|
|
171
|
+
GRAY: "gray",
|
|
172
|
+
CMYK: "cmyk",
|
|
173
|
+
});
|
|
174
|
+
muhammara.PageBox = Object.freeze({
|
|
175
|
+
MEDIA: "media",
|
|
176
|
+
CROP: "crop",
|
|
177
|
+
BLEED: "bleed",
|
|
178
|
+
TRIM: "trim",
|
|
179
|
+
ART: "art",
|
|
180
|
+
});
|
|
181
|
+
muhammara.PDFImageType = Object.freeze({
|
|
182
|
+
PDF: "PDF",
|
|
183
|
+
JPG: "JPG",
|
|
184
|
+
TIFF: "TIFF",
|
|
185
|
+
PNG: "PNG",
|
|
186
|
+
});
|
|
187
|
+
muhammara.EEncoding = Object.freeze({
|
|
188
|
+
TEXT: "text",
|
|
189
|
+
CODE: "code",
|
|
190
|
+
HEX: "hex",
|
|
191
|
+
});
|
|
192
|
+
muhammara.ObjectReplacementScope = Object.freeze({
|
|
193
|
+
GLOBAL: "global",
|
|
194
|
+
});
|
|
76
195
|
muhammara.LineCapStyle = Object.freeze({
|
|
77
196
|
LINECAP_BUTT: 0,
|
|
78
197
|
LINECAP_ROUND: 1,
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
@author Luciano Júnior
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Creates a read stream over bytes in memory.
|
|
9
|
+
* @constructor
|
|
10
|
+
* @param {Buffer|Uint8Array} buffer - The bytes to read; not copied.
|
|
11
|
+
*/
|
|
7
12
|
function PDFRStreamForBuffer(buffer) {
|
|
8
13
|
this.buffer = buffer;
|
|
9
14
|
this.rposition = 0;
|
|
@@ -11,35 +16,75 @@ function PDFRStreamForBuffer(buffer) {
|
|
|
11
16
|
this.mStartPosition = 0;
|
|
12
17
|
}
|
|
13
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Reads the next bytes and advances the position by the amount read.
|
|
21
|
+
* @param {number} inAmount - The maximum number of bytes to read.
|
|
22
|
+
* @returns {Buffer} The bytes read; shorter than requested at the end.
|
|
23
|
+
*/
|
|
14
24
|
PDFRStreamForBuffer.prototype.read = function (inAmount) {
|
|
15
|
-
|
|
16
|
-
var
|
|
17
|
-
this.buffer.subarray(this.rposition, this.rposition +
|
|
25
|
+
// Copy, so callers cannot change the source through the returned chunk.
|
|
26
|
+
var bytes = Buffer.from(
|
|
27
|
+
this.buffer.subarray(this.rposition, this.rposition + inAmount),
|
|
18
28
|
);
|
|
19
|
-
this.rposition +=
|
|
20
|
-
return
|
|
29
|
+
this.rposition += inAmount;
|
|
30
|
+
return bytes;
|
|
21
31
|
};
|
|
22
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Tells whether bytes remain after the current position.
|
|
35
|
+
* @returns {boolean} True while the end has not been reached.
|
|
36
|
+
*/
|
|
23
37
|
PDFRStreamForBuffer.prototype.notEnded = function () {
|
|
24
38
|
return this.rposition < this.fileSize;
|
|
25
39
|
};
|
|
26
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Moves to a position relative to the start position, clamped to the data.
|
|
43
|
+
* @param {number} inPosition - The byte offset from the start position.
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
27
46
|
PDFRStreamForBuffer.prototype.setPosition = function (inPosition) {
|
|
28
|
-
this.rposition =
|
|
47
|
+
this.rposition = Math.min(
|
|
48
|
+
Math.max(this.mStartPosition + inPosition, 0),
|
|
49
|
+
this.fileSize,
|
|
50
|
+
);
|
|
29
51
|
};
|
|
30
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Moves to a position counted back from the end, clamped to the data.
|
|
55
|
+
* @param {number} inPosition - The number of bytes before the end.
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
31
58
|
PDFRStreamForBuffer.prototype.setPositionFromEnd = function (inPosition) {
|
|
32
|
-
this.rposition =
|
|
59
|
+
this.rposition = Math.min(
|
|
60
|
+
Math.max(this.fileSize - inPosition, 0),
|
|
61
|
+
this.fileSize,
|
|
62
|
+
);
|
|
33
63
|
};
|
|
34
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Advances the position without reading.
|
|
67
|
+
* @param {number} inAmount - The number of bytes to skip.
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
35
70
|
PDFRStreamForBuffer.prototype.skip = function (inAmount) {
|
|
36
71
|
this.rposition += inAmount;
|
|
37
72
|
};
|
|
38
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Returns the position relative to the start position.
|
|
76
|
+
* @returns {number} The current byte offset.
|
|
77
|
+
*/
|
|
39
78
|
PDFRStreamForBuffer.prototype.getCurrentPosition = function () {
|
|
40
79
|
return this.rposition - this.mStartPosition;
|
|
41
80
|
};
|
|
42
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Sets the offset that later positions are counted from, for PDF data that
|
|
84
|
+
* does not begin at byte zero.
|
|
85
|
+
* @param {number} inPosition - The absolute byte offset of the start.
|
|
86
|
+
* @returns {void}
|
|
87
|
+
*/
|
|
43
88
|
PDFRStreamForBuffer.prototype.moveStartPosition = function (inPosition) {
|
|
44
89
|
this.mStartPosition = inPosition;
|
|
45
90
|
};
|
package/lib/PDFRStreamForFile.js
CHANGED
|
@@ -3,6 +3,12 @@ var fs = require("fs");
|
|
|
3
3
|
PDFRStreamForFile is an implementation of a read stream using the supplied file path.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Opens a file for reading.
|
|
8
|
+
* @constructor
|
|
9
|
+
* @param {string} inPath - The file path.
|
|
10
|
+
* @throws {Error} If the file cannot be opened or read.
|
|
11
|
+
*/
|
|
6
12
|
function PDFRStreamForFile(inPath) {
|
|
7
13
|
this.rs = fs.openSync(inPath, "r");
|
|
8
14
|
this.path = inPath;
|
|
@@ -11,42 +17,85 @@ function PDFRStreamForFile(inPath) {
|
|
|
11
17
|
this.mStartPosition = 0;
|
|
12
18
|
}
|
|
13
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Reads the next bytes and advances the position by the amount read.
|
|
22
|
+
* @param {number} inAmount - The maximum number of bytes to read.
|
|
23
|
+
* @returns {Buffer} The bytes read; shorter than requested at the end.
|
|
24
|
+
*/
|
|
14
25
|
PDFRStreamForFile.prototype.read = function (inAmount) {
|
|
15
|
-
var buffer = Buffer.alloc(inAmount
|
|
26
|
+
var buffer = Buffer.alloc(inAmount);
|
|
16
27
|
var bytesRead = fs.readSync(this.rs, buffer, 0, inAmount, this.rposition);
|
|
17
|
-
var arr = [];
|
|
18
|
-
|
|
19
|
-
for (var i = 0; i < bytesRead; ++i) arr.push(buffer[i]);
|
|
20
28
|
this.rposition += bytesRead;
|
|
21
|
-
return
|
|
29
|
+
return buffer.subarray(0, bytesRead);
|
|
22
30
|
};
|
|
23
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Tells whether bytes remain after the current position.
|
|
34
|
+
* @returns {boolean} True while the end has not been reached.
|
|
35
|
+
*/
|
|
24
36
|
PDFRStreamForFile.prototype.notEnded = function () {
|
|
25
37
|
return this.rposition < this.fileSize;
|
|
26
38
|
};
|
|
27
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Moves to a position relative to the start position, clamped to the data.
|
|
42
|
+
* @param {number} inPosition - The byte offset from the start position.
|
|
43
|
+
* @returns {void}
|
|
44
|
+
*/
|
|
28
45
|
PDFRStreamForFile.prototype.setPosition = function (inPosition) {
|
|
29
|
-
this.rposition =
|
|
46
|
+
this.rposition = Math.min(
|
|
47
|
+
Math.max(this.mStartPosition + inPosition, 0),
|
|
48
|
+
this.fileSize,
|
|
49
|
+
);
|
|
30
50
|
};
|
|
31
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Moves to a position counted back from the end, clamped to the data.
|
|
54
|
+
* @param {number} inPosition - The number of bytes before the end.
|
|
55
|
+
* @returns {void}
|
|
56
|
+
*/
|
|
32
57
|
PDFRStreamForFile.prototype.setPositionFromEnd = function (inPosition) {
|
|
33
|
-
this.rposition =
|
|
58
|
+
this.rposition = Math.min(
|
|
59
|
+
Math.max(this.fileSize - inPosition, 0),
|
|
60
|
+
this.fileSize,
|
|
61
|
+
);
|
|
34
62
|
};
|
|
35
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Advances the position without reading.
|
|
66
|
+
* @param {number} inAmount - The number of bytes to skip.
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
36
69
|
PDFRStreamForFile.prototype.skip = function (inAmount) {
|
|
37
70
|
this.rposition += inAmount;
|
|
38
71
|
};
|
|
39
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Returns the position relative to the start position.
|
|
75
|
+
* @returns {number} The current byte offset.
|
|
76
|
+
*/
|
|
40
77
|
PDFRStreamForFile.prototype.getCurrentPosition = function () {
|
|
41
78
|
return this.rposition - this.mStartPosition;
|
|
42
79
|
};
|
|
43
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Sets the offset that later positions are counted from, for PDF data that
|
|
83
|
+
* does not begin at byte zero.
|
|
84
|
+
* @param {number} inPosition - The absolute byte offset of the start.
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
44
87
|
PDFRStreamForFile.prototype.moveStartPosition = function (inPosition) {
|
|
45
88
|
this.mStartPosition = inPosition;
|
|
46
89
|
};
|
|
47
90
|
|
|
48
91
|
function noop() {}
|
|
49
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Closes the file.
|
|
95
|
+
* @param {Function} [inCallback] - Called once the file is closed, with the
|
|
96
|
+
* close error if any.
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
50
99
|
PDFRStreamForFile.prototype.close = function (inCallback) {
|
|
51
100
|
fs.close(this.rs, inCallback ? inCallback : noop);
|
|
52
101
|
};
|
|
@@ -2,19 +2,36 @@
|
|
|
2
2
|
PDFStreamForResponse is an implementation of a write stream that writes directly to an HTTP response.
|
|
3
3
|
Using this stream frees the user from having to create a PDF file on disk when generating on-demand PDFs
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* Creates a write stream that writes to an HTTP response.
|
|
7
|
+
* @constructor
|
|
8
|
+
* @param {{write: function(Buffer): *}} inResponse - The response, or any
|
|
9
|
+
* object with a write method.
|
|
10
|
+
*/
|
|
5
11
|
function PDFStreamForResponse(inResponse) {
|
|
6
12
|
this.response = inResponse;
|
|
7
13
|
this.position = 0;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Writes bytes to the response.
|
|
18
|
+
* @param {Buffer|number[]} inBytes - The bytes to write.
|
|
19
|
+
* @returns {number} The number of bytes written.
|
|
20
|
+
*/
|
|
21
|
+
PDFStreamForResponse.prototype.write = function (inBytes) {
|
|
22
|
+
// Arrays of byte values are still accepted from direct callers.
|
|
23
|
+
if (!Buffer.isBuffer(inBytes)) inBytes = Buffer.from(inBytes);
|
|
24
|
+
if (inBytes.length > 0) {
|
|
25
|
+
this.response.write(inBytes);
|
|
26
|
+
this.position += inBytes.length;
|
|
27
|
+
return inBytes.length;
|
|
15
28
|
} else return 0;
|
|
16
29
|
};
|
|
17
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Returns the number of bytes written so far.
|
|
33
|
+
* @returns {number} The current byte offset.
|
|
34
|
+
*/
|
|
18
35
|
PDFStreamForResponse.prototype.getCurrentPosition = function () {
|
|
19
36
|
return this.position;
|
|
20
37
|
};
|
|
@@ -1,23 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
PDFWStreamForBuffer is an implementation of a write stream that collects
|
|
3
|
+
the written bytes in memory. Read the result from its `buffer` property.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Creates a write stream that collects the bytes in memory.
|
|
7
|
+
* @constructor
|
|
8
|
+
*/
|
|
1
9
|
function PDFWStreamForBuffer() {
|
|
2
|
-
this.
|
|
10
|
+
this.chunks = [];
|
|
11
|
+
this.joined = null;
|
|
3
12
|
this.position = 0;
|
|
4
13
|
}
|
|
5
14
|
|
|
6
|
-
PDFWStreamForBuffer.prototype
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
Object.defineProperty(PDFWStreamForBuffer.prototype, "buffer", {
|
|
16
|
+
/**
|
|
17
|
+
* The bytes written so far, or null before the first write. Chunks are
|
|
18
|
+
* joined on first access instead of on every write.
|
|
19
|
+
* @returns {Buffer|null} The written bytes.
|
|
20
|
+
*/
|
|
21
|
+
get: function () {
|
|
22
|
+
if (this.chunks.length === 0) return this.joined;
|
|
23
|
+
this.joined = this.joined
|
|
24
|
+
? Buffer.concat([this.joined].concat(this.chunks))
|
|
25
|
+
: Buffer.concat(this.chunks);
|
|
26
|
+
this.chunks = [];
|
|
27
|
+
return this.joined;
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Replaces the collected bytes.
|
|
31
|
+
* @param {Buffer|null} value - The new contents.
|
|
32
|
+
*/
|
|
33
|
+
set: function (value) {
|
|
34
|
+
this.chunks = [];
|
|
35
|
+
this.joined = value;
|
|
36
|
+
},
|
|
37
|
+
});
|
|
13
38
|
|
|
14
|
-
|
|
15
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Collects a copy of the bytes.
|
|
41
|
+
* @param {Buffer|Uint8Array|number[]} inBytes - The bytes to write.
|
|
42
|
+
* @returns {number} The number of bytes written.
|
|
43
|
+
*/
|
|
44
|
+
PDFWStreamForBuffer.prototype.write = function (inBytes) {
|
|
45
|
+
if (inBytes.length > 0) {
|
|
46
|
+
// Copy, so a caller reusing its buffer cannot change collected output,
|
|
47
|
+
// and so arrays of byte values keep working.
|
|
48
|
+
this.chunks.push(Buffer.from(inBytes));
|
|
49
|
+
this.position += inBytes.length;
|
|
50
|
+
return inBytes.length;
|
|
16
51
|
}
|
|
17
52
|
|
|
18
53
|
return 0;
|
|
19
54
|
};
|
|
20
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Returns the number of bytes written so far.
|
|
58
|
+
* @returns {number} The current byte offset.
|
|
59
|
+
*/
|
|
21
60
|
PDFWStreamForBuffer.prototype.getCurrentPosition = function () {
|
|
22
61
|
return this.position;
|
|
23
62
|
};
|
package/lib/PDFWStreamForFile.js
CHANGED
|
@@ -4,24 +4,45 @@ var fs = require("fs");
|
|
|
4
4
|
PDFWStreamForFile is an implementation of a write stream using the supplied file path.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Creates a write stream to a file, replacing an existing one.
|
|
9
|
+
* @constructor
|
|
10
|
+
* @param {string} inPath - The file path.
|
|
11
|
+
*/
|
|
7
12
|
function PDFWStreamForFile(inPath) {
|
|
8
13
|
this.ws = fs.createWriteStream(inPath);
|
|
9
14
|
this.position = 0;
|
|
10
15
|
this.path = inPath;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Writes bytes to the file.
|
|
20
|
+
* @param {Buffer|number[]} inBytes - The bytes to write.
|
|
21
|
+
* @returns {number} The number of bytes written.
|
|
22
|
+
*/
|
|
23
|
+
PDFWStreamForFile.prototype.write = function (inBytes) {
|
|
24
|
+
// Arrays of byte values are still accepted from direct callers.
|
|
25
|
+
if (!Buffer.isBuffer(inBytes)) inBytes = Buffer.from(inBytes);
|
|
26
|
+
if (inBytes.length > 0) {
|
|
27
|
+
this.ws.write(inBytes);
|
|
28
|
+
this.position += inBytes.length;
|
|
29
|
+
return inBytes.length;
|
|
18
30
|
} else return 0;
|
|
19
31
|
};
|
|
20
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Returns the number of bytes written so far.
|
|
35
|
+
* @returns {number} The current byte offset.
|
|
36
|
+
*/
|
|
21
37
|
PDFWStreamForFile.prototype.getCurrentPosition = function () {
|
|
22
38
|
return this.position;
|
|
23
39
|
};
|
|
24
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Flushes and closes the file.
|
|
43
|
+
* @param {Function} [inCallback] - Called once the file is closed.
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
25
46
|
PDFWStreamForFile.prototype.close = function (inCallback) {
|
|
26
47
|
if (this.ws) {
|
|
27
48
|
var self = this;
|