pdf-lib-extended 1.0.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.
- package/README.md +123 -0
- package/index.js +3 -0
- package/package.json +15 -0
- package/src/PDFLibExtended.js +932 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# PDF-lib-Extended
|
|
2
|
+
|
|
3
|
+
PDF-lib-Extended is a JavaScript library that extends the functionality of the popular [pdf-lib](https://github.com/Hopding/pdf-lib) library. This extension adds several useful methods for working with PDF documents, making it easier to manipulate text, images, tables, and other elements within your PDFs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
To install PDF-lib-Extended, use npm:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install pdf-lib-extended
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
Here’s a basic example of how to use PDF-lib-Extended in your project:
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import PDFExtended from 'pdf-lib-extended';
|
|
18
|
+
|
|
19
|
+
async function createPDF() {
|
|
20
|
+
const pdf = new PDFExtended();
|
|
21
|
+
await pdf.init();
|
|
22
|
+
|
|
23
|
+
// Add a new page
|
|
24
|
+
pdf.addNewPage();
|
|
25
|
+
|
|
26
|
+
// Draw some text
|
|
27
|
+
pdf.drawText('Hello, World!', {
|
|
28
|
+
align: 'center',
|
|
29
|
+
size: 24,
|
|
30
|
+
color: pdf.getColor(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Draw a circle with text inside
|
|
34
|
+
pdf.drawCircleText(100, 100, 'A', {
|
|
35
|
+
size: 12,
|
|
36
|
+
color: pdf.getColor(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Generate and download the PDF
|
|
40
|
+
const pdfUrl = await pdf.generatePDFURL();
|
|
41
|
+
window.open(pdfUrl);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
createPDF();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
PDF-lib-Extended includes several custom methods to enhance your PDF creation experience:
|
|
49
|
+
|
|
50
|
+
* **Text Handling:**
|
|
51
|
+
|
|
52
|
+
* **`drawText(text, options)`:** Draws text on the current page with alignment, color, and size options.
|
|
53
|
+
* **`drawParagraph(text, options)`:** Automatically wraps text to the next line within specified bounds.
|
|
54
|
+
|
|
55
|
+
* **Shape Drawing:**
|
|
56
|
+
|
|
57
|
+
* **`drawCircleText(x, y, text, options)`:** Draws a circle with text centered inside.
|
|
58
|
+
|
|
59
|
+
* **Tables:**
|
|
60
|
+
* **`drawTable(header, data, options)`:** Draws a table with customizable borders, alignment, and size.
|
|
61
|
+
|
|
62
|
+
* **Images:**
|
|
63
|
+
|
|
64
|
+
* **`addImage(x, y, base64Image, imageScale)`:** Adds an image to the PDF document at specified coordinates with optional scaling.
|
|
65
|
+
* **Watermarks:**
|
|
66
|
+
|
|
67
|
+
* **`drawWatermark(text, size)`:** Draws a watermark on each page of the document.
|
|
68
|
+
|
|
69
|
+
* **Document Management:**
|
|
70
|
+
|
|
71
|
+
* **`addNewPage(dimensions)`:** Adds a new page to the PDF document.
|
|
72
|
+
* **`fetchPDF(url)`:** Fetches an existing PDF from a URL and loads it into the document.
|
|
73
|
+
* **`mergePDF(urls)`:** Merges multiple PDFs from URLs into the current document.
|
|
74
|
+
|
|
75
|
+
* **HTML Parsing:**
|
|
76
|
+
|
|
77
|
+
* **`htmlParser(text, parser, options)`:** Parses HTML and draws it on the PDF.
|
|
78
|
+
|
|
79
|
+
## API Reference
|
|
80
|
+
**`init()`**
|
|
81
|
+
Initializes the PDF document and embeds fonts.
|
|
82
|
+
|
|
83
|
+
**`setTextSize(number)`**
|
|
84
|
+
Sets the text size for the document.
|
|
85
|
+
|
|
86
|
+
**`setCircleScale(number)`**
|
|
87
|
+
Sets the scale for circles drawn on the document.
|
|
88
|
+
|
|
89
|
+
**`setMargin(arr)`**
|
|
90
|
+
Sets the margins for the document.
|
|
91
|
+
|
|
92
|
+
**`setCurrentPage(page)`**
|
|
93
|
+
Sets the current page of the document.
|
|
94
|
+
|
|
95
|
+
**`setColor(color)`**
|
|
96
|
+
Sets the color for text and shapes.
|
|
97
|
+
|
|
98
|
+
**`getPDF()`**
|
|
99
|
+
Returns the current PDF document instance.
|
|
100
|
+
|
|
101
|
+
**`generatePDFURL()`**
|
|
102
|
+
Generates a URL for downloading the PDF document.
|
|
103
|
+
|
|
104
|
+
**`drawText(text, options)`**
|
|
105
|
+
Draws text on the current page.
|
|
106
|
+
|
|
107
|
+
**`drawCircleText(x, y, text, options)`**
|
|
108
|
+
Draws a circle with text centered inside.
|
|
109
|
+
|
|
110
|
+
**`drawTable(header, data, options)`**
|
|
111
|
+
Draws a table with the specified header and data.
|
|
112
|
+
|
|
113
|
+
**`addImage(x, y, base64Image, imageScale)`**
|
|
114
|
+
Adds an image to the PDF document.
|
|
115
|
+
|
|
116
|
+
**`drawWatermark(text, size)`**
|
|
117
|
+
Draws a watermark on each page of the document.
|
|
118
|
+
|
|
119
|
+
## Contributing
|
|
120
|
+
If you find a bug or have a feature request, feel free to open an issue or submit a pull request. Contributions are welcome!
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
This project is licensed under the MIT License.
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pdf-lib-extended",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "This project extends the capabilities of the pdf-lib JavaScript library by providing a set of helper functions that simplify common PDF manipulation tasks. It includes utilities for drawing and formatting text, images, and shapes within PDF documents, allowing for more advanced customization and automation. The class-based architecture, designed as a toolkit, ensures that developers can easily integrate these enhanced features into their existing workflows. With this extension, users can streamline the creation of dynamic and complex PDFs with minimal effort.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"author": "JalenLT",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"pdf-lib": "^1.17.1"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
|
|
2
|
+
|
|
3
|
+
class PDFLibExtended {
|
|
4
|
+
// Private properties for the PDF document, font, current page, text size, circle scale, and margins
|
|
5
|
+
#pdf;
|
|
6
|
+
#currentFont;
|
|
7
|
+
#font;
|
|
8
|
+
#boldFont;
|
|
9
|
+
#italicFont;
|
|
10
|
+
#italicBoldFont;
|
|
11
|
+
#currentPage;
|
|
12
|
+
#textSize;
|
|
13
|
+
#circleScale;
|
|
14
|
+
#margin;
|
|
15
|
+
#color;
|
|
16
|
+
#currentNodes;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
// Initialize the class properties with default values
|
|
20
|
+
this.#pdf = null;
|
|
21
|
+
this.#currentFont = null;
|
|
22
|
+
this.#font = null;
|
|
23
|
+
this.#boldFont = null;
|
|
24
|
+
this.#italicFont = null;
|
|
25
|
+
this.#italicBoldFont = null;
|
|
26
|
+
this.#currentPage = null;
|
|
27
|
+
this.#textSize = 12;
|
|
28
|
+
this.#circleScale = 15;
|
|
29
|
+
this.#color = rgb(0, 0, 0);
|
|
30
|
+
this.#currentNodes = [];
|
|
31
|
+
this.#margin = {
|
|
32
|
+
left: 25,
|
|
33
|
+
right: 25,
|
|
34
|
+
top: 25,
|
|
35
|
+
bottom: 25
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async init() {
|
|
40
|
+
// Asynchronously create the PDF document and embed the font
|
|
41
|
+
this.#pdf = await PDFDocument.create();
|
|
42
|
+
this.#font = await this.#pdf.embedFont(StandardFonts.Helvetica);
|
|
43
|
+
this.#boldFont = await this.#pdf.embedFont(StandardFonts.HelveticaBold);
|
|
44
|
+
this.#italicFont = await this.#pdf.embedFont(StandardFonts.HelveticaOblique);
|
|
45
|
+
this.#italicBoldFont = await this.#pdf.embedFont(StandardFonts.HelveticaBoldOblique);
|
|
46
|
+
this.#currentFont = this.#font;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
*### Adds the provided node to the current nodes
|
|
51
|
+
*
|
|
52
|
+
* @param {string} node
|
|
53
|
+
*/
|
|
54
|
+
addNode(node){
|
|
55
|
+
try {
|
|
56
|
+
if(!node) throw new Error("Please enter a valid value");
|
|
57
|
+
this.#currentNodes.push(node);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Error in addNodes: ", error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
*### Removes the provided node from the current nodes
|
|
65
|
+
*
|
|
66
|
+
* @param {string} node
|
|
67
|
+
*/
|
|
68
|
+
removeNode(node) {
|
|
69
|
+
try {
|
|
70
|
+
if (!node) throw new Error("Please enter a valid value");
|
|
71
|
+
|
|
72
|
+
const lastIndex = this.#currentNodes.lastIndexOf(node);
|
|
73
|
+
|
|
74
|
+
if (lastIndex !== -1) {
|
|
75
|
+
this.#currentNodes.splice(lastIndex, 1);
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("Error in removeNode:", error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
*### Method to set the text size, ensuring it's a valid number greater than 0
|
|
84
|
+
* @param {number} number
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
setTextSize(number) {
|
|
88
|
+
try {
|
|
89
|
+
if (isNaN(Number(number))) throw new Error("The value supplied is not a number: ", number);
|
|
90
|
+
number = Number(number);
|
|
91
|
+
if (number <= 0) throw new Error("The value provided must be larger than 0");
|
|
92
|
+
this.#textSize = number;
|
|
93
|
+
return true;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error("Error in setTextSize: ", error);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
*### Method to set the scale of circles, ensuring it's a valid number greater than 0
|
|
102
|
+
*
|
|
103
|
+
* @param {number} number
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
106
|
+
setCircleScale(number) {
|
|
107
|
+
try {
|
|
108
|
+
if (isNaN(Number(number))) throw new Error("The value supplied is not a number: ", number);
|
|
109
|
+
number = Number(number);
|
|
110
|
+
if (number <= 0) throw new Error("The value provided must be larger than 0");
|
|
111
|
+
this.#circleScale = number;
|
|
112
|
+
return true;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("Error in setCircleScale: ", error);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
*### Method to set the margins, ensuring the input is an array
|
|
121
|
+
*
|
|
122
|
+
* @param {object} arr
|
|
123
|
+
* @returns {boolean}
|
|
124
|
+
*/
|
|
125
|
+
setMargin(arr) {
|
|
126
|
+
try {
|
|
127
|
+
if (typeof arr != 'object' || arr === null || !Array.isArray(arr)) throw new Error("The value supplied is not a JSON/Array: ", arr);
|
|
128
|
+
this.#margin = {
|
|
129
|
+
...this.#margin,
|
|
130
|
+
...arr
|
|
131
|
+
};
|
|
132
|
+
return true;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error("Error in setMargin: ", error);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
*### Method to set the current page for the PDF document
|
|
141
|
+
*
|
|
142
|
+
* @param {page} page
|
|
143
|
+
* @returns {boolean}
|
|
144
|
+
*/
|
|
145
|
+
setCurrentPage(page) {
|
|
146
|
+
try {
|
|
147
|
+
if (!page) throw new Error("Please supply a valid value");
|
|
148
|
+
this.#currentPage = page;
|
|
149
|
+
return true;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error("Error in setCurrentPage: ", error);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
*### Setter for color
|
|
158
|
+
*
|
|
159
|
+
* @param {import('pdf-lib').RGB} color
|
|
160
|
+
* @returns {boolean}
|
|
161
|
+
*/
|
|
162
|
+
setColor(color){
|
|
163
|
+
try {
|
|
164
|
+
if(!color) throw new Error("Please supply a valid color");
|
|
165
|
+
this.#color = color;
|
|
166
|
+
return true;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("Error in setColor: ", error);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
*### Getter for the color
|
|
175
|
+
*
|
|
176
|
+
* @returns {import('pdf-lib').RGB}
|
|
177
|
+
*/
|
|
178
|
+
getColor(){
|
|
179
|
+
return this.#color;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
*### Getter for the current nodes being processed by the HTMLParser
|
|
184
|
+
*
|
|
185
|
+
* @returns {array}
|
|
186
|
+
*/
|
|
187
|
+
getCurrentNodes(){
|
|
188
|
+
return this.#currentNodes;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
*### Getter for the PDF
|
|
193
|
+
*
|
|
194
|
+
* @returns {PDFDocument}
|
|
195
|
+
*/
|
|
196
|
+
getPDF(){
|
|
197
|
+
return this.#pdf;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
*### Setter for the font
|
|
202
|
+
*
|
|
203
|
+
* @param {font} font
|
|
204
|
+
*/
|
|
205
|
+
setFont(font){
|
|
206
|
+
try {
|
|
207
|
+
if(!font) throw new Error("Please provide a font");
|
|
208
|
+
this.getCurrentPage().setFont(font);
|
|
209
|
+
this.#currentFont = font;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error("Error in setFont: ", error);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
*### Getter for the current font
|
|
217
|
+
*
|
|
218
|
+
* @returns {font}
|
|
219
|
+
*/
|
|
220
|
+
getCurrentFont(){
|
|
221
|
+
return this.#currentFont;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
*### Getter for the font
|
|
226
|
+
*
|
|
227
|
+
* @returns {StandardFonts.Helvetica}
|
|
228
|
+
*/
|
|
229
|
+
getFont() {
|
|
230
|
+
return this.#font;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
*### Getter for the Bold font
|
|
235
|
+
*
|
|
236
|
+
* @returns {StandardFonts.HelveticaBold}
|
|
237
|
+
*/
|
|
238
|
+
getBoldFont(){
|
|
239
|
+
return this.#boldFont;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
*### Getter for the Italic font
|
|
244
|
+
*
|
|
245
|
+
* @returns {StandardFonts.HelveticaOblique}
|
|
246
|
+
*/
|
|
247
|
+
getItalicFont(){
|
|
248
|
+
return this.#italicFont;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
*### Getter for the ItalicBold font
|
|
253
|
+
*
|
|
254
|
+
* @returns {StandardFonts.HelveticaBoldOblique}
|
|
255
|
+
*/
|
|
256
|
+
getItalicBoldFont(){
|
|
257
|
+
return this.#italicBoldFont;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
*### Getter for the current text size
|
|
262
|
+
* @returns {number}
|
|
263
|
+
*/
|
|
264
|
+
getTextSize() {
|
|
265
|
+
return this.#textSize;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
*### Getter for the current circle scale
|
|
270
|
+
* @returns {number}
|
|
271
|
+
*/
|
|
272
|
+
getCircleScale() {
|
|
273
|
+
return this.#circleScale;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
*### Getter for the current page of the PDF document
|
|
278
|
+
* @returns {PDFDocument.page}
|
|
279
|
+
*/
|
|
280
|
+
getCurrentPage() {
|
|
281
|
+
return this.#currentPage;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
*### Getter for the document margins
|
|
286
|
+
*
|
|
287
|
+
* @returns {array}
|
|
288
|
+
* @example margin = {
|
|
289
|
+
* left: number,
|
|
290
|
+
* right: number,
|
|
291
|
+
* top: number,
|
|
292
|
+
* bottom: number
|
|
293
|
+
* }
|
|
294
|
+
*/
|
|
295
|
+
getMargin() {
|
|
296
|
+
return this.#margin;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
*### Method to create a new page in the PDF document with optional dimensions
|
|
301
|
+
*
|
|
302
|
+
* @param {array} dimensions
|
|
303
|
+
*/
|
|
304
|
+
addNewPage(dimensions = null) {
|
|
305
|
+
if (!dimensions) this.setCurrentPage(this.#pdf.addPage());
|
|
306
|
+
else this.setCurrentPage(this.#pdf.addPage(dimensions));
|
|
307
|
+
|
|
308
|
+
this.getCurrentPage().moveTo(this.getMargin().left, this.getCurrentPage().getHeight() - this.getMargin().top);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
*### Method to fetch an existing PDF from a URL and load it into the document
|
|
313
|
+
*
|
|
314
|
+
* @param {string} url
|
|
315
|
+
* @returns {Promise<PDFDocument>|boolean}
|
|
316
|
+
*/
|
|
317
|
+
async fetchPDF(url) {
|
|
318
|
+
try {
|
|
319
|
+
if(!url) throw new Error("A URL must be provided");
|
|
320
|
+
const response = await fetch(url);
|
|
321
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
322
|
+
return PDFDocument.load(arrayBuffer);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error("Error in fetchPDF: ", error);
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
*### Fetches PDFs from the provided URLs and appends them to the end of the current document
|
|
331
|
+
* @param {string|array} urls
|
|
332
|
+
* @returns
|
|
333
|
+
*/
|
|
334
|
+
async mergePDF(urls) {
|
|
335
|
+
try {
|
|
336
|
+
if (!urls) throw new Error("A valid URL must be provided");
|
|
337
|
+
|
|
338
|
+
const pdfDoc = this.getPDF();
|
|
339
|
+
|
|
340
|
+
const urlArray = typeof urls === "string" ? [urls] : urls;
|
|
341
|
+
|
|
342
|
+
for (const url of urlArray) {
|
|
343
|
+
const fetchedPdf = await this.fetchPDF(url);
|
|
344
|
+
const copiedPages = await pdfDoc.copyPages(fetchedPdf, fetchedPdf.getPageIndices());
|
|
345
|
+
|
|
346
|
+
copiedPages.forEach(page => pdfDoc.addPage(page));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const pdfBytes = await pdfDoc.save();
|
|
350
|
+
|
|
351
|
+
return pdfBytes;
|
|
352
|
+
|
|
353
|
+
} catch (error) {
|
|
354
|
+
console.error("Error in mergePDF: ", error);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
*### Method to generate a URL for the PDF document
|
|
360
|
+
*
|
|
361
|
+
* @returns {string|boolean}
|
|
362
|
+
*/
|
|
363
|
+
async generatePDFURL() {
|
|
364
|
+
try {
|
|
365
|
+
const pdfBytes = await this.getPDF().save();
|
|
366
|
+
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
|
367
|
+
return URL.createObjectURL(blob);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.error("Error in generatePDFURL: ", error);
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
*### Moves the pointer to the next line of the current page
|
|
376
|
+
*/
|
|
377
|
+
nextLine() {
|
|
378
|
+
this.getCurrentPage().moveTo(this.getMargin().left, this.getCurrentPage().getY() - this.getTextSize());
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
*### Draws text on the current page
|
|
383
|
+
*
|
|
384
|
+
* @param {string} text
|
|
385
|
+
* @param {object} options
|
|
386
|
+
* @param {"left"|"center"|"right"} options.align
|
|
387
|
+
* @param {{left: number, right: number}} options.range - The bounds in which the text is written
|
|
388
|
+
* @param {number} options.size - The size of the text
|
|
389
|
+
* @param {import('pdf-lib').RGB} options.color - The color of the text
|
|
390
|
+
* @param {number} options.opacity - The opacity of the text
|
|
391
|
+
*/
|
|
392
|
+
drawText(text, options = {}) {
|
|
393
|
+
let defaultOptions = {
|
|
394
|
+
align: "left",
|
|
395
|
+
range: {
|
|
396
|
+
left: this.getMargin().left,
|
|
397
|
+
right: this.getCurrentPage().getWidth() - this.getMargin().right
|
|
398
|
+
},
|
|
399
|
+
size: this.getTextSize(),
|
|
400
|
+
color: this.getColor(),
|
|
401
|
+
opacity: 1,
|
|
402
|
+
...options,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
switch (defaultOptions.align) {
|
|
406
|
+
case "left":
|
|
407
|
+
this.getCurrentPage().drawText(text, {
|
|
408
|
+
size: defaultOptions.size,
|
|
409
|
+
color: defaultOptions.color,
|
|
410
|
+
opacity: defaultOptions.opacity
|
|
411
|
+
});
|
|
412
|
+
break;
|
|
413
|
+
|
|
414
|
+
case "center":
|
|
415
|
+
let xCenterPosition = defaultOptions.range.left +
|
|
416
|
+
((defaultOptions.range.right - defaultOptions.range.left) / 2) -
|
|
417
|
+
(this.getCurrentFont().widthOfTextAtSize(text, defaultOptions.size) / 2);
|
|
418
|
+
|
|
419
|
+
let currentPositionCenter = {
|
|
420
|
+
x: this.getCurrentPage().getX(),
|
|
421
|
+
y: this.getCurrentPage().getY()
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
this.getCurrentPage().moveTo(xCenterPosition, this.getCurrentPage().getY());
|
|
425
|
+
this.getCurrentPage().drawText(text, {
|
|
426
|
+
size: defaultOptions.size,
|
|
427
|
+
color: defaultOptions.color,
|
|
428
|
+
opacity: defaultOptions.opacity
|
|
429
|
+
});
|
|
430
|
+
this.getCurrentPage().moveTo(currentPositionCenter.x, currentPositionCenter.y);
|
|
431
|
+
break;
|
|
432
|
+
|
|
433
|
+
case "right":
|
|
434
|
+
let textWidth = this.getCurrentFont().widthOfTextAtSize(text, defaultOptions.size);
|
|
435
|
+
let xRightPosition = defaultOptions.range.right - textWidth;
|
|
436
|
+
|
|
437
|
+
let currentPositionRight = {
|
|
438
|
+
x: this.getCurrentPage().getX(),
|
|
439
|
+
y: this.getCurrentPage().getY()
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
this.getCurrentPage().moveTo(xRightPosition, this.getCurrentPage().getY());
|
|
443
|
+
this.getCurrentPage().drawText(text, {
|
|
444
|
+
size: defaultOptions.size,
|
|
445
|
+
color: defaultOptions.color,
|
|
446
|
+
opacity: defaultOptions.opacity
|
|
447
|
+
});
|
|
448
|
+
this.getCurrentPage().moveTo(currentPositionRight.x, currentPositionRight.y);
|
|
449
|
+
break;
|
|
450
|
+
|
|
451
|
+
default:
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
*### Draws text that automatically wraps to the next line
|
|
458
|
+
*
|
|
459
|
+
* @param {string} text
|
|
460
|
+
* @param {object} options
|
|
461
|
+
* @param {"left"|"center"|"right"} options.align
|
|
462
|
+
* @param {{left: number, right: number}} options.range - The bounds in which the paragraph is written
|
|
463
|
+
* @param {number} options.size - The size of the text
|
|
464
|
+
* @param {import('pdf-lib').RGB} options.color - The color of the text
|
|
465
|
+
* @param {number} options.opacity - The opacity of the text
|
|
466
|
+
*/
|
|
467
|
+
drawParagraph(text, options = {}) {
|
|
468
|
+
let defaultOptions = {
|
|
469
|
+
align: "left",
|
|
470
|
+
range: {
|
|
471
|
+
left: this.getMargin().left,
|
|
472
|
+
right: this.getCurrentPage().getWidth() - this.getMargin().right
|
|
473
|
+
},
|
|
474
|
+
size: this.getTextSize(),
|
|
475
|
+
color: this.getColor(),
|
|
476
|
+
opacity: 1,
|
|
477
|
+
...options
|
|
478
|
+
};
|
|
479
|
+
if(options.range) this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
480
|
+
|
|
481
|
+
let maxWidth = defaultOptions.range.right - defaultOptions.range.left;
|
|
482
|
+
let currentWidth = 0;
|
|
483
|
+
let currentLine = "";
|
|
484
|
+
|
|
485
|
+
text = text.split(" ");
|
|
486
|
+
|
|
487
|
+
text.forEach((string, i) => {
|
|
488
|
+
let wordWidth = this.getCurrentFont().widthOfTextAtSize(string, defaultOptions.size);
|
|
489
|
+
|
|
490
|
+
// Check if adding this word would overflow
|
|
491
|
+
if (currentWidth + wordWidth > maxWidth) {
|
|
492
|
+
this.drawText(currentLine.trim(), {
|
|
493
|
+
size: defaultOptions.size,
|
|
494
|
+
color: defaultOptions.color,
|
|
495
|
+
opacity: defaultOptions.opacity,
|
|
496
|
+
align: defaultOptions.align,
|
|
497
|
+
range: defaultOptions.range
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Move to the next line
|
|
501
|
+
this.nextLine();
|
|
502
|
+
this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
503
|
+
currentLine = "";
|
|
504
|
+
currentWidth = 0;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
currentLine += string + " ";
|
|
508
|
+
currentWidth += wordWidth;
|
|
509
|
+
|
|
510
|
+
// If it's the last word, draw the remaining line
|
|
511
|
+
if (i === text.length - 1) {
|
|
512
|
+
this.drawText(currentLine.trim(), {
|
|
513
|
+
size: defaultOptions.size,
|
|
514
|
+
color: defaultOptions.color,
|
|
515
|
+
opacity: defaultOptions.opacity,
|
|
516
|
+
align: defaultOptions.align,
|
|
517
|
+
range: defaultOptions.range
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
*### Draws a cell on the current page
|
|
525
|
+
*
|
|
526
|
+
* @param {string} text
|
|
527
|
+
* @param {number} x
|
|
528
|
+
* @param {number} y
|
|
529
|
+
* @param {number} width
|
|
530
|
+
* @param {object} options
|
|
531
|
+
* @param {number} options.height - The height of the cell
|
|
532
|
+
* @param {boolean|string} options.border - The border of the cell.
|
|
533
|
+
* Can be:
|
|
534
|
+
* - `true`/`false` to toggle all borders
|
|
535
|
+
* - A string with specific borders: `"t"` (top), `"r"` (right), `"b"` (bottom), `"l"` (left)
|
|
536
|
+
* - A combination of these letters to apply multiple borders, e.g., `"tb"` for top and bottom or `"tbl"` for top, bottom, and left.
|
|
537
|
+
* @param {"left"|"center"|"right"} options.align
|
|
538
|
+
* @param {boolean} options.newLine
|
|
539
|
+
* @param {number} options.size - The size of the text
|
|
540
|
+
* @param {import('pdf-lib').RGB} options.color - The color of the border and text
|
|
541
|
+
* @param {number} options.lineThickness - The thickness of the border
|
|
542
|
+
* @param {number} options.padding
|
|
543
|
+
* @param {number} options.borderOpacity
|
|
544
|
+
*/
|
|
545
|
+
drawCell(text, x, y, width, options = {}){
|
|
546
|
+
let defaultOptions = {
|
|
547
|
+
height: null,
|
|
548
|
+
border: false,
|
|
549
|
+
align: "left",
|
|
550
|
+
newLine: true,
|
|
551
|
+
size: this.getTextSize(),
|
|
552
|
+
color: this.getColor(),
|
|
553
|
+
lineThickness: 1,
|
|
554
|
+
padding: 4,
|
|
555
|
+
borderOpacity: 0.3,
|
|
556
|
+
...options
|
|
557
|
+
};
|
|
558
|
+
let page = this.getCurrentPage();
|
|
559
|
+
page.moveTo(x, y);
|
|
560
|
+
let change = page.getY();
|
|
561
|
+
this.drawParagraph(text, {range: {left: x, right: x + width}, align: defaultOptions.align, size: defaultOptions.size, color: defaultOptions.color});
|
|
562
|
+
change -= page.getY() - defaultOptions.size;
|
|
563
|
+
|
|
564
|
+
y += defaultOptions.size;
|
|
565
|
+
|
|
566
|
+
/*** BORDERS ***/
|
|
567
|
+
if(defaultOptions.border){
|
|
568
|
+
// TOP
|
|
569
|
+
if(defaultOptions.border === true || defaultOptions.border.includes("t") || defaultOptions.border.includes("T")){
|
|
570
|
+
page.drawLine({
|
|
571
|
+
start: {x: x - defaultOptions.padding, y: y},
|
|
572
|
+
end: {x: x + width, y: y},
|
|
573
|
+
thickness: defaultOptions.lineThickness,
|
|
574
|
+
color: defaultOptions.color,
|
|
575
|
+
opacity: defaultOptions.borderOpacity
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
// BOTTOM
|
|
579
|
+
if(defaultOptions.border === true || defaultOptions.border.includes("b") || defaultOptions.border.includes("B")){
|
|
580
|
+
page.drawLine({
|
|
581
|
+
start: {x: x - defaultOptions.padding, y: ((defaultOptions.height) ? y - defaultOptions.height : y - change - defaultOptions.padding)},
|
|
582
|
+
end: {x: x + width, y: ((defaultOptions.height) ? y - defaultOptions.height : y - change -defaultOptions.padding)},
|
|
583
|
+
thickness: defaultOptions.lineThickness,
|
|
584
|
+
color: defaultOptions.color,
|
|
585
|
+
opacity: defaultOptions.borderOpacity
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
// LEFT
|
|
589
|
+
if(defaultOptions.border === true || defaultOptions.border.includes("l") || defaultOptions.border.includes("L")){
|
|
590
|
+
page.drawLine({
|
|
591
|
+
start: {x: x - defaultOptions.padding, y: y + (defaultOptions.lineThickness / 2)},
|
|
592
|
+
end: {x: x - defaultOptions.padding, y: ((defaultOptions.height) ? y - defaultOptions.height - (defaultOptions.lineThickness / 2) : y - change - defaultOptions.padding - (defaultOptions.lineThickness / 2))},
|
|
593
|
+
thickness: defaultOptions.lineThickness,
|
|
594
|
+
color: defaultOptions.color,
|
|
595
|
+
opacity: defaultOptions.borderOpacity
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
// RIGHT
|
|
599
|
+
if(defaultOptions.border === true || defaultOptions.border.includes("r") || defaultOptions.border.includes("R")){
|
|
600
|
+
page.drawLine({
|
|
601
|
+
start: {x: x + width, y: y + (defaultOptions.lineThickness / 2)},
|
|
602
|
+
end: {x: x + width, y: ((defaultOptions.height) ? y - defaultOptions.height - (defaultOptions.lineThickness / 2) : y - change - defaultOptions.padding - (defaultOptions.lineThickness / 2))},
|
|
603
|
+
thickness: defaultOptions.lineThickness,
|
|
604
|
+
color: defaultOptions.color,
|
|
605
|
+
opacity: defaultOptions.borderOpacity
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if(defaultOptions.newLine){
|
|
611
|
+
this.nextLine();
|
|
612
|
+
page.moveTo(page.getX(), y - Number(defaultOptions.height) - change - defaultOptions.size - defaultOptions.padding);
|
|
613
|
+
}
|
|
614
|
+
else page.moveTo(x + width + defaultOptions.padding, y - defaultOptions.size);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
*### Draws a table
|
|
619
|
+
*
|
|
620
|
+
* @param {array} header
|
|
621
|
+
* @param {array} data
|
|
622
|
+
* @param {object} options
|
|
623
|
+
* @param {boolean|string} options.border - The border of the cell.
|
|
624
|
+
* Can be:
|
|
625
|
+
* - `true`/`false` to toggle all borders
|
|
626
|
+
* - A string with specific borders: `"t"` (top), `"r"` (right), `"b"` (bottom), `"l"` (left)
|
|
627
|
+
* - A combination of these letters to apply multiple borders, e.g., `"tb"` for top and bottom or `"tbl"` for top, bottom, and left.
|
|
628
|
+
* @param {{left: number, right: number}} options.range - The bounds in which the paragraph is written
|
|
629
|
+
* @param {"left"|"center"|"right"} options.align
|
|
630
|
+
* @param {number} options.size - The size of the text
|
|
631
|
+
* @param {import('pdf-lib').RGB} options.color - The color of the border and text
|
|
632
|
+
* @param {number} options.headerDifference - The difference in size of the header text from the regular text size
|
|
633
|
+
*/
|
|
634
|
+
drawTable(header = null, data = null, options = {}){
|
|
635
|
+
let defaultOptions = {
|
|
636
|
+
range: {
|
|
637
|
+
left: this.getMargin().left,
|
|
638
|
+
right: this.getCurrentPage().getWidth() - this.getMargin().right
|
|
639
|
+
},
|
|
640
|
+
headerDifference: 0,
|
|
641
|
+
align: "center",
|
|
642
|
+
border: true,
|
|
643
|
+
size: this.getTextSize(),
|
|
644
|
+
color: this.getColor(),
|
|
645
|
+
...options
|
|
646
|
+
};
|
|
647
|
+
if (options.range) this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
648
|
+
|
|
649
|
+
let cellWidth = (defaultOptions.range.right - defaultOptions.range.left) / 3;
|
|
650
|
+
let pointer = {
|
|
651
|
+
x: defaultOptions.range.left,
|
|
652
|
+
y: this.getCurrentPage().getY()
|
|
653
|
+
}
|
|
654
|
+
this.getCurrentPage().moveTo(pointer.x, pointer.y);
|
|
655
|
+
if(header){
|
|
656
|
+
this.setFont(this.getBoldFont());
|
|
657
|
+
header.forEach((head, i) => {
|
|
658
|
+
this.drawCell(head, this.getCurrentPage().getX(), this.getCurrentPage().getY(), cellWidth, {
|
|
659
|
+
border: defaultOptions.border,
|
|
660
|
+
align: defaultOptions.align,
|
|
661
|
+
size: defaultOptions.size + defaultOptions.headerDifference,
|
|
662
|
+
color: defaultOptions.color,
|
|
663
|
+
newLine: (i === header.length - 1) ? true : false
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
if(data){
|
|
668
|
+
this.setFont(this.getFont());
|
|
669
|
+
this.getCurrentPage().moveTo(pointer.x, this.getCurrentPage().getY() + defaultOptions.headerDifference);
|
|
670
|
+
data.forEach((row) => {
|
|
671
|
+
this.getCurrentPage().moveTo(pointer.x, this.getCurrentPage().getY());
|
|
672
|
+
row.forEach((string, i) => {
|
|
673
|
+
this.drawCell(string, this.getCurrentPage().getX(), this.getCurrentPage().getY(), cellWidth, {
|
|
674
|
+
border: defaultOptions.border,
|
|
675
|
+
align: defaultOptions.align,
|
|
676
|
+
size: defaultOptions.size + defaultOptions.headerDifference,
|
|
677
|
+
color: defaultOptions.color,
|
|
678
|
+
newLine: (i === header.length - 1) ? true : false
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
*
|
|
687
|
+
* @param {string} text - HTML text
|
|
688
|
+
* @param {DomParser} parser
|
|
689
|
+
* @param {object} options
|
|
690
|
+
* @param {number} options.margin
|
|
691
|
+
* @param {{left: number, right: number}} options.range - The bounds in which the paragraph is written
|
|
692
|
+
* @param {"left"|"center"|"right"} options.align
|
|
693
|
+
* @param {number} options.size - The size of the text
|
|
694
|
+
* @param {import('pdf-lib').RGB} options.color - The color of the border and text
|
|
695
|
+
*/
|
|
696
|
+
htmlParser(text, parser = null, options = {}){
|
|
697
|
+
let doc;
|
|
698
|
+
if(!parser){
|
|
699
|
+
parser = new DOMParser();
|
|
700
|
+
doc = parser.parseFromString(text, "text/html");
|
|
701
|
+
}else doc = text;
|
|
702
|
+
let nodes = doc.childNodes;
|
|
703
|
+
let defaultOptions = {
|
|
704
|
+
margin: 8,
|
|
705
|
+
range: {
|
|
706
|
+
left: this.getMargin().left,
|
|
707
|
+
right: this.getCurrentPage().getWidth() - this.getMargin().right
|
|
708
|
+
},
|
|
709
|
+
align: "left",
|
|
710
|
+
color: this.getColor(),
|
|
711
|
+
size: this.getTextSize(),
|
|
712
|
+
...options,
|
|
713
|
+
}
|
|
714
|
+
if (defaultOptions.range && !parser) this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
715
|
+
let maxWidth = defaultOptions.range.right;
|
|
716
|
+
|
|
717
|
+
if(nodes.length > 0){
|
|
718
|
+
for(let node of nodes){
|
|
719
|
+
if(node.nodeType === Node.ELEMENT_NODE){
|
|
720
|
+
this.addNode(node.nodeName);
|
|
721
|
+
switch (node.nodeName) {
|
|
722
|
+
case "P":
|
|
723
|
+
this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY() - defaultOptions.margin);
|
|
724
|
+
this.nextLine();
|
|
725
|
+
this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
726
|
+
break;
|
|
727
|
+
case "STRONG":
|
|
728
|
+
if(this.getCurrentNodes().includes("I")) this.setFont(this.getItalicBoldFont());
|
|
729
|
+
else this.setFont(this.getBoldFont());
|
|
730
|
+
break;
|
|
731
|
+
case "I":
|
|
732
|
+
if(this.getCurrentNodes().includes("STRONG")) this.setFont(this.getItalicBoldFont());
|
|
733
|
+
else this.setFont(this.getItalicFont());
|
|
734
|
+
break;
|
|
735
|
+
case "UL":
|
|
736
|
+
break;
|
|
737
|
+
case "LI":
|
|
738
|
+
this.nextLine();
|
|
739
|
+
this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
740
|
+
this.getCurrentPage().moveRight(this.getCurrentNodes().filter(value => value === "UL").length * this.getMargin().left);
|
|
741
|
+
this.drawText("• ", { size: defaultOptions.size, color: defaultOptions.color });
|
|
742
|
+
this.getCurrentPage().moveRight(this.getCurrentFont().widthOfTextAtSize("• ", defaultOptions.size));
|
|
743
|
+
break;
|
|
744
|
+
case "TABLE":
|
|
745
|
+
let tableHead = [];
|
|
746
|
+
let tableData = [];
|
|
747
|
+
node.querySelectorAll("thead th").forEach(element => {
|
|
748
|
+
tableHead.push(element.innerHTML.replace(/<\/?[^>]+(>|$)/g, ""));
|
|
749
|
+
});
|
|
750
|
+
node.querySelectorAll("tbody tr").forEach(row => {
|
|
751
|
+
let tableRow = [];
|
|
752
|
+
row.querySelectorAll("td").forEach(col => {
|
|
753
|
+
tableRow.push(col.innerHTML.replace(/<\/?[^>]+(>|$)/g, ""));
|
|
754
|
+
});
|
|
755
|
+
tableData.push(tableRow);
|
|
756
|
+
});
|
|
757
|
+
this.nextLine();
|
|
758
|
+
this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
759
|
+
this.drawTable(tableHead, tableData, {
|
|
760
|
+
range: {left: defaultOptions.range.left, right: defaultOptions.range.right},
|
|
761
|
+
size: defaultOptions.size,
|
|
762
|
+
color: defaultOptions.color,
|
|
763
|
+
});
|
|
764
|
+
break;
|
|
765
|
+
default:
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if(node.nodeName === "TABLE") break;
|
|
771
|
+
|
|
772
|
+
this.htmlParser(node, parser, defaultOptions);
|
|
773
|
+
}
|
|
774
|
+
}else{
|
|
775
|
+
if(text.data){
|
|
776
|
+
/*** DRAW TEXT ***/
|
|
777
|
+
let splitText = text.data.split(" ");
|
|
778
|
+
splitText.forEach(string => {
|
|
779
|
+
string += " ";
|
|
780
|
+
let wordWidth = this.getCurrentFont().widthOfTextAtSize(string, defaultOptions.size);
|
|
781
|
+
if(wordWidth + this.getCurrentPage().getX() >= maxWidth){
|
|
782
|
+
this.nextLine();
|
|
783
|
+
this.getCurrentPage().moveTo(defaultOptions.range.left, this.getCurrentPage().getY());
|
|
784
|
+
}
|
|
785
|
+
this.drawText(string, {
|
|
786
|
+
size: defaultOptions.size,
|
|
787
|
+
color: defaultOptions.color
|
|
788
|
+
});
|
|
789
|
+
this.getCurrentPage().moveRight(this.getCurrentFont().widthOfTextAtSize(string, defaultOptions.size));
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
/*** REMOVE STYLINGS ***/
|
|
793
|
+
this.checkParentHTML(text);
|
|
794
|
+
}
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
*
|
|
801
|
+
* @param {string} text
|
|
802
|
+
* @returns
|
|
803
|
+
*/
|
|
804
|
+
checkParentHTML(text){
|
|
805
|
+
if(!text.parentNode) return;
|
|
806
|
+
switch (text.parentNode.nodeName) {
|
|
807
|
+
case "STRONG":
|
|
808
|
+
if(!text.nextSibling){
|
|
809
|
+
if(this.getCurrentNodes().includes("I")) this.setFont(this.getItalicFont());
|
|
810
|
+
else this.setFont(this.getFont());
|
|
811
|
+
this.removeNode(text.parentNode.nodeName);
|
|
812
|
+
}
|
|
813
|
+
break;
|
|
814
|
+
case "I":
|
|
815
|
+
if(!text.nextSibling){
|
|
816
|
+
if(this.getCurrentNodes().includes("B")) this.setFont(this.getBoldFont());
|
|
817
|
+
else this.setFont(this.getFont());
|
|
818
|
+
this.removeNode(text.parentNode.nodeName);
|
|
819
|
+
}
|
|
820
|
+
break;
|
|
821
|
+
case "LI":
|
|
822
|
+
if(!text.nextSibling){
|
|
823
|
+
this.removeNode(text.parentNode.nodeName);
|
|
824
|
+
}
|
|
825
|
+
break;
|
|
826
|
+
case "UL":
|
|
827
|
+
if(!text.nextSibling){
|
|
828
|
+
this.removeNode(text.parentNode.nodeName);
|
|
829
|
+
}
|
|
830
|
+
break;
|
|
831
|
+
default:
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
if(!text.parentNode.nextSibling){
|
|
835
|
+
this.checkParentHTML(text.parentNode);
|
|
836
|
+
}else{
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
*### Draws a cirlce with text in the center
|
|
843
|
+
*
|
|
844
|
+
* @param {number} x
|
|
845
|
+
* @param {number} y
|
|
846
|
+
* @param {string} text
|
|
847
|
+
* @param {object} options
|
|
848
|
+
* @param {number} options.size - The size of the text
|
|
849
|
+
* @param {import('pdf-lib').RGB} options.color - The color of the text and circle
|
|
850
|
+
* @param {number} options.borderWidth
|
|
851
|
+
*/
|
|
852
|
+
drawCircleText(x, y, text, options = {}){
|
|
853
|
+
let defaultOptions = {
|
|
854
|
+
size: this.getTextSize(),
|
|
855
|
+
color: this.getColor(),
|
|
856
|
+
borderWidth: 2,
|
|
857
|
+
...options
|
|
858
|
+
};
|
|
859
|
+
// Calculate common values
|
|
860
|
+
const centerX = x;
|
|
861
|
+
const centerY = y - (this.getCurrentFont().widthOfTextAtSize(text, defaultOptions.size)) - 5;
|
|
862
|
+
const textX = centerX - (this.getCurrentFont().widthOfTextAtSize(text, defaultOptions.size) / 2);
|
|
863
|
+
const textY = centerY - (defaultOptions.size / 3);
|
|
864
|
+
|
|
865
|
+
// Draw the circle
|
|
866
|
+
this.getCurrentPage().drawEllipse({
|
|
867
|
+
x: centerX,
|
|
868
|
+
y: centerY,
|
|
869
|
+
xScale: this.getCurrentFont().widthOfTextAtSize(text, defaultOptions.size),
|
|
870
|
+
yScale: this.getCurrentFont().widthOfTextAtSize(text, defaultOptions.size),
|
|
871
|
+
borderWidth: defaultOptions.borderWidth,
|
|
872
|
+
borderColor: defaultOptions.color,
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// Draw the text
|
|
876
|
+
this.getCurrentPage().drawText(String(text), {
|
|
877
|
+
x: textX,
|
|
878
|
+
y: textY,
|
|
879
|
+
color: defaultOptions.color,
|
|
880
|
+
size: defaultOptions.size,
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
*### Method to add an image to the PDF document at specified coordinates with optional scaling
|
|
886
|
+
* @param {number} x
|
|
887
|
+
* @param {number} y
|
|
888
|
+
* @param {base64} base64Image
|
|
889
|
+
* @param {number} imageScale
|
|
890
|
+
*/
|
|
891
|
+
async addImage(x, y, base64Image, imageScale = 1) {
|
|
892
|
+
if (base64Image.includes(",")) base64Image = base64Image.split(",")[1];
|
|
893
|
+
let imageBytes = Uint8Array.from(atob(base64Image), c => c.charCodeAt(0));
|
|
894
|
+
let embeddedImage = await this.getPDF().embedPng(imageBytes);
|
|
895
|
+
let { width, height } = embeddedImage.scale(imageScale);
|
|
896
|
+
this.getCurrentPage().drawImage(embeddedImage, {
|
|
897
|
+
x: x,
|
|
898
|
+
y: y,
|
|
899
|
+
width: width,
|
|
900
|
+
height: height,
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
*### Draws a watermark on each page of the document
|
|
906
|
+
* @param {string} text
|
|
907
|
+
* @param {number} size
|
|
908
|
+
* @returns
|
|
909
|
+
*/
|
|
910
|
+
drawWatermark(text, size = 30){
|
|
911
|
+
try {
|
|
912
|
+
if(!text) throw new Error("The text provided must be a valid value");
|
|
913
|
+
this.getPDF().getPages().forEach(page => {
|
|
914
|
+
page.drawText(text, {
|
|
915
|
+
x: (page.getWidth() / 2) - (this.getBoldFont().widthOfTextAtSize(text, size) * 0.36),
|
|
916
|
+
y: (page.getHeight() / 2) + (this.getBoldFont().widthOfTextAtSize(text, size) * 0.36),
|
|
917
|
+
font: this.getBoldFont(),
|
|
918
|
+
size: size,
|
|
919
|
+
color: this.getColor(),
|
|
920
|
+
opacity: 0.25,
|
|
921
|
+
rotate: degrees(-45)
|
|
922
|
+
});
|
|
923
|
+
});
|
|
924
|
+
return true;
|
|
925
|
+
} catch (error) {
|
|
926
|
+
console.error("Error in drawWatermark: ", error);
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
export default PDFLibExtended;
|