pdfmake 0.3.7 → 0.3.8
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/CHANGELOG.md +16 -0
- package/build/pdfmake.js +781 -602
- package/build/pdfmake.js.map +1 -1
- package/build/pdfmake.min.js +2 -2
- package/build/pdfmake.min.js.map +1 -1
- package/js/LayoutBuilder.js +10 -1
- package/js/PDFDocument.js +37 -2
- package/js/Printer.js +4 -2
- package/js/Renderer.js +6 -6
- package/js/TableProcessor.js +15 -2
- package/js/TextDecorator.js +5 -5
- package/js/URLResolver.js +52 -15
- package/js/base.js +5 -1
- package/js/index.js +10 -0
- package/package.json +8 -8
- package/src/LayoutBuilder.js +14 -1
- package/src/PDFDocument.js +48 -3
- package/src/Printer.js +4 -2
- package/src/Renderer.js +6 -6
- package/src/TableProcessor.js +21 -2
- package/src/TextDecorator.js +6 -5
- package/src/URLResolver.js +51 -11
- package/src/base.js +7 -1
- package/src/index.js +11 -0
package/src/TableProcessor.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import ColumnCalculator from './columnCalculator';
|
|
2
2
|
import { isNumber, isPositiveInteger } from './helpers/variableType';
|
|
3
3
|
|
|
4
|
+
const PAGE_BREAK_VALUES = new Set(['before', 'beforeOdd', 'beforeEven', 'after', 'afterOdd', 'afterEven']);
|
|
5
|
+
|
|
6
|
+
const hasExplicitPageBreak = cell => {
|
|
7
|
+
if (!cell || typeof cell !== 'object') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return PAGE_BREAK_VALUES.has(cell.pageBreak);
|
|
12
|
+
};
|
|
13
|
+
|
|
4
14
|
class TableProcessor {
|
|
5
15
|
constructor(tableNode) {
|
|
6
16
|
this.tableNode = tableNode;
|
|
17
|
+
this._isCurrentRowUnbreakable = false;
|
|
7
18
|
}
|
|
8
19
|
|
|
9
20
|
beginTable(writer) {
|
|
@@ -163,7 +174,11 @@ class TableProcessor {
|
|
|
163
174
|
|
|
164
175
|
this.rowTopPageY = writer.context().y + this.rowPaddingTop;
|
|
165
176
|
|
|
166
|
-
|
|
177
|
+
const rowCells = this.tableNode.table.body[rowIndex] || [];
|
|
178
|
+
const rowHasPageBreak = rowCells.some(hasExplicitPageBreak);
|
|
179
|
+
this._isCurrentRowUnbreakable = this.dontBreakRows && rowIndex > 0 && !rowHasPageBreak;
|
|
180
|
+
|
|
181
|
+
if (this._isCurrentRowUnbreakable) {
|
|
167
182
|
writer.beginUnbreakableBlock();
|
|
168
183
|
}
|
|
169
184
|
this.rowTopY = writer.context().y;
|
|
@@ -589,7 +604,9 @@ class TableProcessor {
|
|
|
589
604
|
this.headerRepeatable = writer.currentBlockToRepeatable();
|
|
590
605
|
}
|
|
591
606
|
|
|
592
|
-
|
|
607
|
+
const shouldCommitCurrentRowUnbreakable = this.dontBreakRows && (rowIndex === 0 || this._isCurrentRowUnbreakable);
|
|
608
|
+
|
|
609
|
+
if (shouldCommitCurrentRowUnbreakable) {
|
|
593
610
|
const pageChangedCallback = () => {
|
|
594
611
|
if (rowIndex > 0 && !this.headerRows && this.layout.hLineWhenBroken !== false) {
|
|
595
612
|
// Draw the top border of the row after a page break
|
|
@@ -604,6 +621,8 @@ class TableProcessor {
|
|
|
604
621
|
writer.removeListener('pageChanged', pageChangedCallback);
|
|
605
622
|
}
|
|
606
623
|
|
|
624
|
+
this._isCurrentRowUnbreakable = false;
|
|
625
|
+
|
|
607
626
|
if (this.headerRepeatable && (rowIndex === (this.rowsWithoutPageBreak - 1) || rowIndex === this.tableNode.table.body.length - 1)) {
|
|
608
627
|
writer.commitUnbreakableBlock();
|
|
609
628
|
writer.pushToRepeatables(this.headerRepeatable);
|
package/src/TextDecorator.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isNumber } from './helpers/variableType';
|
|
2
2
|
|
|
3
|
-
const groupDecorations = line => {
|
|
3
|
+
const groupDecorations = (line, pdfDocument) => {
|
|
4
4
|
let groups = [];
|
|
5
5
|
let currentGroup = null;
|
|
6
6
|
for (let i = 0, l = line.inlines.length; i < l; i++) {
|
|
@@ -13,7 +13,7 @@ const groupDecorations = line => {
|
|
|
13
13
|
if (!Array.isArray(decoration)) {
|
|
14
14
|
decoration = [decoration];
|
|
15
15
|
}
|
|
16
|
-
let color = inline.decorationColor
|
|
16
|
+
let color = pdfDocument.resolveColor(pdfDocument.resolveColor(inline.decorationColor, inline.color), 'black');
|
|
17
17
|
let style = inline.decorationStyle || 'solid';
|
|
18
18
|
let thickness = isNumber(inline.decorationThickness) ? inline.decorationThickness : null;
|
|
19
19
|
for (let ii = 0, ll = decoration.length; ii < ll; ii++) {
|
|
@@ -49,11 +49,12 @@ class TextDecorator {
|
|
|
49
49
|
let height = line.getHeight();
|
|
50
50
|
for (let i = 0, l = line.inlines.length; i < l; i++) {
|
|
51
51
|
let inline = line.inlines[i];
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
let color = this.pdfDocument.resolveColor(inline.background, undefined);
|
|
54
|
+
if (!color) {
|
|
53
55
|
continue;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
let color = inline.background;
|
|
57
58
|
let patternColor = this.pdfDocument.providePattern(inline.background);
|
|
58
59
|
if (patternColor !== null) {
|
|
59
60
|
color = patternColor;
|
|
@@ -67,7 +68,7 @@ class TextDecorator {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
drawDecorations(line, x, y) {
|
|
70
|
-
let groups = groupDecorations(line);
|
|
71
|
+
let groups = groupDecorations(line, this.pdfDocument);
|
|
71
72
|
for (let i = 0, l = groups.length; i < l; i++) {
|
|
72
73
|
this._drawDecoration(groups[i], x, y);
|
|
73
74
|
}
|
package/src/URLResolver.js
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
|
|
2
|
+
const MAX_REDIRECTS = 30;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} url
|
|
6
|
+
* @param {object} headers
|
|
7
|
+
* @param {(url: string) => boolean} urlAccessPolicy
|
|
8
|
+
* @returns {Promise<Response>}
|
|
9
|
+
*/
|
|
10
|
+
async function fetchUrl(url, headers = {}, urlAccessPolicy) {
|
|
11
|
+
for (let i = 0; i <= MAX_REDIRECTS; i++) {
|
|
12
|
+
if ((typeof urlAccessPolicy !== 'undefined') && (urlAccessPolicy(url) !== true)) {
|
|
13
|
+
throw new Error(`Access to URL denied by resource access policy: ${url}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
let response = await fetch(url, { headers, redirect: 'manual' });
|
|
18
|
+
|
|
19
|
+
// redirect url
|
|
20
|
+
if (response.status >= 300 && response.status < 400) {
|
|
21
|
+
let location = response.headers.get('location');
|
|
22
|
+
if (!location) {
|
|
23
|
+
throw new Error('Redirect response missing Location header');
|
|
24
|
+
}
|
|
25
|
+
url = new URL(location, url).href;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// browsers do not support redirect: 'manual'
|
|
30
|
+
if (response.type === 'opaqueredirect') {
|
|
31
|
+
response = await fetch(url, {headers});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`Failed to fetch (status code: ${response.status})`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new Error(`Network request failed (url: "${url}", error: ${error.message})`, {cause: error});
|
|
6
42
|
}
|
|
7
|
-
return await response.arrayBuffer();
|
|
8
|
-
} catch (error) {
|
|
9
|
-
throw new Error(`Network request failed (url: "${url}", error: ${error.message})`, { cause: error });
|
|
10
43
|
}
|
|
44
|
+
|
|
45
|
+
throw new Error(`Network request failed (url: "${url}", error: Too many redirects)`);
|
|
11
46
|
}
|
|
12
47
|
|
|
13
48
|
class URLResolver {
|
|
@@ -31,11 +66,16 @@ class URLResolver {
|
|
|
31
66
|
return; // url was downloaded earlier
|
|
32
67
|
}
|
|
33
68
|
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
const response = await fetchUrl(url, headers, this.urlAccessPolicy);
|
|
70
|
+
|
|
71
|
+
// validate access policy on redirected url (in browsers, only the final URL is validated)
|
|
72
|
+
if (response.redirected) {
|
|
73
|
+
if ((typeof this.urlAccessPolicy !== 'undefined') && (this.urlAccessPolicy(response.url) !== true)) {
|
|
74
|
+
throw new Error(`Access to URL denied by resource access policy: ${response.url}`);
|
|
75
|
+
}
|
|
36
76
|
}
|
|
37
77
|
|
|
38
|
-
const buffer = await
|
|
78
|
+
const buffer = await response.arrayBuffer();
|
|
39
79
|
this.fs.writeFileSync(url, buffer);
|
|
40
80
|
}
|
|
41
81
|
// else cannot be resolved
|
package/src/base.js
CHANGED
|
@@ -9,6 +9,7 @@ class pdfmake {
|
|
|
9
9
|
constructor() {
|
|
10
10
|
this.virtualfs = virtualfs;
|
|
11
11
|
this.urlAccessPolicy = undefined;
|
|
12
|
+
this.localAccessPolicy = undefined;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -34,11 +35,16 @@ class pdfmake {
|
|
|
34
35
|
'No URL access policy defined. Consider using setUrlAccessPolicy() to restrict external resource downloads.'
|
|
35
36
|
);
|
|
36
37
|
}
|
|
38
|
+
if (typeof this.localAccessPolicy === 'undefined' && isServer) {
|
|
39
|
+
console.warn(
|
|
40
|
+
'No local access policy defined. Consider using setLocalAccessPolicy() to restrict local file system access.'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
37
43
|
|
|
38
44
|
let urlResolver = new URLResolver(this.virtualfs);
|
|
39
45
|
urlResolver.setUrlAccessPolicy(this.urlAccessPolicy);
|
|
40
46
|
|
|
41
|
-
let printer = new Printer(this.fonts, this.virtualfs, urlResolver);
|
|
47
|
+
let printer = new Printer(this.fonts, this.virtualfs, urlResolver, this.localAccessPolicy);
|
|
42
48
|
const pdfDocumentPromise = printer.createPdfKitDocument(docDefinition, options);
|
|
43
49
|
|
|
44
50
|
return this._transformToDocument(pdfDocumentPromise);
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,17 @@ class pdfmake extends pdfmakeBase {
|
|
|
6
6
|
super();
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @param {(path: string) => boolean} callback
|
|
11
|
+
*/
|
|
12
|
+
setLocalAccessPolicy(callback) {
|
|
13
|
+
if (callback !== undefined && typeof callback !== 'function') {
|
|
14
|
+
throw new Error("Parameter 'callback' has an invalid type. Function or undefined expected.");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.localAccessPolicy = callback;
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
_transformToDocument(doc) {
|
|
10
21
|
return new OutputDocumentServer(doc);
|
|
11
22
|
}
|