pdfmake 0.2.4 → 0.3.0-beta.2
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 +10 -27
- package/README.md +11 -7
- package/build/pdfmake.js +44733 -42435
- package/build/pdfmake.js.map +1 -1
- package/build/pdfmake.min.js +2 -2
- package/build/pdfmake.min.js.map +1 -1
- package/build/standard-fonts/Courier.js +27 -0
- package/build/standard-fonts/Helvetica.js +27 -0
- package/build/standard-fonts/Symbol.js +21 -0
- package/build/standard-fonts/Times.js +27 -0
- package/build/standard-fonts/ZapfDingbats.js +21 -0
- package/build/vfs_fonts.js +2 -2
- package/build-vfs.js +2 -2
- package/fonts/Roboto/Roboto-Italic.ttf +0 -0
- package/fonts/Roboto/Roboto-Medium.ttf +0 -0
- package/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
- package/fonts/Roboto/Roboto-Regular.ttf +0 -0
- package/fonts/Roboto.js +8 -0
- package/js/3rd-party/svg-to-pdfkit/source.js +4301 -0
- package/js/3rd-party/svg-to-pdfkit.js +11 -0
- package/js/DocMeasure.js +758 -0
- package/js/DocPreprocessor.js +291 -0
- package/js/DocumentContext.js +306 -0
- package/js/ElementWriter.js +400 -0
- package/js/LayoutBuilder.js +840 -0
- package/js/Line.js +125 -0
- package/js/OutputDocument.js +86 -0
- package/js/OutputDocumentServer.js +34 -0
- package/js/PDFDocument.js +190 -0
- package/js/PageElementWriter.js +165 -0
- package/js/PageSize.js +88 -0
- package/js/Printer.js +300 -0
- package/js/Renderer.js +463 -0
- package/js/SVGMeasure.js +89 -0
- package/js/StyleContextStack.js +206 -0
- package/js/TableProcessor.js +590 -0
- package/js/TextBreaker.js +182 -0
- package/js/TextDecorator.js +181 -0
- package/js/TextInlines.js +248 -0
- package/js/URLResolver.js +82 -0
- package/js/base.js +69 -0
- package/js/browser-extensions/OutputDocumentBrowser.js +131 -0
- package/js/browser-extensions/URLBrowserResolver.js +94 -0
- package/js/browser-extensions/fonts/Roboto.js +42 -0
- package/js/browser-extensions/index.js +70 -0
- package/js/browser-extensions/pdfMake.js +17 -0
- package/js/browser-extensions/standard-fonts/Courier.js +42 -0
- package/js/browser-extensions/standard-fonts/Helvetica.js +42 -0
- package/js/browser-extensions/standard-fonts/Symbol.js +27 -0
- package/js/browser-extensions/standard-fonts/Times.js +42 -0
- package/js/browser-extensions/standard-fonts/ZapfDingbats.js +27 -0
- package/js/browser-extensions/virtual-fs-cjs.js +3 -0
- package/js/columnCalculator.js +142 -0
- package/js/helpers/node.js +122 -0
- package/js/helpers/tools.js +48 -0
- package/js/helpers/variableType.js +52 -0
- package/js/index.js +21 -0
- package/js/qrEnc.js +810 -0
- package/js/standardPageSizes.js +57 -0
- package/js/tableLayouts.js +127 -0
- package/js/virtual-fs.js +77 -0
- package/package.json +26 -22
- package/src/3rd-party/svg-to-pdfkit.js +2 -2
- package/src/DocMeasure.js +703 -0
- package/src/DocPreprocessor.js +264 -0
- package/src/DocumentContext.js +309 -0
- package/src/ElementWriter.js +394 -0
- package/src/LayoutBuilder.js +821 -0
- package/src/Line.js +114 -0
- package/src/OutputDocument.js +78 -0
- package/src/OutputDocumentServer.js +26 -0
- package/src/PDFDocument.js +174 -0
- package/src/PageElementWriter.js +160 -0
- package/src/PageSize.js +53 -0
- package/src/Printer.js +277 -0
- package/src/Renderer.js +405 -0
- package/src/SVGMeasure.js +79 -0
- package/src/StyleContextStack.js +216 -0
- package/src/TableProcessor.js +558 -0
- package/src/TextBreaker.js +149 -0
- package/src/TextDecorator.js +161 -0
- package/src/TextInlines.js +223 -0
- package/src/URLResolver.js +72 -0
- package/src/base.js +61 -0
- package/src/browser-extensions/OutputDocumentBrowser.js +117 -0
- package/src/browser-extensions/URLBrowserResolver.js +49 -53
- package/src/browser-extensions/fonts/Roboto.js +27 -0
- package/src/browser-extensions/index.js +55 -0
- package/src/browser-extensions/pdfMake.js +10 -282
- package/src/browser-extensions/standard-fonts/Courier.js +27 -0
- package/src/browser-extensions/standard-fonts/Helvetica.js +27 -0
- package/src/browser-extensions/standard-fonts/Symbol.js +21 -0
- package/src/browser-extensions/standard-fonts/Times.js +27 -0
- package/src/browser-extensions/standard-fonts/ZapfDingbats.js +21 -0
- package/src/browser-extensions/virtual-fs-cjs.js +1 -0
- package/src/columnCalculator.js +29 -32
- package/src/helpers/node.js +110 -0
- package/src/helpers/tools.js +39 -0
- package/src/helpers/variableType.js +39 -0
- package/src/index.js +16 -0
- package/src/qrEnc.js +15 -10
- package/src/standardPageSizes.js +1 -3
- package/src/tableLayouts.js +100 -0
- package/src/virtual-fs.js +66 -0
- package/standard-fonts/Courier.js +8 -0
- package/standard-fonts/Helvetica.js +9 -0
- package/standard-fonts/Symbol.js +5 -0
- package/standard-fonts/Times.js +8 -0
- package/standard-fonts/ZapfDingbats.js +5 -0
- package/src/browser-extensions/virtual-fs.js +0 -55
- package/src/docMeasure.js +0 -807
- package/src/docPreprocessor.js +0 -255
- package/src/documentContext.js +0 -314
- package/src/elementWriter.js +0 -322
- package/src/fontProvider.js +0 -68
- package/src/helpers.js +0 -126
- package/src/imageMeasure.js +0 -51
- package/src/layoutBuilder.js +0 -807
- package/src/line.js +0 -91
- package/src/pageElementWriter.js +0 -174
- package/src/pdfKitEngine.js +0 -21
- package/src/printer.js +0 -705
- package/src/styleContextStack.js +0 -179
- package/src/svgMeasure.js +0 -70
- package/src/tableProcessor.js +0 -561
- package/src/textDecorator.js +0 -157
- package/src/textTools.js +0 -373
- package/src/traversalTracker.js +0 -47
package/src/Line.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
class Line {
|
|
2
|
+
/**
|
|
3
|
+
* @param {number} maxWidth Maximum width this line can have
|
|
4
|
+
*/
|
|
5
|
+
constructor(maxWidth) {
|
|
6
|
+
this.maxWidth = maxWidth;
|
|
7
|
+
this.leadingCut = 0;
|
|
8
|
+
this.trailingCut = 0;
|
|
9
|
+
this.inlineWidths = 0;
|
|
10
|
+
this.inlines = [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} inline
|
|
15
|
+
*/
|
|
16
|
+
addInline(inline) {
|
|
17
|
+
if (this.inlines.length === 0) {
|
|
18
|
+
this.leadingCut = inline.leadingCut || 0;
|
|
19
|
+
}
|
|
20
|
+
this.trailingCut = inline.trailingCut || 0;
|
|
21
|
+
|
|
22
|
+
inline.x = this.inlineWidths - this.leadingCut;
|
|
23
|
+
|
|
24
|
+
this.inlines.push(inline);
|
|
25
|
+
this.inlineWidths += inline.width;
|
|
26
|
+
|
|
27
|
+
if (inline.lineEnd) {
|
|
28
|
+
this.newLineForced = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @returns {number}
|
|
34
|
+
*/
|
|
35
|
+
getHeight() {
|
|
36
|
+
let max = 0;
|
|
37
|
+
|
|
38
|
+
this.inlines.forEach(item => {
|
|
39
|
+
max = Math.max(max, item.height || 0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return max;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @returns {number}
|
|
47
|
+
*/
|
|
48
|
+
getAscenderHeight() {
|
|
49
|
+
let y = 0;
|
|
50
|
+
|
|
51
|
+
this.inlines.forEach(inline => {
|
|
52
|
+
y = Math.max(y, inline.font.ascender / 1000 * inline.fontSize);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return y;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @returns {number}
|
|
60
|
+
*/
|
|
61
|
+
getWidth() {
|
|
62
|
+
return this.inlineWidths - this.leadingCut - this.trailingCut;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @returns {number}
|
|
67
|
+
*/
|
|
68
|
+
getAvailableWidth() {
|
|
69
|
+
return this.maxWidth - this.getWidth();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {object} inline
|
|
74
|
+
* @param {Array} nextInlines
|
|
75
|
+
* @returns {boolean}
|
|
76
|
+
*/
|
|
77
|
+
hasEnoughSpaceForInline(inline, nextInlines = []) {
|
|
78
|
+
if (this.inlines.length === 0) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (this.newLineForced) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let inlineWidth = inline.width;
|
|
86
|
+
let inlineTrailingCut = inline.trailingCut || 0;
|
|
87
|
+
if (inline.noNewLine) {
|
|
88
|
+
for (let i = 0, l = nextInlines.length; i < l; i++) {
|
|
89
|
+
let nextInline = nextInlines[i];
|
|
90
|
+
inlineWidth += nextInline.width;
|
|
91
|
+
inlineTrailingCut += nextInline.trailingCut || 0;
|
|
92
|
+
if (!nextInline.noNewLine) {
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (this.inlineWidths + inlineWidth - this.leadingCut - inlineTrailingCut) <= this.maxWidth;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
clone() {
|
|
102
|
+
let result = new Line(this.maxWidth);
|
|
103
|
+
|
|
104
|
+
for (let key in this) {
|
|
105
|
+
if (this.hasOwnProperty(key)) {
|
|
106
|
+
result[key] = this[key];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default Line;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
class OutputDocument {
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {Promise<object>} pdfDocumentPromise
|
|
5
|
+
*/
|
|
6
|
+
constructor(pdfDocumentPromise) {
|
|
7
|
+
this.bufferSize = 9007199254740991;
|
|
8
|
+
this.pdfDocumentPromise = pdfDocumentPromise;
|
|
9
|
+
this.bufferPromise = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @returns {Promise<object>}
|
|
14
|
+
*/
|
|
15
|
+
getStream() {
|
|
16
|
+
return this.pdfDocumentPromise;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @returns {Promise<Buffer>}
|
|
21
|
+
*/
|
|
22
|
+
getBuffer() {
|
|
23
|
+
if (this.bufferPromise === null) {
|
|
24
|
+
this.bufferPromise = new Promise((resolve, reject) => {
|
|
25
|
+
this.getStream().then(stream => {
|
|
26
|
+
|
|
27
|
+
let chunks = [];
|
|
28
|
+
let result;
|
|
29
|
+
stream.on('readable', () => {
|
|
30
|
+
let chunk;
|
|
31
|
+
while ((chunk = stream.read(this.bufferSize)) !== null) {
|
|
32
|
+
chunks.push(chunk);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
stream.on('end', () => {
|
|
36
|
+
result = Buffer.concat(chunks);
|
|
37
|
+
resolve(result);
|
|
38
|
+
});
|
|
39
|
+
stream.end();
|
|
40
|
+
|
|
41
|
+
}, result => {
|
|
42
|
+
reject(result);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.bufferPromise;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @returns {Promise<string>}
|
|
52
|
+
*/
|
|
53
|
+
getBase64() {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
this.getBuffer().then(buffer => {
|
|
56
|
+
resolve(buffer.toString('base64'));
|
|
57
|
+
}, result => {
|
|
58
|
+
reject(result);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @returns {Promise<string>}
|
|
65
|
+
*/
|
|
66
|
+
getDataUrl() {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
this.getBase64().then(data => {
|
|
69
|
+
resolve('data:application/pdf;base64,' + data);
|
|
70
|
+
}, result => {
|
|
71
|
+
reject(result);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default OutputDocument;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import OutputDocument from './OutputDocument';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
class OutputDocumentServer extends OutputDocument {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} filename
|
|
8
|
+
* @returns {Promise}
|
|
9
|
+
*/
|
|
10
|
+
write(filename) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
this.getStream().then(stream => {
|
|
13
|
+
stream.pipe(fs.createWriteStream(filename));
|
|
14
|
+
stream.on('end', () => {
|
|
15
|
+
resolve();
|
|
16
|
+
});
|
|
17
|
+
stream.end();
|
|
18
|
+
}, result => {
|
|
19
|
+
reject(result);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default OutputDocumentServer;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import PDFKit from '@foliojs-fork/pdfkit';
|
|
2
|
+
|
|
3
|
+
const typeName = (bold, italics) => {
|
|
4
|
+
let type = 'normal';
|
|
5
|
+
if (bold && italics) {
|
|
6
|
+
type = 'bolditalics';
|
|
7
|
+
} else if (bold) {
|
|
8
|
+
type = 'bold';
|
|
9
|
+
} else if (italics) {
|
|
10
|
+
type = 'italics';
|
|
11
|
+
}
|
|
12
|
+
return type;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class PDFDocument extends PDFKit {
|
|
16
|
+
constructor(fonts = {}, images = {}, patterns = {}, attachments = {}, options = {}, virtualfs = null) {
|
|
17
|
+
super(options);
|
|
18
|
+
|
|
19
|
+
this.fonts = {};
|
|
20
|
+
this.fontCache = {};
|
|
21
|
+
for (let font in fonts) {
|
|
22
|
+
if (fonts.hasOwnProperty(font)) {
|
|
23
|
+
let fontDef = fonts[font];
|
|
24
|
+
|
|
25
|
+
this.fonts[font] = {
|
|
26
|
+
normal: fontDef.normal,
|
|
27
|
+
bold: fontDef.bold,
|
|
28
|
+
italics: fontDef.italics,
|
|
29
|
+
bolditalics: fontDef.bolditalics
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.patterns = {};
|
|
35
|
+
for (let pattern in patterns) {
|
|
36
|
+
if (patterns.hasOwnProperty(pattern)) {
|
|
37
|
+
let patternDef = patterns[pattern];
|
|
38
|
+
this.patterns[pattern] = this.pattern(patternDef.boundingBox, patternDef.xStep, patternDef.yStep, patternDef.pattern, patternDef.colored);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
this.images = images;
|
|
44
|
+
this.attachments = attachments;
|
|
45
|
+
this.virtualfs = virtualfs;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getFontType(bold, italics) {
|
|
49
|
+
return typeName(bold, italics);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getFontFile(familyName, bold, italics) {
|
|
53
|
+
let type = this.getFontType(bold, italics);
|
|
54
|
+
if (!this.fonts[familyName] || !this.fonts[familyName][type]) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return this.fonts[familyName][type];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
provideFont(familyName, bold, italics) {
|
|
62
|
+
let type = this.getFontType(bold, italics);
|
|
63
|
+
if (this.getFontFile(familyName, bold, italics) === null) {
|
|
64
|
+
throw new Error(`Font '${familyName}' in style '${type}' is not defined in the font section of the document definition.`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.fontCache[familyName] = this.fontCache[familyName] || {};
|
|
68
|
+
|
|
69
|
+
if (!this.fontCache[familyName][type]) {
|
|
70
|
+
let def = this.fonts[familyName][type];
|
|
71
|
+
if (!Array.isArray(def)) {
|
|
72
|
+
def = [def];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this.virtualfs && this.virtualfs.existsSync(def[0])) {
|
|
76
|
+
def[0] = this.virtualfs.readFileSync(def[0]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.fontCache[familyName][type] = this.font(...def)._font;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return this.fontCache[familyName][type];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
provideImage(src) {
|
|
86
|
+
const realImageSrc = src => {
|
|
87
|
+
let image = this.images[src];
|
|
88
|
+
|
|
89
|
+
if (!image) {
|
|
90
|
+
return src;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (this.virtualfs && this.virtualfs.existsSync(image)) {
|
|
94
|
+
return this.virtualfs.readFileSync(image);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let index = image.indexOf('base64,');
|
|
98
|
+
if (index < 0) {
|
|
99
|
+
return this.images[src];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Buffer.from(image.substring(index + 7), 'base64');
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (this._imageRegistry[src]) {
|
|
106
|
+
return this._imageRegistry[src];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let image;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
image = this.openImage(realImageSrc(src));
|
|
113
|
+
if (!image) {
|
|
114
|
+
throw new Error('No image');
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw new Error(`Invalid image: ${error.toString()}\nImages dictionary should contain dataURL entries (or local file paths in node.js)`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
image.embed(this);
|
|
121
|
+
this._imageRegistry[src] = image;
|
|
122
|
+
|
|
123
|
+
return image;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {Array} color pdfmake format: [<pattern name>, <color>]
|
|
128
|
+
* @returns {Array} pdfkit format: [<pattern object>, <color>]
|
|
129
|
+
*/
|
|
130
|
+
providePattern(color) {
|
|
131
|
+
if (Array.isArray(color) && color.length === 2) {
|
|
132
|
+
return [this.patterns[color[0]], color[1]];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
provideAttachment(src) {
|
|
139
|
+
const checkRequired = obj => {
|
|
140
|
+
if (!obj) {
|
|
141
|
+
throw new Error('No attachment');
|
|
142
|
+
}
|
|
143
|
+
if (!obj.src) {
|
|
144
|
+
throw new Error('The "src" key is required for attachments');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return obj;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (typeof src === 'object') {
|
|
151
|
+
return checkRequired(src);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let attachment = checkRequired(this.attachments[src]);
|
|
155
|
+
|
|
156
|
+
if (this.virtualfs && this.virtualfs.existsSync(attachment.src)) {
|
|
157
|
+
return this.virtualfs.readFileSync(attachment.src);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return attachment;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
setOpenActionAsPrint() {
|
|
164
|
+
let printActionRef = this.ref({
|
|
165
|
+
Type: 'Action',
|
|
166
|
+
S: 'Named',
|
|
167
|
+
N: 'Print'
|
|
168
|
+
});
|
|
169
|
+
this._root.data.OpenAction = printActionRef;
|
|
170
|
+
printActionRef.end();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default PDFDocument;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import ElementWriter from './ElementWriter';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An extended ElementWriter which can handle:
|
|
5
|
+
* - page-breaks (it adds new pages when there's not enough space left),
|
|
6
|
+
* - repeatable fragments (like table-headers, which are repeated everytime
|
|
7
|
+
* a page-break occurs)
|
|
8
|
+
* - transactions (used for unbreakable-blocks when we want to make sure
|
|
9
|
+
* whole block will be rendered on the same page)
|
|
10
|
+
*/
|
|
11
|
+
class PageElementWriter extends ElementWriter {
|
|
12
|
+
constructor(context) {
|
|
13
|
+
super(context);
|
|
14
|
+
this.transactionLevel = 0;
|
|
15
|
+
this.repeatables = [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
addLine(line, dontUpdateContextPosition, index) {
|
|
19
|
+
return this._fitOnPage(() => super.addLine(line, dontUpdateContextPosition, index));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
addImage(image, index) {
|
|
23
|
+
return this._fitOnPage(() => super.addImage(image, index));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
addCanvas(image, index) {
|
|
27
|
+
return this._fitOnPage(() => super.addCanvas(image, index));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addSVG(image, index) {
|
|
31
|
+
return this._fitOnPage(() => super.addSVG(image, index));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
addQr(qr, index) {
|
|
35
|
+
return this._fitOnPage(() => super.addQr(qr, index));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
addAttachment(attachment, index) {
|
|
39
|
+
return this._fitOnPage(() => super.addAttachment(attachment, index));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
addVector(vector, ignoreContextX, ignoreContextY, index) {
|
|
43
|
+
return super.addVector(vector, ignoreContextX, ignoreContextY, index);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
beginClip(width, height) {
|
|
47
|
+
return super.beginClip(width, height);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
endClip() {
|
|
51
|
+
return super.endClip();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition) {
|
|
55
|
+
return this._fitOnPage(() => super.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
moveToNextPage(pageOrientation) {
|
|
59
|
+
let nextPage = this.context().moveToNextPage(pageOrientation);
|
|
60
|
+
|
|
61
|
+
// moveToNextPage is called multiple times for table, because is called for each column
|
|
62
|
+
// and repeatables are inserted only in the first time. If columns are used, is needed
|
|
63
|
+
// call for table in first column and then for table in the second column (is other repeatables).
|
|
64
|
+
this.repeatables.forEach(function (rep) {
|
|
65
|
+
if (rep.insertedOnPages[this.context().page] === undefined) {
|
|
66
|
+
rep.insertedOnPages[this.context().page] = true;
|
|
67
|
+
this.addFragment(rep, true);
|
|
68
|
+
} else {
|
|
69
|
+
this.context().moveDown(rep.height);
|
|
70
|
+
}
|
|
71
|
+
}, this);
|
|
72
|
+
|
|
73
|
+
this.emit('pageChanged', {
|
|
74
|
+
prevPage: nextPage.prevPage,
|
|
75
|
+
prevY: nextPage.prevY,
|
|
76
|
+
y: this.context().y
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
beginUnbreakableBlock(width, height) {
|
|
81
|
+
if (this.transactionLevel++ === 0) {
|
|
82
|
+
this.originalX = this.context().x;
|
|
83
|
+
this.pushContext(width, height);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
commitUnbreakableBlock(forcedX, forcedY) {
|
|
88
|
+
if (--this.transactionLevel === 0) {
|
|
89
|
+
let unbreakableContext = this.context();
|
|
90
|
+
this.popContext();
|
|
91
|
+
|
|
92
|
+
let nbPages = unbreakableContext.pages.length;
|
|
93
|
+
if (nbPages > 0) {
|
|
94
|
+
// no support for multi-page unbreakableBlocks
|
|
95
|
+
let fragment = unbreakableContext.pages[0];
|
|
96
|
+
fragment.xOffset = forcedX;
|
|
97
|
+
fragment.yOffset = forcedY;
|
|
98
|
+
|
|
99
|
+
//TODO: vectors can influence height in some situations
|
|
100
|
+
if (nbPages > 1) {
|
|
101
|
+
// on out-of-context blocs (headers, footers, background) height should be the whole DocumentContext height
|
|
102
|
+
if (forcedX !== undefined || forcedY !== undefined) {
|
|
103
|
+
fragment.height = unbreakableContext.getCurrentPage().pageSize.height - unbreakableContext.pageMargins.top - unbreakableContext.pageMargins.bottom;
|
|
104
|
+
} else {
|
|
105
|
+
fragment.height = this.context().getCurrentPage().pageSize.height - this.context().pageMargins.top - this.context().pageMargins.bottom;
|
|
106
|
+
for (let i = 0, l = this.repeatables.length; i < l; i++) {
|
|
107
|
+
fragment.height -= this.repeatables[i].height;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
fragment.height = unbreakableContext.y;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (forcedX !== undefined || forcedY !== undefined) {
|
|
115
|
+
super.addFragment(fragment, true, true, true);
|
|
116
|
+
} else {
|
|
117
|
+
this.addFragment(fragment);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
currentBlockToRepeatable() {
|
|
124
|
+
let unbreakableContext = this.context();
|
|
125
|
+
let rep = { items: [] };
|
|
126
|
+
|
|
127
|
+
unbreakableContext.pages[0].items.forEach(item => {
|
|
128
|
+
rep.items.push(item);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
rep.xOffset = this.originalX;
|
|
132
|
+
|
|
133
|
+
//TODO: vectors can influence height in some situations
|
|
134
|
+
rep.height = unbreakableContext.y;
|
|
135
|
+
|
|
136
|
+
rep.insertedOnPages = [];
|
|
137
|
+
|
|
138
|
+
return rep;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
pushToRepeatables(rep) {
|
|
142
|
+
this.repeatables.push(rep);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
popFromRepeatables() {
|
|
146
|
+
this.repeatables.pop();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_fitOnPage(addFct) {
|
|
150
|
+
let position = addFct();
|
|
151
|
+
if (!position) {
|
|
152
|
+
this.moveToNextPage();
|
|
153
|
+
position = addFct();
|
|
154
|
+
}
|
|
155
|
+
return position;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export default PageElementWriter;
|
package/src/PageSize.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import sizes from './standardPageSizes';
|
|
2
|
+
import { isString, isNumber } from './helpers/variableType';
|
|
3
|
+
|
|
4
|
+
export function normalizePageSize(pageSize, pageOrientation) {
|
|
5
|
+
function isNeedSwapPageSizes(pageOrientation) {
|
|
6
|
+
if (isString(pageOrientation)) {
|
|
7
|
+
pageOrientation = pageOrientation.toLowerCase();
|
|
8
|
+
return ((pageOrientation === 'portrait') && (size.width > size.height)) ||
|
|
9
|
+
((pageOrientation === 'landscape') && (size.width < size.height));
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function pageSizeToWidthAndHeight(pageSize) {
|
|
15
|
+
if (isString(pageSize)) {
|
|
16
|
+
let size = sizes[pageSize.toUpperCase()];
|
|
17
|
+
if (!size) {
|
|
18
|
+
throw new Error(`Page size ${pageSize} not recognized`);
|
|
19
|
+
}
|
|
20
|
+
return { width: size[0], height: size[1] };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return pageSize;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// if pageSize.height is set to auto, set the height to infinity so there are no page breaks.
|
|
27
|
+
if (pageSize && pageSize.height === 'auto') {
|
|
28
|
+
pageSize.height = Infinity;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let size = pageSizeToWidthAndHeight(pageSize || 'A4');
|
|
32
|
+
if (isNeedSwapPageSizes(pageOrientation)) { // swap page sizes
|
|
33
|
+
size = { width: size.height, height: size.width };
|
|
34
|
+
}
|
|
35
|
+
size.orientation = size.width > size.height ? 'landscape' : 'portrait';
|
|
36
|
+
return size;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function normalizePageMargin(margin) {
|
|
40
|
+
if (isNumber(margin)) {
|
|
41
|
+
margin = { left: margin, right: margin, top: margin, bottom: margin };
|
|
42
|
+
} else if (Array.isArray(margin)) {
|
|
43
|
+
if (margin.length === 2) {
|
|
44
|
+
margin = { left: margin[0], top: margin[1], right: margin[0], bottom: margin[1] };
|
|
45
|
+
} else if (margin.length === 4) {
|
|
46
|
+
margin = { left: margin[0], top: margin[1], right: margin[2], bottom: margin[3] };
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error('Invalid pageMargins definition');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return margin;
|
|
53
|
+
}
|