@digicole/pdfmake-rtl 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.
@@ -0,0 +1,273 @@
1
+ 'use strict';
2
+
3
+ var isString = require('./helpers').isString;
4
+ var isNumber = require('./helpers').isNumber;
5
+ var isBoolean = require('./helpers').isBoolean;
6
+ var isArray = require('./helpers').isArray;
7
+ var isUndefined = require('./helpers').isUndefined;
8
+ var fontStringify = require('./helpers').fontStringify;
9
+ var rtlUtils = require('./rtlUtils');
10
+
11
+ function DocPreprocessor() {
12
+
13
+ }
14
+
15
+ DocPreprocessor.prototype.preprocessDocument = function (docStructure) {
16
+ this.parentNode = null;
17
+ this.tocs = [];
18
+ this.nodeReferences = [];
19
+ return this.preprocessNode(docStructure);
20
+ };
21
+
22
+ DocPreprocessor.prototype.preprocessNode = function (node) {
23
+ // expand shortcuts and casting values
24
+ if (isArray(node)) {
25
+ node = { stack: node };
26
+ } else if (isString(node)) {
27
+ node = { text: node };
28
+ } else if (isNumber(node) || isBoolean(node)) {
29
+ node = { text: node.toString() };
30
+ } else if (node === undefined || node === null) {
31
+ node = { text: '' };
32
+ } else if (Object.keys(node).length === 0) { // empty object
33
+ node = { text: '' };
34
+ } else if ('text' in node && (node.text === undefined || node.text === null)) {
35
+ node.text = '';
36
+ }
37
+
38
+ if (node.columns) {
39
+ return this.preprocessColumns(node);
40
+ } else if (node.stack) {
41
+ return this.preprocessVerticalContainer(node);
42
+ } else if (node.ul) {
43
+ return this.preprocessList(node);
44
+ } else if (node.ol) {
45
+ return this.preprocessList(node);
46
+ } else if (node.table) {
47
+ return this.preprocessTable(node);
48
+ } else if (node.text !== undefined) {
49
+ return this.preprocessText(node);
50
+ } else if (node.toc) {
51
+ return this.preprocessToc(node);
52
+ } else if (node.image) {
53
+ return this.preprocessImage(node);
54
+ } else if (node.svg) {
55
+ return this.preprocessSVG(node);
56
+ } else if (node.canvas) {
57
+ return this.preprocessCanvas(node);
58
+ } else if (node.qr) {
59
+ return this.preprocessQr(node);
60
+ } else if (node.pageReference || node.textReference) {
61
+ return this.preprocessText(node);
62
+ } else {
63
+ throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
64
+ }
65
+ };
66
+
67
+ DocPreprocessor.prototype.preprocessColumns = function (node) {
68
+ var columns = node.columns;
69
+
70
+ for (var i = 0, l = columns.length; i < l; i++) {
71
+ columns[i] = this.preprocessNode(columns[i]);
72
+ }
73
+
74
+ return node;
75
+ };
76
+
77
+ DocPreprocessor.prototype.preprocessVerticalContainer = function (node) {
78
+ var items = node.stack;
79
+
80
+ for (var i = 0, l = items.length; i < l; i++) {
81
+ items[i] = this.preprocessNode(items[i]);
82
+ }
83
+
84
+ return node;
85
+ };
86
+
87
+ DocPreprocessor.prototype.preprocessList = function (node) {
88
+ var items = node.ul || node.ol;
89
+
90
+ // Apply automatic RTL detection to list items
91
+ if (node.ul) {
92
+ node.ul = rtlUtils.processRTLList(node.ul);
93
+ items = node.ul;
94
+ } else if (node.ol) {
95
+ node.ol = rtlUtils.processRTLList(node.ol);
96
+ items = node.ol;
97
+ }
98
+
99
+ for (var i = 0, l = items.length; i < l; i++) {
100
+ items[i] = this.preprocessNode(items[i]);
101
+ }
102
+
103
+ return node;
104
+ };
105
+
106
+ DocPreprocessor.prototype.preprocessTable = function (node) {
107
+ var col, row, cols, rows;
108
+
109
+ // Apply automatic RTL detection and processing
110
+ node = rtlUtils.processAutoRTLTable(node);
111
+
112
+ for (col = 0, cols = node.table.body[0].length; col < cols; col++) {
113
+ for (row = 0, rows = node.table.body.length; row < rows; row++) {
114
+ var rowData = node.table.body[row];
115
+ var data = rowData[col];
116
+ if (data !== undefined) {
117
+ if (data === null) { // transform to object
118
+ data = '';
119
+ }
120
+ if (!data._span) {
121
+ // Apply automatic RTL detection to cell content
122
+ data = rtlUtils.autoApplyRTL(data);
123
+ rowData[col] = this.preprocessNode(data);
124
+ }
125
+ }
126
+ }
127
+ }
128
+
129
+ return node;
130
+ };
131
+
132
+ DocPreprocessor.prototype.preprocessText = function (node) {
133
+ // Apply automatic RTL detection to text elements
134
+ node = rtlUtils.autoApplyRTL(node);
135
+
136
+ if (node.tocItem) {
137
+ if (!isArray(node.tocItem)) {
138
+ node.tocItem = [node.tocItem];
139
+ }
140
+
141
+ for (var i = 0, l = node.tocItem.length; i < l; i++) {
142
+ if (!isString(node.tocItem[i])) {
143
+ node.tocItem[i] = '_default_';
144
+ }
145
+
146
+ var tocItemId = node.tocItem[i];
147
+
148
+ if (!this.tocs[tocItemId]) {
149
+ this.tocs[tocItemId] = { toc: { _items: [], _pseudo: true } };
150
+ }
151
+
152
+ if (!node.id) {
153
+ node.id = 'toc-' + tocItemId + '-' + this.tocs[tocItemId].toc._items.length;
154
+ }
155
+
156
+ var tocItemRef = {
157
+ _nodeRef: this._getNodeForNodeRef(node),
158
+ _textNodeRef: node
159
+ };
160
+ this.tocs[tocItemId].toc._items.push(tocItemRef);
161
+ }
162
+ }
163
+
164
+ if (node.id) {
165
+ if (this.nodeReferences[node.id]) {
166
+ if (!this.nodeReferences[node.id]._pseudo) {
167
+ throw "Node id '" + node.id + "' already exists";
168
+ }
169
+
170
+ this.nodeReferences[node.id]._nodeRef = this._getNodeForNodeRef(node);
171
+ this.nodeReferences[node.id]._textNodeRef = node;
172
+ this.nodeReferences[node.id]._pseudo = false;
173
+ } else {
174
+ this.nodeReferences[node.id] = {
175
+ _nodeRef: this._getNodeForNodeRef(node),
176
+ _textNodeRef: node
177
+ };
178
+ }
179
+ }
180
+
181
+ if (node.pageReference) {
182
+ if (!this.nodeReferences[node.pageReference]) {
183
+ this.nodeReferences[node.pageReference] = {
184
+ _nodeRef: {},
185
+ _textNodeRef: {},
186
+ _pseudo: true
187
+ };
188
+ }
189
+ node.text = '00000';
190
+ node.linkToDestination = node.pageReference;
191
+ node._pageRef = this.nodeReferences[node.pageReference];
192
+ }
193
+
194
+ if (node.textReference) {
195
+ if (!this.nodeReferences[node.textReference]) {
196
+ this.nodeReferences[node.textReference] = { _nodeRef: {}, _pseudo: true };
197
+ }
198
+
199
+ node.text = '';
200
+ node.linkToDestination = node.textReference;
201
+ node._textRef = this.nodeReferences[node.textReference];
202
+ }
203
+
204
+ if (node.text && node.text.text) {
205
+ node.text = [this.preprocessNode(node.text)];
206
+ } else if (isArray(node.text)) {
207
+ var isSetParentNode = false;
208
+ if (this.parentNode === null) {
209
+ this.parentNode = node;
210
+ isSetParentNode = true;
211
+ }
212
+
213
+ for (var i = 0, l = node.text.length; i < l; i++) {
214
+ node.text[i] = this.preprocessNode(node.text[i]);
215
+ }
216
+
217
+ if (isSetParentNode) {
218
+ this.parentNode = null;
219
+ }
220
+ }
221
+
222
+ return node;
223
+ };
224
+
225
+ DocPreprocessor.prototype.preprocessToc = function (node) {
226
+ if (!node.toc.id) {
227
+ node.toc.id = '_default_';
228
+ }
229
+
230
+ node.toc.title = node.toc.title ? this.preprocessNode(node.toc.title) : null;
231
+ node.toc._items = [];
232
+
233
+ if (this.tocs[node.toc.id]) {
234
+ if (!this.tocs[node.toc.id].toc._pseudo) {
235
+ throw "TOC '" + node.toc.id + "' already exists";
236
+ }
237
+
238
+ node.toc._items = this.tocs[node.toc.id].toc._items;
239
+ }
240
+
241
+ this.tocs[node.toc.id] = node;
242
+
243
+ return node;
244
+ };
245
+
246
+ DocPreprocessor.prototype.preprocessImage = function (node) {
247
+ if (!isUndefined(node.image.type) && !isUndefined(node.image.data) && (node.image.type === 'Buffer') && isArray(node.image.data)) {
248
+ node.image = Buffer.from(node.image.data);
249
+ }
250
+ return node;
251
+ };
252
+
253
+ DocPreprocessor.prototype.preprocessSVG = function (node) {
254
+ return node;
255
+ };
256
+
257
+ DocPreprocessor.prototype.preprocessCanvas = function (node) {
258
+ return node;
259
+ };
260
+
261
+ DocPreprocessor.prototype.preprocessQr = function (node) {
262
+ return node;
263
+ };
264
+
265
+ DocPreprocessor.prototype._getNodeForNodeRef = function (node) {
266
+ if (this.parentNode) {
267
+ return this.parentNode;
268
+ }
269
+
270
+ return node;
271
+ };
272
+
273
+ module.exports = DocPreprocessor;
@@ -0,0 +1,340 @@
1
+ 'use strict';
2
+
3
+ var TraversalTracker = require('./traversalTracker');
4
+ var isString = require('./helpers').isString;
5
+
6
+ /**
7
+ * Creates an instance of DocumentContext - a store for current x, y positions and available width/height.
8
+ * It facilitates column divisions and vertical sync
9
+ */
10
+ function DocumentContext(pageSize, pageMargins) {
11
+ this.pages = [];
12
+
13
+ this.pageMargins = pageMargins;
14
+
15
+ this.x = pageMargins.left;
16
+ this.availableWidth = pageSize.width - pageMargins.left - pageMargins.right;
17
+ this.availableHeight = 0;
18
+ this.page = -1;
19
+
20
+ this.snapshots = [];
21
+
22
+ this.tracker = new TraversalTracker();
23
+
24
+ this.backgroundLength = [];
25
+
26
+ this.addPage(pageSize);
27
+ }
28
+
29
+ DocumentContext.prototype.beginColumnGroup = function (marginXTopParent, bottomByPage = {}) {
30
+ this.snapshots.push({
31
+ x: this.x,
32
+ y: this.y,
33
+ availableHeight: this.availableHeight,
34
+ availableWidth: this.availableWidth,
35
+ page: this.page,
36
+ bottomByPage: bottomByPage ? bottomByPage : {},
37
+ bottomMost: {
38
+ x: this.x,
39
+ y: this.y,
40
+ availableHeight: this.availableHeight,
41
+ availableWidth: this.availableWidth,
42
+ page: this.page
43
+ },
44
+ lastColumnWidth: this.lastColumnWidth
45
+ });
46
+
47
+ this.lastColumnWidth = 0;
48
+ if (marginXTopParent) {
49
+ this.marginXTopParent = marginXTopParent;
50
+ }
51
+ };
52
+
53
+ DocumentContext.prototype.updateBottomByPage = function () {
54
+ const lastSnapshot = this.snapshots[this.snapshots.length - 1];
55
+ const lastPage = this.page;
56
+ let previousBottom = -Number.MIN_VALUE;
57
+ if (lastSnapshot.bottomByPage[lastPage]) {
58
+ previousBottom = lastSnapshot.bottomByPage[lastPage];
59
+ }
60
+ lastSnapshot.bottomByPage[lastPage] = Math.max(previousBottom, this.y);
61
+ };
62
+
63
+ DocumentContext.prototype.resetMarginXTopParent = function () {
64
+ this.marginXTopParent = null;
65
+ };
66
+
67
+ DocumentContext.prototype.beginColumn = function (width, offset, endingCell) {
68
+ var saved = this.snapshots[this.snapshots.length - 1];
69
+
70
+ this.calculateBottomMost(saved, endingCell);
71
+
72
+ this.page = saved.page;
73
+ this.x = this.x + this.lastColumnWidth + (offset || 0);
74
+ this.y = saved.y;
75
+ this.availableWidth = width; //saved.availableWidth - offset;
76
+ this.availableHeight = saved.availableHeight;
77
+
78
+ this.lastColumnWidth = width;
79
+ };
80
+
81
+ DocumentContext.prototype.calculateBottomMost = function (destContext, endingCell) {
82
+ if (endingCell) {
83
+ this.saveContextInEndingCell(endingCell);
84
+ } else {
85
+ destContext.bottomMost = bottomMostContext(this, destContext.bottomMost);
86
+ }
87
+ };
88
+
89
+ DocumentContext.prototype.markEnding = function (endingCell, originalXOffset, discountY) {
90
+ this.page = endingCell._columnEndingContext.page;
91
+ this.x = endingCell._columnEndingContext.x + originalXOffset;
92
+ this.y = endingCell._columnEndingContext.y - discountY;
93
+ this.availableWidth = endingCell._columnEndingContext.availableWidth;
94
+ this.availableHeight = endingCell._columnEndingContext.availableHeight;
95
+ this.lastColumnWidth = endingCell._columnEndingContext.lastColumnWidth;
96
+ };
97
+
98
+ DocumentContext.prototype.saveContextInEndingCell = function (endingCell) {
99
+ endingCell._columnEndingContext = {
100
+ page: this.page,
101
+ x: this.x,
102
+ y: this.y,
103
+ availableHeight: this.availableHeight,
104
+ availableWidth: this.availableWidth,
105
+ lastColumnWidth: this.lastColumnWidth
106
+ };
107
+ };
108
+
109
+ DocumentContext.prototype.completeColumnGroup = function (height, endingCell) {
110
+ var saved = this.snapshots.pop();
111
+
112
+ this.calculateBottomMost(saved, endingCell);
113
+
114
+ this.x = saved.x;
115
+
116
+ var y = saved.bottomMost.y;
117
+ if (height) {
118
+ if (saved.page === saved.bottomMost.page) {
119
+ if ((saved.y + height) > y) {
120
+ y = saved.y + height;
121
+ }
122
+ } else {
123
+ y += height;
124
+ }
125
+ }
126
+
127
+ this.y = y;
128
+ this.page = saved.bottomMost.page;
129
+ this.availableWidth = saved.availableWidth;
130
+ this.availableHeight = saved.bottomMost.availableHeight;
131
+ if (height) {
132
+ this.availableHeight -= (y - saved.bottomMost.y);
133
+ }
134
+ this.lastColumnWidth = saved.lastColumnWidth;
135
+ return saved.bottomByPage;
136
+ };
137
+
138
+ DocumentContext.prototype.addMargin = function (left, right) {
139
+ this.x += left;
140
+ this.availableWidth -= left + (right || 0);
141
+ };
142
+
143
+ DocumentContext.prototype.moveDown = function (offset) {
144
+ this.y += offset;
145
+ this.availableHeight -= offset;
146
+
147
+ return this.availableHeight > 0;
148
+ };
149
+
150
+ DocumentContext.prototype.initializePage = function () {
151
+ this.y = this.pageMargins.top;
152
+ this.availableHeight = this.getCurrentPage().pageSize.height - this.pageMargins.top - this.pageMargins.bottom;
153
+ const { pageCtx, isSnapshot } = this.pageSnapshot();
154
+ pageCtx.availableWidth = this.getCurrentPage().pageSize.width - this.pageMargins.left - this.pageMargins.right;
155
+ if (isSnapshot && this.marginXTopParent) {
156
+ pageCtx.availableWidth -= this.marginXTopParent[0];
157
+ pageCtx.availableWidth -= this.marginXTopParent[1];
158
+ }
159
+ };
160
+
161
+ DocumentContext.prototype.pageSnapshot = function () {
162
+ if (this.snapshots[0]) {
163
+ return { pageCtx: this.snapshots[0], isSnapshot: true };
164
+ } else {
165
+ return { pageCtx: this, isSnapshot: false };
166
+ }
167
+ };
168
+
169
+ DocumentContext.prototype.moveTo = function (x, y) {
170
+ if (x !== undefined && x !== null) {
171
+ this.x = x;
172
+ this.availableWidth = this.getCurrentPage().pageSize.width - this.x - this.pageMargins.right;
173
+ }
174
+ if (y !== undefined && y !== null) {
175
+ this.y = y;
176
+ this.availableHeight = this.getCurrentPage().pageSize.height - this.y - this.pageMargins.bottom;
177
+ }
178
+ };
179
+
180
+ DocumentContext.prototype.moveToRelative = function (x, y) {
181
+ if (x !== undefined && x !== null) {
182
+ this.x = this.x + x;
183
+ }
184
+ if (y !== undefined && y !== null) {
185
+ this.y = this.y + y;
186
+ }
187
+ };
188
+
189
+ DocumentContext.prototype.beginDetachedBlock = function () {
190
+ this.snapshots.push({
191
+ x: this.x,
192
+ y: this.y,
193
+ availableHeight: this.availableHeight,
194
+ availableWidth: this.availableWidth,
195
+ page: this.page,
196
+ lastColumnWidth: this.lastColumnWidth
197
+ });
198
+ };
199
+
200
+ DocumentContext.prototype.endDetachedBlock = function () {
201
+ var saved = this.snapshots.pop();
202
+
203
+ this.x = saved.x;
204
+ this.y = saved.y;
205
+ this.availableWidth = saved.availableWidth;
206
+ this.availableHeight = saved.availableHeight;
207
+ this.page = saved.page;
208
+ this.lastColumnWidth = saved.lastColumnWidth;
209
+ };
210
+
211
+ function pageOrientation(pageOrientationString, currentPageOrientation) {
212
+ if (pageOrientationString === undefined) {
213
+ return currentPageOrientation;
214
+ } else if (isString(pageOrientationString) && (pageOrientationString.toLowerCase() === 'landscape')) {
215
+ return 'landscape';
216
+ } else {
217
+ return 'portrait';
218
+ }
219
+ }
220
+
221
+ var getPageSize = function (currentPage, newPageOrientation) {
222
+
223
+ newPageOrientation = pageOrientation(newPageOrientation, currentPage.pageSize.orientation);
224
+
225
+ if (newPageOrientation !== currentPage.pageSize.orientation) {
226
+ return {
227
+ orientation: newPageOrientation,
228
+ width: currentPage.pageSize.height,
229
+ height: currentPage.pageSize.width
230
+ };
231
+ } else {
232
+ return {
233
+ orientation: currentPage.pageSize.orientation,
234
+ width: currentPage.pageSize.width,
235
+ height: currentPage.pageSize.height
236
+ };
237
+ }
238
+
239
+ };
240
+
241
+
242
+ DocumentContext.prototype.moveToNextPage = function (pageOrientation) {
243
+ var nextPageIndex = this.page + 1;
244
+
245
+ var prevPage = this.page;
246
+ var prevY = this.y;
247
+
248
+ // If we are in a column group
249
+ if (this.snapshots.length > 0) {
250
+ var lastSnapshot = this.snapshots[this.snapshots.length - 1];
251
+ // We have to update prevY accordingly by also taking into consideration
252
+ // the 'y' of cells that don't break page
253
+ if (lastSnapshot.bottomMost && lastSnapshot.bottomMost.y) {
254
+ prevY = Math.max(this.y, lastSnapshot.bottomMost.y);
255
+ }
256
+ }
257
+
258
+ var createNewPage = nextPageIndex >= this.pages.length;
259
+ if (createNewPage) {
260
+ var currentAvailableWidth = this.availableWidth;
261
+ var currentPageOrientation = this.getCurrentPage().pageSize.orientation;
262
+
263
+ var pageSize = getPageSize(this.getCurrentPage(), pageOrientation);
264
+ this.addPage(pageSize);
265
+
266
+ if (currentPageOrientation === pageSize.orientation) {
267
+ this.availableWidth = currentAvailableWidth;
268
+ }
269
+ } else {
270
+ this.page = nextPageIndex;
271
+ this.initializePage();
272
+ }
273
+
274
+ return {
275
+ newPageCreated: createNewPage,
276
+ prevPage: prevPage,
277
+ prevY: prevY,
278
+ y: this.y
279
+ };
280
+ };
281
+
282
+
283
+ DocumentContext.prototype.addPage = function (pageSize) {
284
+ var page = { items: [], pageSize: pageSize };
285
+ this.pages.push(page);
286
+ this.backgroundLength.push(0);
287
+ this.page = this.pages.length - 1;
288
+ this.initializePage();
289
+
290
+ this.tracker.emit('pageAdded');
291
+
292
+ return page;
293
+ };
294
+
295
+ DocumentContext.prototype.getCurrentPage = function () {
296
+ if (this.page < 0 || this.page >= this.pages.length) {
297
+ return null;
298
+ }
299
+
300
+ return this.pages[this.page];
301
+ };
302
+
303
+ DocumentContext.prototype.getCurrentPosition = function () {
304
+ var pageSize = this.getCurrentPage().pageSize;
305
+ var innerHeight = pageSize.height - this.pageMargins.top - this.pageMargins.bottom;
306
+ var innerWidth = pageSize.width - this.pageMargins.left - this.pageMargins.right;
307
+
308
+ return {
309
+ pageNumber: this.page + 1,
310
+ pageOrientation: pageSize.orientation,
311
+ pageInnerHeight: innerHeight,
312
+ pageInnerWidth: innerWidth,
313
+ left: this.x,
314
+ top: this.y,
315
+ verticalRatio: ((this.y - this.pageMargins.top) / innerHeight),
316
+ horizontalRatio: ((this.x - this.pageMargins.left) / innerWidth)
317
+ };
318
+ };
319
+
320
+ function bottomMostContext(c1, c2) {
321
+ var r;
322
+
323
+ if (c1.page > c2.page) {
324
+ r = c1;
325
+ } else if (c2.page > c1.page) {
326
+ r = c2;
327
+ } else {
328
+ r = (c1.y > c2.y) ? c1 : c2;
329
+ }
330
+
331
+ return {
332
+ page: r.page,
333
+ x: r.x,
334
+ y: r.y,
335
+ availableHeight: r.availableHeight,
336
+ availableWidth: r.availableWidth
337
+ };
338
+ }
339
+
340
+ module.exports = DocumentContext;