muhammara 3.8.0 → 4.1.0

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +47 -1
  2. package/README.md +260 -3
  3. package/lib/PDFRStreamForBuffer.js +4 -5
  4. package/lib/Recipe.js +1 -1
  5. package/lib/recipe/annotation.js +47 -13
  6. package/lib/recipe/font.js +2 -0
  7. package/lib/recipe/htmlToTextObjects.js +1 -0
  8. package/lib/recipe/overlay.js +1 -0
  9. package/lib/recipe/page.js +3 -0
  10. package/lib/recipe/text.js +41 -6
  11. package/lib/recipe/vector.helper.js +1 -0
  12. package/muhammara.d.ts +69 -9
  13. package/node_modules/@mapbox/node-pre-gyp/.github/workflows/codeql.yml +74 -0
  14. package/node_modules/@mapbox/node-pre-gyp/CHANGELOG.md +3 -0
  15. package/node_modules/@mapbox/node-pre-gyp/package.json +10 -10
  16. package/node_modules/balanced-match/package.json +2 -1
  17. package/node_modules/brace-expansion/package.json +1 -1
  18. package/node_modules/debug/package.json +2 -1
  19. package/node_modules/detect-libc/README.md +4 -1
  20. package/node_modules/detect-libc/index.d.ts +3 -0
  21. package/node_modules/detect-libc/lib/detect-libc.js +105 -4
  22. package/node_modules/detect-libc/lib/filesystem.js +41 -0
  23. package/node_modules/detect-libc/lib/process.js +3 -0
  24. package/node_modules/detect-libc/package.json +12 -5
  25. package/node_modules/inherits/package.json +1 -6
  26. package/node_modules/make-dir/node_modules/semver/package.json +21 -12
  27. package/node_modules/make-dir/node_modules/semver/semver.js +71 -24
  28. package/node_modules/minipass/README.md +7 -7
  29. package/node_modules/minipass/index.d.ts +18 -16
  30. package/node_modules/minipass/index.js +6 -1
  31. package/node_modules/minipass/index.mjs +7 -2
  32. package/node_modules/minipass/package.json +10 -10
  33. package/node_modules/node-fetch/README.md +2 -1
  34. package/node_modules/node-fetch/lib/index.es.js +5 -6
  35. package/node_modules/node-fetch/lib/index.js +5 -5
  36. package/node_modules/node-fetch/lib/index.mjs +5 -6
  37. package/node_modules/node-fetch/package.json +5 -5
  38. package/node_modules/readable-stream/lib/_stream_duplex.js +8 -8
  39. package/node_modules/readable-stream/lib/_stream_passthrough.js +1 -1
  40. package/node_modules/readable-stream/lib/_stream_readable.js +37 -36
  41. package/node_modules/readable-stream/lib/_stream_transform.js +6 -5
  42. package/node_modules/readable-stream/lib/_stream_writable.js +19 -18
  43. package/node_modules/readable-stream/lib/internal/streams/async_iterator.js +81 -86
  44. package/node_modules/readable-stream/lib/internal/streams/buffer_list.js +155 -127
  45. package/node_modules/readable-stream/lib/internal/streams/destroy.js +17 -16
  46. package/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +11 -11
  47. package/node_modules/readable-stream/lib/internal/streams/from.js +6 -6
  48. package/node_modules/readable-stream/lib/internal/streams/pipeline.js +14 -14
  49. package/node_modules/readable-stream/lib/internal/streams/state.js +4 -4
  50. package/node_modules/readable-stream/package.json +5 -5
  51. package/node_modules/semver/README.md +70 -1
  52. package/node_modules/semver/bin/semver.js +16 -2
  53. package/node_modules/semver/classes/comparator.js +39 -34
  54. package/node_modules/semver/classes/range.js +45 -28
  55. package/node_modules/semver/classes/semver.js +32 -17
  56. package/node_modules/semver/functions/coerce.js +1 -1
  57. package/node_modules/semver/functions/diff.js +58 -16
  58. package/node_modules/semver/functions/inc.js +3 -2
  59. package/node_modules/semver/functions/parse.js +5 -22
  60. package/node_modules/semver/index.js +1 -0
  61. package/node_modules/semver/internal/constants.js +20 -2
  62. package/node_modules/semver/internal/parse-options.js +14 -10
  63. package/node_modules/semver/internal/re.js +34 -4
  64. package/node_modules/semver/package.json +12 -11
  65. package/node_modules/semver/ranges/intersects.js +1 -1
  66. package/node_modules/semver/ranges/subset.js +6 -3
  67. package/node_modules/tar/lib/normalize-unicode.js +1 -1
  68. package/node_modules/tar/lib/pack.js +20 -8
  69. package/node_modules/tar/lib/parse.js +46 -3
  70. package/node_modules/tar/lib/path-reservations.js +1 -1
  71. package/node_modules/tar/lib/read-entry.js +2 -2
  72. package/node_modules/tar/lib/replace.js +1 -1
  73. package/node_modules/tar/lib/unpack.js +1 -1
  74. package/node_modules/tar/lib/update.js +1 -1
  75. package/node_modules/tar/lib/write-entry.js +3 -3
  76. package/node_modules/tar/package.json +11 -16
  77. package/node_modules/util-deprecate/package.json +1 -5
  78. package/package.json +4 -6
  79. package/node_modules/make-dir/node_modules/semver/CHANGELOG.md +0 -70
package/CHANGELOG.md CHANGED
@@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.1.0] - 2023-12-13
11
+
12
+ ### Fixed
13
+
14
+ - definitions: appendPage optionnal parameter
15
+ - Build musl/musl-arm with node 20
16
+ - Support negative rotation in recipe page changes
17
+ - registerFont now returns recipe as stated in typescript definition
18
+
19
+ ### Added
20
+
21
+ - Add nodejs v21.0.0
22
+
23
+ ## [4.0.0] - 2023-07-14
24
+
25
+ ### Fixed
26
+
27
+ - Underline in text object
28
+ - Recipe type / arg documentation
29
+ - Add missing type `userPassword` to `EncryptOptions`
30
+
31
+ ### Added
32
+
33
+ - Strikethrough implementation in text object
34
+ - Add electron 23.2., 23.3
35
+ - Recipe infos to readme
36
+ - Electron v24.1, 24.2, 24.3, 24.4, 24.5, 24.6
37
+ - Add node 20.x
38
+ - Add electron v25.0, 25.1, 25.2, 25.3
39
+ - Option to add annotation replies to annotations
40
+ - Textboxes now show title and date as annotations in pdfs
41
+
42
+ ### Removed
43
+
44
+ - Dependency to static-eval and static-module as they are not used directly
45
+ - Node versions: 11.x - 14.x
46
+ - Electron versions: 11.x - 14.2
47
+
48
+ ### Changed
49
+
50
+ - Updated node-gyp version to 1.0.10
51
+ - Older node ubuntu 18.04 builds are now building on docker, as github actions removed 18.04
52
+ - CI linux builds use ubuntu 20.04 instead of 18.04 -> glibc Update, see readme for breaking changes in v4
53
+
10
54
  ## [3.8.0] - 2023-03-01
11
55
 
12
56
  ### Added
@@ -368,7 +412,9 @@ with the following changes.
368
412
 
369
413
  - Initial release
370
414
 
371
- [unreleased]: https://github.com/julianhille/MuhammaraJS/compare/3.8.0...HEAD
415
+ [unreleased]: https://github.com/julianhille/MuhammaraJS/compare/4.1.0...HEAD
416
+ [4.1.0]: https://github.com/julianhille/MuhammaraJS/compare/4.0.0...4.1.0
417
+ [4.0.0]: https://github.com/julianhille/MuhammaraJS/compare/3.8.0...4.0.0
372
418
  [3.8.0]: https://github.com/julianhille/MuhammaraJS/compare/3.7.0...3.8.0
373
419
  [3.7.0]: https://github.com/julianhille/MuhammaraJS/compare/3.6.0...3.7.0
374
420
  [3.6.0]: https://github.com/julianhille/MuhammaraJS/compare/3.5.0...3.6.0
package/README.md CHANGED
@@ -28,15 +28,26 @@ It serves as a drop in replacement.
28
28
 
29
29
  # Caution
30
30
 
31
- Version 2.0 will be incompatible with some older node and
31
+ ## Breaking changes
32
+
33
+ ### Version 2.x
34
+
35
+ will be incompatible with some older node and
32
36
  electron versions because we needed to upgrade node-pre-gyp.
33
37
 
34
- Version 3.x has breaking changes:
38
+ ### Version 3.x
35
39
 
36
40
  - Node < 11 and Electron < 11 removed the prebuilts
37
41
  - Renamed typo exported value from eTokenSeprator to eTokenSeparator
38
42
 
39
- This wont affect alot of you but still.
43
+ This won't affect a lot of you but still.
44
+
45
+ ### Version 4.x
46
+
47
+ - Node < 15 and electron < 15 pre-builts have been removed
48
+ - Ubuntu 18.04 has been removed from github actions and so it is unable to build on 18.04.
49
+ This means the glibc has been raised to 2.31 which might break pre-builts for you.
50
+ It is still possible to build for older glibc version.
40
51
 
41
52
  # Installation
42
53
 
@@ -82,3 +93,249 @@ You can find samples and documentation [here](./docs/Home.md)
82
93
  To generate the documentation you could clone this repo and execute:
83
94
 
84
95
  `npm run recipe-jsdoc`
96
+
97
+ ## Recipe
98
+
99
+ ### Instructions
100
+
101
+ - [GetStarted](#getstarted)
102
+ - [Coordinate System](#coordinate-system)
103
+ - [Create a new PDF](#create-a-new-pdf)
104
+ - [Modify an existing PDF](#modify-an-existing-pdf)
105
+ - [PDF Pages/Info/Structure](#page-info)
106
+ - [Append PDF](#append-pdf)
107
+ - [Insert PDF](#insert-pdf)
108
+ - [Overlay PDF](#overlay-pdf)
109
+ - [Split PDF](#split-pdf)
110
+ - [Encryption](#encryption)
111
+
112
+ ### GetStarted
113
+
114
+ ```bash
115
+ const Recipe = require('muhammara').Recipe
116
+ ```
117
+
118
+ ### Coordinate System
119
+
120
+ In order to make things easier, I use `Left-Top` as center `[0,0]` instead of `Left-Bottom`.
121
+ You may write and edit the pdf like you write things on papers from the left top corner.
122
+ It is similar to the [Html Canvas](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)
123
+
124
+ ```javascript
125
+ pdfDoc
126
+ .text('start from here', 0, 0)
127
+ .text('next line', 0, 20)
128
+ .text('some other texts', 100, 100)
129
+ ...
130
+ ```
131
+
132
+ ### Create a new PDF
133
+
134
+ ```javascript
135
+ const Recipe = require("muhammara").Recipe;
136
+
137
+ const pdfDoc = new Recipe("new", "output.pdf", {
138
+ version: 1.6,
139
+ author: "John Doe",
140
+ title: "Hummus Recipe",
141
+ subject: "A brand new PDF",
142
+ });
143
+
144
+ pdfDoc.createPage("letter-size").endPage().endPDF();
145
+ ```
146
+
147
+ ```javascript
148
+ const Recipe = require("muhammara").Recipe;
149
+ const pdfDoc = new Recipe("new", "output.pdf");
150
+
151
+ pdfDoc
152
+ // 1st Page
153
+ .createPage("letter-size")
154
+ .circle("center", 100, 30, { stroke: "#3b7721", fill: "#eee000" })
155
+ .polygon(
156
+ [
157
+ [50, 250],
158
+ [100, 200],
159
+ [512, 200],
160
+ [562, 250],
161
+ [512, 300],
162
+ [100, 300],
163
+ [50, 250],
164
+ ],
165
+ {
166
+ color: [153, 143, 32],
167
+ stroke: [0, 0, 140],
168
+ fill: [153, 143, 32],
169
+ lineWidth: 5,
170
+ }
171
+ )
172
+ .rectangle(240, 400, 50, 50, {
173
+ stroke: "#3b7721",
174
+ fill: "#eee000",
175
+ lineWidth: 6,
176
+ opacity: 0.3,
177
+ })
178
+ .moveTo(200, 600)
179
+ .lineTo("center", 650)
180
+ .lineTo(412, 600)
181
+ .text("Welcome to Hummus-Recipe", "center", 250, {
182
+ color: "#066099",
183
+ fontSize: 30,
184
+ bold: true,
185
+ font: "Helvatica",
186
+ align: "center center",
187
+ opacity: 0.8,
188
+ rotation: 180,
189
+ })
190
+ .text("some text box", 450, 400, {
191
+ color: "#066099",
192
+ fontSize: 20,
193
+ font: "Courier New",
194
+ strikeOut: true,
195
+ highlight: {
196
+ color: [255, 0, 0],
197
+ },
198
+ textBox: {
199
+ width: 150,
200
+ lineHeight: 16,
201
+ padding: [5, 15],
202
+ style: {
203
+ lineWidth: 1,
204
+ stroke: "#00ff00",
205
+ fill: "#ff0000",
206
+ dash: [20, 20],
207
+ opacity: 0.1,
208
+ },
209
+ },
210
+ })
211
+ .comment("Feel free to open issues to help us!", "center", 100)
212
+ .endPage()
213
+ // 2nd page
214
+ .createPage("A4", 90)
215
+ .circle(150, 150, 300)
216
+ .endPage()
217
+ // end and save
218
+ .endPDF(() => {
219
+ /* done! */
220
+ });
221
+ ```
222
+
223
+ #### Create a new PDF as a Buffer
224
+
225
+ ```javascript
226
+ const Recipe = require("muhammara").Recipe;
227
+
228
+ const pdfDoc = new Recipe(Buffer.from("new"), null, {
229
+ version: 1.6,
230
+ author: "John Doe",
231
+ title: "Hummus Recipe",
232
+ subject: "A brand new PDF",
233
+ });
234
+
235
+ const pdfBuffer = pdfDoc.createPage("letter-size").endPage().endPDF();
236
+ ```
237
+
238
+ ### Modify an existing PDF
239
+
240
+ ```javascript
241
+ const Recipe = require("muhammara").Recipe;
242
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
243
+
244
+ pdfDoc
245
+ // edit 1st page
246
+ .editPage(1)
247
+ .text("Add some texts to an existing pdf file", 150, 300)
248
+ .rectangle(20, 20, 40, 100)
249
+ .comment("Add 1st comment annotation", 200, 300)
250
+ .image("/path/to/image.jpg", 20, 100, { width: 300, keepAspectRatio: true })
251
+ .endPage()
252
+ // edit 2nd page
253
+ .editPage(2)
254
+ .comment("Add 2nd comment annotation", 200, 100)
255
+ .endPage()
256
+ // end and save
257
+ .endPDF();
258
+ ```
259
+
260
+ ### Page Info
261
+
262
+ ```javascript
263
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
264
+ console.log(pdfDoc.pageInfo(1));
265
+ ```
266
+
267
+ #### Print the pdf structure
268
+
269
+ ```javascript
270
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
271
+
272
+ recipe.structure("pdf-structure.txt").endPDF(done);
273
+ ```
274
+
275
+ ### Append PDF
276
+
277
+ ```javascript
278
+ const Recipe = require("muhammara").Recipe;
279
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
280
+ const longPDF = "/longPDF.pdf";
281
+
282
+ pdfDoc
283
+ // just page 10
284
+ .appendPage(longPDF, 10)
285
+ // page 4 and page 6
286
+ .appendPage(longPDF, [4, 6])
287
+ // page 1-3 and 6-20
288
+ .appendPage(longPDF, [
289
+ [1, 3],
290
+ [6, 20],
291
+ ])
292
+ // all pages
293
+ .appendPage(longPDF)
294
+ .endPDF();
295
+ ```
296
+
297
+ ### Insert PDF
298
+
299
+ ```javascript
300
+ const Recipe = require("muhammara").Recipe;
301
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
302
+
303
+ pdfDoc
304
+ // insert page3 from longPDF to current page 2
305
+ .insertPage(2, "/longPDF.pdf", 3)
306
+ .endPDF();
307
+ ```
308
+
309
+ ### Overlay PDF
310
+
311
+ ```javascript
312
+ const Recipe = require("muhammara").Recipe;
313
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
314
+
315
+ pdfDoc.overlay("/overlayPDF.pdf").endPDF();
316
+ ```
317
+
318
+ ### Split PDF
319
+
320
+ ```javascript
321
+ const Recipe = require("muhammara").Recipe;
322
+ const pdfDoc = new Recipe("input.pdf");
323
+ const outputDir = path.join(__dirname, "output");
324
+
325
+ pdfDoc.split(outputDir, "prefix").endPDF();
326
+ ```
327
+
328
+ ### Encryption
329
+
330
+ ```javascript
331
+ const Recipe = require("muhammara").Recipe;
332
+ const pdfDoc = new Recipe("input.pdf", "output.pdf");
333
+
334
+ pdfDoc
335
+ .encrypt({
336
+ userPassword: "123",
337
+ ownerPassword: "123",
338
+ userProtectionFlag: 4,
339
+ })
340
+ .endPDF();
341
+ ```
@@ -5,17 +5,16 @@
5
5
  */
6
6
 
7
7
  function PDFRStreamForBuffer(buffer) {
8
- this.innerArray = Array.prototype.slice.call(buffer, 0);
8
+ this.buffer = buffer;
9
9
  this.rposition = 0;
10
- this.fileSize = this.innerArray.length;
10
+ this.fileSize = this.buffer.length;
11
11
  this.mStartPosition = 0;
12
12
  }
13
13
 
14
14
  PDFRStreamForBuffer.prototype.read = function (inAmount) {
15
15
  var amountToRead = inAmount;
16
- var arr = this.innerArray.slice(
17
- this.rposition,
18
- this.rposition + amountToRead
16
+ var arr = Array.from(
17
+ this.buffer.subarray(this.rposition, this.rposition + amountToRead)
19
18
  );
20
19
  this.rposition += amountToRead;
21
20
  return arr;
package/lib/Recipe.js CHANGED
@@ -9,7 +9,7 @@ const streams = require("memory-streams");
9
9
  * @namespace
10
10
  * @constructor
11
11
  * @param {string} src - The file path or Buffer of the src file.
12
- * @param {string} output - The path of the output file.
12
+ * @param {string} [output] - The path of the output file uses src if its not a buffer.
13
13
  * @param {Object} [options] - The options for pdfDoc
14
14
  * @param {number} [options.version] - The pdf version
15
15
  * @param {string} [options.author] - The author
@@ -27,18 +27,22 @@ exports.comment = function comment(text = "", x, y, options = {}) {
27
27
  * @name annot
28
28
  * @function
29
29
  * @memberof Recipe
30
- * @todo support for rich texst RC
30
+ * @todo support for rich text RC
31
31
  * @todo support for opacity CA
32
32
  * @param {number} x - The coordinate x
33
33
  * @param {number} y - The coordinate y
34
- * @param {string} subtype - The markup annotation type 'Text'|'FreeText'|'Line'|'Square'|'Circle'|'Polygon'|'PolyLine'|'Highlight'|'Underline'|'Squiggly'|'StrikeOut'|'Stamp'|'Caret'|'Ink'|'FileAttachment'|'Sound'
34
+ * @param {string} subtype - The markup annotation type 'Text'|'Link'|'FreeText'|'Line'|'Square'|'Circle'|'Polygon'|'PolyLine'|'Highlight'|'Underline'|'Squiggly'|'StrikeOut'|'Caret'|'Stamp'|'Ink'|'Popup'|'FileAttachment'|'Sound'|'Movie'|'Screen'|'Widget'|'PrinterMark'|'TrapNet'|'Watermark'|'3D'|'Redact'|'Projection'|'RichMedia'
35
35
  * @param {Object} [options] - The options
36
36
  * @param {string} [options.title] - The title.
37
- * @param {boolean} [options.open=false] - Open the annotation by default?
37
+ * @param {boolean} [options.open=false] - Open the annotation. Annotation will be closed by default. Specific to text annotations; subtype='Text'
38
+ * @param {boolean} [options.richText] - Rich text
38
39
  * @param {'invisible'|'hidden'|'print'|'nozoom'|'norotate'|'noview'|'readonly'|'locked'|'togglenoview'} [options.flag] - The flag property
39
- * @param {'Comment'|'Key'|'Note'|'Help'|'NewParagraph'|'Paragraph'|'Insert'} [options.icon] - The icon of annotation.
40
+ * @param {'Comment'|'Key'|'Note'|'Help'|'NewParagraph'|'Paragraph'|'Insert'} [options.icon='Note'] - The icon of annotation. Specific to text annotations. Default value: 'Note'
40
41
  * @param {number} [options.width] - Width
41
42
  * @param {number} [options.height] - Height
43
+ * @param {string} [options.date] - Date of annotation
44
+ * @param {string} [options.subject] - The subject.
45
+ * @param {Array} [options.replies] - Array of annotation replies
42
46
  */
43
47
  exports.annot = function annot(
44
48
  x,
@@ -46,11 +50,12 @@ exports.annot = function annot(
46
50
  subtype,
47
51
  options = { text: "", width: 0, height: 0 }
48
52
  ) {
49
- const { text, width, height } = options;
53
+ const { text, width, height, replies } = options;
50
54
  this.annotationsToWrite.push({
51
55
  subtype,
52
56
  args: { text, x, y, width, height, options },
53
57
  pageNumber: this.pageNumber,
58
+ replies,
54
59
  });
55
60
  return this;
56
61
  };
@@ -59,8 +64,9 @@ exports.annot = function annot(
59
64
  // Link, Popup, Movie, Widget, Screen, PrinterMark, TrapNet, Watermark, 3D
60
65
  exports._attachNonMarkupAnnot = function _attachNonMarkupAnnot() {};
61
66
 
62
- exports._annot = function _annot(subtype, args = {}, pageNumber) {
63
- const { x, y, width, height, text, options } = args;
67
+ exports._annot = function _annot(subtype, args = {}, pageNumber, ref) {
68
+ const { x, y, width, height, options, reply } = args;
69
+ let { text } = args;
64
70
  this._startDictionary(pageNumber);
65
71
  const { rotate } = this.metadata[pageNumber];
66
72
  let { nx, ny } = this._calibrateCoordinateForAnnots(x, y, 0, 0, pageNumber);
@@ -92,7 +98,7 @@ exports._annot = function _annot(subtype, args = {}, pageNumber) {
92
98
  {
93
99
  title: "",
94
100
  subject: "",
95
- date: new Date(),
101
+ date: "",
96
102
  open: false,
97
103
  flag: "", // 'readonly'
98
104
  },
@@ -103,6 +109,15 @@ exports._annot = function _annot(subtype, args = {}, pageNumber) {
103
109
  const ey = nHeight ? nHeight : 0;
104
110
  const position = [nx, ny, nx + ex, ny + ey];
105
111
 
112
+ if (reply && ref) {
113
+ text = reply.text;
114
+ params.title = reply.title || params.title;
115
+ params.date = reply.date || params.date;
116
+ params.subject = reply.subject || params.subject;
117
+ params.richText = Boolean(reply.richText);
118
+ params.flag = reply.flag || params.flag;
119
+ }
120
+
106
121
  this.dictionaryContext
107
122
  .writeKey("Type")
108
123
  .writeNameValue("Annot")
@@ -117,7 +132,9 @@ exports._annot = function _annot(subtype, args = {}, pageNumber) {
117
132
  .writeKey("T")
118
133
  .writeLiteralStringValue(params.title || "")
119
134
  .writeKey("M")
120
- .writeLiteralStringValue(this.writer.createPDFDate(params.date).toString())
135
+ .writeLiteralStringValue(
136
+ this.writer.createPDFDate(new Date(params.date)).toString()
137
+ )
121
138
  .writeKey("Open")
122
139
  .writeBooleanValue(params.open)
123
140
  .writeKey("F")
@@ -127,9 +144,9 @@ exports._annot = function _annot(subtype, args = {}, pageNumber) {
127
144
  * Rich Text Strings
128
145
  * 12.7.3.4
129
146
  */
130
- if (text && options.richText) {
147
+ if (text && params.richText) {
131
148
  const richText =
132
- text.substring(0, 5) !== "<?xml" ? contentToRC(text) : options.richText;
149
+ text.substring(0, 5) !== "<?xml" ? contentToRC(text) : params.richText;
133
150
  const richTextContent = richText;
134
151
  this.dictionaryContext
135
152
  .writeKey("RC")
@@ -141,6 +158,14 @@ exports._annot = function _annot(subtype, args = {}, pageNumber) {
141
158
  .writeLiteralStringValue(textContent);
142
159
  }
143
160
 
161
+ if (reply && ref) {
162
+ this.dictionaryContext
163
+ .writeKey("IRT")
164
+ .writeObjectReferenceValue(ref)
165
+ .writeKey("RT")
166
+ .writeNameValue("R");
167
+ }
168
+
144
169
  let { border, color } = options;
145
170
 
146
171
  if (this._getTextMarkupAnnotationSubtype(subtype)) {
@@ -212,12 +237,19 @@ exports._annot = function _annot(subtype, args = {}, pageNumber) {
212
237
  if (params.icon) {
213
238
  this.dictionaryContext.writeKey("Name").writeNameValue(params.icon);
214
239
  }
215
- this._endDictionary(pageNumber);
240
+ return this._endDictionary(pageNumber);
216
241
  };
217
242
 
218
243
  exports._writeAnnotations = function _writeAnnotations() {
219
244
  this.annotationsToWrite.forEach((annot) => {
220
- this._annot(annot.subtype, annot.args, annot.pageNumber);
245
+ const ref = this._annot(annot.subtype, annot.args, annot.pageNumber);
246
+
247
+ if (annot.replies) {
248
+ annot.replies.forEach((reply) => {
249
+ annot.args.reply = reply;
250
+ this._annot(annot.subtype, annot.args, annot.pageNumber, ref);
251
+ });
252
+ }
221
253
  });
222
254
  this.annotations.forEach((pageAnnots, index) => {
223
255
  this._writeAnnotation(index);
@@ -276,6 +308,8 @@ exports._endDictionary = function _endDictionary(pageNumber) {
276
308
  const pageIndex = pageNumber - 1;
277
309
  this.annotations[pageIndex] = this.annotations[pageIndex] || [];
278
310
  this.annotations[pageIndex].push(this.dictionaryObject);
311
+
312
+ return this.dictionaryObject;
279
313
  };
280
314
 
281
315
  exports._getTextMarkupAnnotationSubtype =
@@ -79,6 +79,8 @@ exports._registerFont = function _registerFont(
79
79
  break;
80
80
  }
81
81
  this.fonts[family] = font;
82
+
83
+ return this;
82
84
  };
83
85
 
84
86
  function _getFontFile(self, options = {}) {
@@ -85,6 +85,7 @@ function parseNode(node) {
85
85
  isBold: isBoldTag(node.tagName),
86
86
  isItalic: isItalicTag(node.tagName),
87
87
  underline: node.tagName == "u",
88
+ strikeOut: node.tagName == "del",
88
89
  attributes,
89
90
  styles,
90
91
  needsLineBreaker: needsLineBreaker(node.tagName),
@@ -9,6 +9,7 @@ const { xObjectForm } = require("./xObjectForm");
9
9
  * @param {number} x - The coordinate x
10
10
  * @param {number} y - The coordinate y
11
11
  * @param {number} [options.scale] - Scale the overlay pdf, default is 1
12
+ * @param {number} [options.page] - Page of the overlay pdf, default is 1
12
13
  * @param {boolean} [options.keepAspectRatio] - To keep the aspect ratio when scaling, default is true
13
14
  * @param {boolean} [options.fitWidth] - To set the width to 100% (use with keepAspectRatio=true)
14
15
  * @param {boolean} [options.fitHeight] - To set the height to 100% (use with keepAspectRatio=true)
@@ -184,12 +184,15 @@ exports._resumePageRotation = function _resumePageRotation(
184
184
 
185
185
  switch (rotate) {
186
186
  case 90:
187
+ case -270:
187
188
  context.cm(0, 1, -1, 0, height - startX, startY);
188
189
  break;
189
190
  case 180:
191
+ case -180:
190
192
  context.cm(-1, 0, 0, -1, width, height);
191
193
  break;
192
194
  case 270:
195
+ case -90:
193
196
  context.cm(0, -1, 1, 0, startX, width - startY);
194
197
  break;
195
198
 
@@ -238,6 +238,13 @@ exports._makeTextBox = function _makeTextBox(options) {
238
238
  * @param {string|number[]} [options.textBox.style.fill] - Text Box border background color (HexColor, PercentColor or DecimalColor)
239
239
  * @param {number} [options.textBox.style.opacity=1] - Text Box border background opacity
240
240
  * @param {boolean|number|number[]} [options.textBox.style.borderRadius=0] - Border radius to apply to get rounded corners.
241
+ * @param {string} [options.title] - Title of annotation
242
+ * @param {boolean} [options.open=false] - Open the annotation. Annotation will be closed by default. Specific to text annotations; subtype='Text'
243
+ * @param {boolean} [options.richText] - Rich text in annotation
244
+ * @param {AnnotOptionsFlag} [options.flag] - The flag property of annotation
245
+ * @param {AnnotOptionsIcon} [options.icon='Note'] - The icon of annotation. Specific to text annotations. Default value: 'Note'
246
+ * @param {string} [options.date] - Date of text to show up on annotation
247
+ * @param {string} [options.subject] - Subject of annotation
241
248
  * When true is given, the default radius size for all corners is 5. A four number array may be used to give specific sizees to each
242
249
  * corner. The numbering starts from the top, left corner, and goes clockwise around the text box.
243
250
  */
@@ -359,12 +366,8 @@ exports.text = function text(text = "", x, y, options = {}) {
359
366
  const addUnderline = (x, y, ctx, options) => {
360
367
  // underline implementation
361
368
  if (options.underline) {
362
- // console.log(textWidth, lineWidth, currentLineWidth)
363
369
  const underlineY = y - options.textHeight * 0.1;
364
- // TODO: fix the line with calculation
365
- const width =
366
- currentLineWidth -
367
- (isHTML || toWriteTextObject.text.endsWith(" ") ? spaceWidth : 0);
370
+ const width = options.lineWidth;
368
371
  ctx
369
372
  .q()
370
373
  .drawPath(x, underlineY, x + width, underlineY, options)
@@ -372,6 +375,18 @@ exports.text = function text(text = "", x, y, options = {}) {
372
375
  }
373
376
  };
374
377
 
378
+ const addStrikeOut = (x, y, ctx, options) => {
379
+ // strikethrough implementation
380
+ if (options.strikeOut) {
381
+ const strikeOutY = y + options.textHeight * 0.2;
382
+ const width = options.lineWidth;
383
+ ctx
384
+ .q()
385
+ .drawPath(x, strikeOutY, x + width, strikeOutY, options)
386
+ .Q();
387
+ }
388
+ };
389
+
375
390
  const addTextTraits = (ctx, options) => {
376
391
  ctx.Tf(options.font, options.size);
377
392
  ctx.Tc(options.charSpace);
@@ -394,6 +409,7 @@ exports.text = function text(text = "", x, y, options = {}) {
394
409
  ctx.ET();
395
410
 
396
411
  addUnderline(x, y, ctx, options);
412
+ addStrikeOut(x, y, ctx, options);
397
413
  };
398
414
 
399
415
  const justifyText = (left, x, wto, textBox, ctx, options, callback) => {
@@ -416,7 +432,7 @@ exports.text = function text(text = "", x, y, options = {}) {
416
432
 
417
433
  const writeText = (context, x, y, wto) => {
418
434
  const options = wto.writeOptions;
419
- const { lineHeight, text, baseline } = wto;
435
+ const { lineWidth, lineHeight, text, baseline } = wto;
420
436
  let next_x = 0;
421
437
 
422
438
  if (text === "") {
@@ -424,6 +440,10 @@ exports.text = function text(text = "", x, y, options = {}) {
424
440
  return next_x;
425
441
  }
426
442
 
443
+ if (options.underline || options.strikeOut) {
444
+ options.lineWidth = lineWidth;
445
+ }
446
+
427
447
  // Produce a hilite under words?
428
448
  if (options.hilite) {
429
449
  const bgColor = options.hilite.color || "#ffff00";
@@ -549,13 +569,24 @@ exports.text = function text(text = "", x, y, options = {}) {
549
569
  typeof targetAnnotations[key] != "object"
550
570
  ? {}
551
571
  : targetAnnotations[key];
572
+ const { title, open, richText, flag, icon, date, subject } =
573
+ targetAnnotations;
552
574
  Object.assign(markupOption, {
553
575
  height: textHeight * 1.4,
554
576
  width: currentLineWidth,
555
577
  text: markupOption.text || "",
556
578
  _textHeight: textHeight,
579
+ // add options to annotation
580
+ title: title || "",
581
+ open: Boolean(open),
582
+ richText: Boolean(richText),
583
+ flag: flag || "",
584
+ icon: icon || "",
585
+ date: date || "",
586
+ subject: subject || "",
557
587
  });
558
588
  const { ox, oy } = this._reverseCoordinate(x, y - textHeight * 0.2);
589
+
559
590
  this.annot(ox, oy, subtype, markupOption);
560
591
  }
561
592
  }
@@ -798,6 +829,9 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
798
829
  child.underline = textObject.underline
799
830
  ? textObject.underline
800
831
  : child.underline;
832
+ child.strikeOut = textObject.strikeOut
833
+ ? textObject.strikeOut
834
+ : child.strikeOut;
801
835
 
802
836
  child.lineID = textObject.lineID;
803
837
  writeValue(child);
@@ -1075,6 +1109,7 @@ function makeTextObjects(self, textObject = {}, pathOptions, textBox = {}) {
1075
1109
  color: textObject.styles.color,
1076
1110
  opacity: parseFloat(textObject.styles.opacity || pathOptions.opacity || 1),
1077
1111
  underline: textObject.underline || pathOptions.underline,
1112
+ strikeOut: textObject.strikeOut || pathOptions.strikeOut,
1078
1113
  size: textObject.size,
1079
1114
  alignHorizontal: alignHorizontal,
1080
1115
  alignVertical: alignVertical,
@@ -16,6 +16,7 @@ exports._getPathOptions = function _getPathOptions(
16
16
  size: options.size || this.current.defaultFontSize,
17
17
  charSpace: options.charSpace || 0,
18
18
  underline: false,
19
+ strikeOut: false,
19
20
  color: this._transformColor(options.color, {
20
21
  colorspace: colorspace,
21
22
  colorName: options.colorName,