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.
@@ -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
- if (this.dontBreakRows && rowIndex > 0) {
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
- if (this.dontBreakRows) {
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);
@@ -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 || inline.color || 'black';
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
- if (!inline.background) {
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
  }
@@ -1,13 +1,48 @@
1
- async function fetchUrl(url, headers = {}) {
2
- try {
3
- const response = await fetch(url, { headers });
4
- if (!response.ok) {
5
- throw new Error(`Failed to fetch (status code: ${response.status}, url: "${url}")`);
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
- if ((typeof this.urlAccessPolicy !== 'undefined') && (this.urlAccessPolicy(url) !== true)) {
35
- throw new Error(`Access to URL denied by resource access policy: ${url}`);
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 fetchUrl(url, headers);
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
  }