@flowaccount/pdfmake 1.0.4-staging.0 → 1.0.4-staging.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/build/pdfmake.js +660 -772
- package/build/pdfmake.min.js +2 -2
- package/build/pdfmake.min.js.map +1 -1
- package/package.json +1 -1
- package/src/pageElementWriter.js +1 -1
- package/src/textTools.js +181 -293
package/package.json
CHANGED
package/src/pageElementWriter.js
CHANGED
package/src/textTools.js
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
|
+
/* jslint node: true */
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
|
-
var isString = require('./helpers').isString;
|
|
4
|
-
var isNumber = require('./helpers').isNumber;
|
|
5
|
-
var isObject = require('./helpers').isObject;
|
|
6
|
-
var isArray = require('./helpers').isArray;
|
|
7
|
-
var isUndefined = require('./helpers').isUndefined;
|
|
8
4
|
var LineBreaker = require('@foliojs-fork/linebreak');
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
const Tokenizer = require('@flowaccount/node-icu-tokenizer');
|
|
6
|
+
const fontkit = require('fontkit');
|
|
11
7
|
|
|
12
8
|
var LEADING = /^(\s)+/g;
|
|
13
9
|
var TRAILING = /(\s)+$/g;
|
|
14
10
|
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
var fontSubstituteCache =
|
|
18
|
-
var defaultFont = '
|
|
19
|
-
|
|
11
|
+
var fontCacheName_new = '';
|
|
12
|
+
var fontCache_new = {};
|
|
13
|
+
var fontSubstituteCache = [];
|
|
14
|
+
var defaultFont = '';
|
|
20
15
|
/**
|
|
21
16
|
* Creates an instance of TextTools - text measurement utility
|
|
22
17
|
*
|
|
@@ -36,10 +31,57 @@ function TextTools(fontProvider) {
|
|
|
36
31
|
* @return {Object} collection of inlines, minWidth, maxWidth
|
|
37
32
|
*/
|
|
38
33
|
TextTools.prototype.buildInlines = function (textArray, styleContextStack) {
|
|
39
|
-
primeFontCaches.call(this, styleContextStack);
|
|
40
34
|
|
|
41
|
-
var measured = measure(this.fontProvider, textArray, styleContextStack);
|
|
42
35
|
|
|
36
|
+
defaultFont = (styleContextStack && styleContextStack.getProperty('font')) || 'Roboto';
|
|
37
|
+
// if(!fontCacheName_new)
|
|
38
|
+
// {
|
|
39
|
+
fontCacheName_new = defaultFont;
|
|
40
|
+
// Only open font files if fonts exist and provider has fonts property
|
|
41
|
+
if (this.fontProvider.fonts && this.fontProvider.fonts[fontCacheName_new] && this.fontProvider.fonts[fontCacheName_new].normal) {
|
|
42
|
+
try {
|
|
43
|
+
fontCache_new = fontkit.openSync(this.fontProvider.fonts[fontCacheName_new].normal);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Font file doesn't exist (likely in tests), provide mock methods
|
|
46
|
+
fontCache_new = {
|
|
47
|
+
glyphsForString: function(text) {
|
|
48
|
+
// Return mock glyphs - all valid (id > 0)
|
|
49
|
+
return text.split('').map(function(char, i) {
|
|
50
|
+
return { id: i + 1, char: char };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//}
|
|
57
|
+
//var fcount = 0;
|
|
58
|
+
if (this.fontProvider.fonts) {
|
|
59
|
+
for(let fontItem in this.fontProvider.fonts) {
|
|
60
|
+
if(fontCacheName_new != fontItem) {
|
|
61
|
+
if(!fontSubstituteCache[fontItem])
|
|
62
|
+
{
|
|
63
|
+
try {
|
|
64
|
+
var fontObj = fontkit.openSync(this.fontProvider.fonts[fontItem].normal);
|
|
65
|
+
fontSubstituteCache[fontItem] = { "Name" : fontItem, "FontObj" : fontObj };
|
|
66
|
+
} catch (e) {
|
|
67
|
+
// Font file doesn't exist (likely in tests), provide mock font object
|
|
68
|
+
fontSubstituteCache[fontItem] = {
|
|
69
|
+
"Name" : fontItem,
|
|
70
|
+
"FontObj" : {
|
|
71
|
+
glyphsForString: function(text) {
|
|
72
|
+
// Return mock glyphs - all valid (id > 0)
|
|
73
|
+
return text.split('').map(function(char, i) {
|
|
74
|
+
return { id: i + 1, char: char };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
var measured = measure(this.fontProvider, textArray, styleContextStack);
|
|
43
85
|
var minWidth = 0,
|
|
44
86
|
maxWidth = 0,
|
|
45
87
|
currentLineWidth;
|
|
@@ -48,7 +90,7 @@ TextTools.prototype.buildInlines = function (textArray, styleContextStack) {
|
|
|
48
90
|
minWidth = Math.max(minWidth, inline.width - inline.leadingCut - inline.trailingCut);
|
|
49
91
|
|
|
50
92
|
if (!currentLineWidth) {
|
|
51
|
-
currentLineWidth = {
|
|
93
|
+
currentLineWidth = {width: 0, leadingCut: inline.leadingCut, trailingCut: 0};
|
|
52
94
|
}
|
|
53
95
|
|
|
54
96
|
currentLineWidth.width += inline.width;
|
|
@@ -83,12 +125,11 @@ TextTools.prototype.buildInlines = function (textArray, styleContextStack) {
|
|
|
83
125
|
* @return {Object} size of the specified string
|
|
84
126
|
*/
|
|
85
127
|
TextTools.prototype.sizeOfString = function (text, styleContextStack) {
|
|
86
|
-
text = text ? text.toString().replace(
|
|
128
|
+
text = text ? text.toString().replace('\t', ' ') : '';
|
|
87
129
|
|
|
88
130
|
//TODO: refactor - extract from measure
|
|
89
131
|
var fontName = getStyleProperty({}, styleContextStack, 'font', 'Roboto');
|
|
90
132
|
var fontSize = getStyleProperty({}, styleContextStack, 'fontSize', 12);
|
|
91
|
-
var fontFeatures = getStyleProperty({}, styleContextStack, 'fontFeatures', null);
|
|
92
133
|
var bold = getStyleProperty({}, styleContextStack, 'bold', false);
|
|
93
134
|
var italics = getStyleProperty({}, styleContextStack, 'italics', false);
|
|
94
135
|
var lineHeight = getStyleProperty({}, styleContextStack, 'lineHeight', 1);
|
|
@@ -97,7 +138,7 @@ TextTools.prototype.sizeOfString = function (text, styleContextStack) {
|
|
|
97
138
|
var font = this.fontProvider.provideFont(fontName, bold, italics);
|
|
98
139
|
|
|
99
140
|
return {
|
|
100
|
-
width: widthOfString(text, font, fontSize, characterSpacing
|
|
141
|
+
width: widthOfString(text, font, fontSize, characterSpacing),
|
|
101
142
|
height: font.lineHeight(fontSize) * lineHeight,
|
|
102
143
|
fontSize: fontSize,
|
|
103
144
|
lineHeight: lineHeight,
|
|
@@ -106,26 +147,31 @@ TextTools.prototype.sizeOfString = function (text, styleContextStack) {
|
|
|
106
147
|
};
|
|
107
148
|
};
|
|
108
149
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
*
|
|
112
|
-
* @param {string} text text to be measured
|
|
113
|
-
* @param {number} angle
|
|
114
|
-
* @param {object} styleContextStack current style stack
|
|
115
|
-
* @returns {object} size of the specified string
|
|
116
|
-
*/
|
|
117
|
-
TextTools.prototype.sizeOfRotatedText = function (text, angle, styleContextStack) {
|
|
118
|
-
var angleRad = angle * Math.PI / -180;
|
|
119
|
-
var size = this.sizeOfString(text, styleContextStack);
|
|
120
|
-
return {
|
|
121
|
-
width: Math.abs(size.height * Math.sin(angleRad)) + Math.abs(size.width * Math.cos(angleRad)),
|
|
122
|
-
height: Math.abs(size.width * Math.sin(angleRad)) + Math.abs(size.height * Math.cos(angleRad))
|
|
123
|
-
};
|
|
150
|
+
TextTools.prototype.widthOfString = function (text, font, fontSize, characterSpacing) {
|
|
151
|
+
return widthOfString(text, font, fontSize, characterSpacing);
|
|
124
152
|
};
|
|
125
153
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
154
|
+
function tokenizerWords(word) {
|
|
155
|
+
|
|
156
|
+
var regex = / /;
|
|
157
|
+
word = word.replace(regex, "[BLANK]");
|
|
158
|
+
|
|
159
|
+
var tokenizer = new Tokenizer().tokenize(word, { ignoreWhitespaceTokens:false });
|
|
160
|
+
for(var tItem in tokenizer){
|
|
161
|
+
if(tItem < tokenizer.length){
|
|
162
|
+
var numIndex = parseInt(tItem);
|
|
163
|
+
if(tokenizer[numIndex].token == '[' && tokenizer[numIndex + 1].token == 'BLANK' && tokenizer[numIndex + 2].token == ']') {
|
|
164
|
+
tokenizer[numIndex].token = ' ';
|
|
165
|
+
tokenizer[numIndex + 1].del = true;
|
|
166
|
+
tokenizer[numIndex + 2].del = true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
} else {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return tokenizer;
|
|
174
|
+
}
|
|
129
175
|
|
|
130
176
|
function splitWords(text, noWrap) {
|
|
131
177
|
var results = [];
|
|
@@ -133,86 +179,58 @@ function splitWords(text, noWrap) {
|
|
|
133
179
|
text = text.replace(/\t/g, ' ');
|
|
134
180
|
text = text.replace(/\r/g, '');
|
|
135
181
|
|
|
136
|
-
// Remove
|
|
137
|
-
|
|
138
|
-
// See CLAUDE.md "Hidden Unicode Characters & Font Rendering Issues" for full list
|
|
139
|
-
text = text.replace(/[\u200B-\u200D\uFEFF\u200E\u200F\u202A-\u202E\u2028\u2029\u180E\u2060-\u2064\u206A-\u206F]/g, '');
|
|
182
|
+
// Remove Hidden Unicode
|
|
183
|
+
text = text.replace(/[\u200D\uFEFF\u200E\u200F\u202A-\u202E\u2028\u2029\u180E\u2060-\u2064\u206A-\u206F]/g, '');
|
|
140
184
|
|
|
141
185
|
if (noWrap) {
|
|
142
|
-
results.push({
|
|
186
|
+
results.push({text: text});
|
|
143
187
|
return results;
|
|
144
|
-
}
|
|
188
|
+
}
|
|
145
189
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
190
|
+
var breaker = new LineBreaker(text);
|
|
191
|
+
var last = 0;
|
|
192
|
+
var bk;
|
|
149
193
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
var isLineEnd = bk.required || /\r?\n$|\r$/.test(word);
|
|
153
|
-
var trimmed = word.trim();
|
|
154
|
-
var useTokenizer = !exceptTokenizer(trimmed) && needsTokenizer(trimmed);
|
|
194
|
+
while (bk = breaker.nextBreak()) {
|
|
195
|
+
var word = text.slice(last, bk.position);
|
|
155
196
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
output = output.replace(/\r?\n$|\r$/, '');
|
|
160
|
-
}
|
|
161
|
-
results.push({ text: output, lineEnd: isLineEnd });
|
|
162
|
-
} else {
|
|
163
|
-
var tokenWord = tokenizerWords(word);
|
|
164
|
-
for (var tIndex = 0; tIndex < tokenWord.length; tIndex++) {
|
|
165
|
-
var token = tokenWord[tIndex];
|
|
166
|
-
if (!token || token.del) {
|
|
167
|
-
continue;
|
|
197
|
+
if(exceptTokenizer(word.trim()))
|
|
198
|
+
{
|
|
199
|
+
results.push({text: word, lineEnd: (bk.required || word.match(/\r?\n$|\r$/))});
|
|
168
200
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
201
|
+
else if (bk.required || word.match(/\r?\n$|\r$/)) {
|
|
202
|
+
//word = word.replace(/\r?\n$|\r$/, '');
|
|
203
|
+
|
|
204
|
+
var tokenWord = tokenizerWords(word);
|
|
205
|
+
for (var tIndex in tokenWord) {
|
|
206
|
+
if (!tokenWord[tIndex].del) {
|
|
207
|
+
if (tIndex < tokenWord.length - 1 && tokenWord[tIndex].token != '\n') {
|
|
208
|
+
results.push({ text: tokenWord[tIndex].token });
|
|
209
|
+
} else {
|
|
210
|
+
results.push({ text: tokenWord[tIndex].token, lineEnd: true });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
var tokenWord = tokenizerWords(word);
|
|
216
|
+
for (var tIndex in tokenWord) {
|
|
217
|
+
if (!tokenWord[tIndex].del) {
|
|
218
|
+
results.push({ text: tokenWord[tIndex].token });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
179
221
|
}
|
|
180
222
|
|
|
181
|
-
|
|
223
|
+
last = bk.position;
|
|
182
224
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
last = bk.position;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
225
|
return results;
|
|
189
226
|
}
|
|
190
227
|
|
|
191
|
-
function tokenizerWords(word) {
|
|
192
|
-
var regex = / /;
|
|
193
|
-
word = word.replace(regex, '[BLANK]');
|
|
194
|
-
|
|
195
|
-
var tokenizer = new Tokenizer().tokenize(word, { ignoreWhitespaceTokens: false });
|
|
196
|
-
|
|
197
|
-
for (var i = 0; i < tokenizer.length; i++) {
|
|
198
|
-
if (tokenizer[i] && tokenizer[i].token === '[' &&
|
|
199
|
-
tokenizer[i + 1] && tokenizer[i + 1].token === 'BLANK' &&
|
|
200
|
-
tokenizer[i + 2] && tokenizer[i + 2].token === ']') {
|
|
201
|
-
tokenizer[i].token = ' ';
|
|
202
|
-
tokenizer[i + 1].del = true;
|
|
203
|
-
tokenizer[i + 2].del = true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return tokenizer;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
228
|
function copyStyle(source, destination) {
|
|
211
229
|
destination = destination || {};
|
|
212
230
|
source = source || {}; //TODO: default style
|
|
213
231
|
|
|
214
232
|
for (var key in source) {
|
|
215
|
-
if (key
|
|
233
|
+
if (key != 'text' && source.hasOwnProperty(key)) {
|
|
216
234
|
destination[key] = source[key];
|
|
217
235
|
}
|
|
218
236
|
}
|
|
@@ -221,69 +239,26 @@ function copyStyle(source, destination) {
|
|
|
221
239
|
}
|
|
222
240
|
|
|
223
241
|
function normalizeTextArray(array, styleContextStack) {
|
|
224
|
-
function flatten(target) {
|
|
225
|
-
return target.reduce(function (prev, cur) {
|
|
226
|
-
var current = isObject(cur) && isArray(cur.text) ? flatten(cur.text) : cur;
|
|
227
|
-
var more = [].concat(current).some(Array.isArray);
|
|
228
|
-
return prev.concat(more ? flatten(current) : current);
|
|
229
|
-
}, []);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function getOneWord(index, words, noWrap) {
|
|
233
|
-
if (isUndefined(words[index])) {
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (words[index].lineEnd) {
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
var word = words[index].text;
|
|
242
|
-
|
|
243
|
-
if (noWrap) {
|
|
244
|
-
var tmpWords = splitWords(normalizeString(word), false);
|
|
245
|
-
if (isUndefined(tmpWords[tmpWords.length - 1])) {
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
word = tmpWords[tmpWords.length - 1].text;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return word;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
242
|
var results = [];
|
|
255
243
|
|
|
256
|
-
if (!isArray(array)) {
|
|
244
|
+
if (!Array.isArray(array)) {
|
|
257
245
|
array = [array];
|
|
258
246
|
}
|
|
259
247
|
|
|
260
|
-
array = flatten(array);
|
|
261
|
-
|
|
262
|
-
var lastWord = null;
|
|
263
248
|
for (var i = 0, l = array.length; i < l; i++) {
|
|
264
249
|
var item = array[i];
|
|
265
250
|
var style = null;
|
|
266
251
|
var words;
|
|
267
252
|
|
|
268
253
|
var noWrap = getStyleProperty(item || {}, styleContextStack, 'noWrap', false);
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
item.text = item._textRef._textNodeRef.text;
|
|
272
|
-
}
|
|
254
|
+
|
|
255
|
+
if (item !== null && (typeof item === 'object' || item instanceof Object)) {
|
|
273
256
|
words = splitWords(normalizeString(item.text), noWrap);
|
|
274
257
|
style = copyStyle(item);
|
|
275
258
|
} else {
|
|
276
259
|
words = splitWords(normalizeString(item), noWrap);
|
|
277
260
|
}
|
|
278
261
|
|
|
279
|
-
if (lastWord && words.length) {
|
|
280
|
-
var firstWord = getOneWord(0, words, noWrap);
|
|
281
|
-
var wrapWords = splitWords(normalizeString(lastWord + firstWord), false);
|
|
282
|
-
if (wrapWords.length === 1) {
|
|
283
|
-
results[results.length - 1].noNewLine = true;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
262
|
for (var i2 = 0, l2 = words.length; i2 < l2; i2++) {
|
|
288
263
|
var result = {
|
|
289
264
|
text: words[i2].text
|
|
@@ -293,32 +268,29 @@ function normalizeTextArray(array, styleContextStack) {
|
|
|
293
268
|
result.lineEnd = true;
|
|
294
269
|
}
|
|
295
270
|
|
|
296
|
-
// Preserve leading spaces for words that come after a line break (not the first word)
|
|
297
|
-
// This fixes the issue where spaces at the beginning of new lines are removed
|
|
298
|
-
if (i2 > 0 && words[i2 - 1].lineEnd) {
|
|
299
|
-
result.preserveLeadingSpaces = true;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
271
|
copyStyle(style, result);
|
|
303
272
|
results.push(result);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
lastWord = null;
|
|
307
|
-
if (i + 1 < l) {
|
|
308
|
-
lastWord = getOneWord(words.length - 1, words, noWrap);
|
|
309
|
-
}
|
|
273
|
+
}
|
|
310
274
|
}
|
|
311
|
-
|
|
312
275
|
return results;
|
|
313
276
|
}
|
|
314
277
|
|
|
315
278
|
function normalizeString(value) {
|
|
316
279
|
if (value === undefined || value === null) {
|
|
317
280
|
return '';
|
|
318
|
-
} else if (
|
|
281
|
+
} else if (typeof value === 'number') {
|
|
319
282
|
return value.toString();
|
|
320
|
-
} else if (
|
|
283
|
+
} else if (typeof value === 'string' || value instanceof String) {
|
|
321
284
|
return value;
|
|
285
|
+
} else if (Array.isArray(value)) {
|
|
286
|
+
// Handle arrays - extract text from first element if it exists
|
|
287
|
+
if (value.length > 0) {
|
|
288
|
+
return normalizeString(value[0]);
|
|
289
|
+
}
|
|
290
|
+
return '';
|
|
291
|
+
} else if (typeof value === 'object' && value.text !== undefined) {
|
|
292
|
+
// Recursively extract nested text property (handles { text: { text: 'hello' } })
|
|
293
|
+
return normalizeString(value.text);
|
|
322
294
|
} else {
|
|
323
295
|
return value.toString();
|
|
324
296
|
}
|
|
@@ -327,18 +299,22 @@ function normalizeString(value) {
|
|
|
327
299
|
function getStyleProperty(item, styleContextStack, property, defaultValue) {
|
|
328
300
|
var value;
|
|
329
301
|
|
|
330
|
-
if (item
|
|
302
|
+
if (item[property] !== undefined && item[property] !== null) {
|
|
331
303
|
// item defines this property
|
|
332
304
|
return item[property];
|
|
333
305
|
}
|
|
334
306
|
|
|
335
|
-
if (!styleContextStack
|
|
307
|
+
if (!styleContextStack) {
|
|
336
308
|
return defaultValue;
|
|
337
309
|
}
|
|
338
310
|
|
|
339
|
-
styleContextStack.auto
|
|
311
|
+
if (styleContextStack.auto && typeof styleContextStack.auto === 'function') {
|
|
312
|
+
styleContextStack.auto(item, function () {
|
|
313
|
+
value = styleContextStack.getProperty(property);
|
|
314
|
+
});
|
|
315
|
+
} else if (styleContextStack.getProperty && typeof styleContextStack.getProperty === 'function') {
|
|
340
316
|
value = styleContextStack.getProperty(property);
|
|
341
|
-
}
|
|
317
|
+
}
|
|
342
318
|
|
|
343
319
|
if (value !== null && value !== undefined) {
|
|
344
320
|
return value;
|
|
@@ -347,9 +323,38 @@ function getStyleProperty(item, styleContextStack, property, defaultValue) {
|
|
|
347
323
|
}
|
|
348
324
|
}
|
|
349
325
|
|
|
326
|
+
function getFontCompaitible(item, fontProvider, styleContextStack) {
|
|
327
|
+
// Guard against missing or non-string text
|
|
328
|
+
if (!item.text || typeof item.text !== 'string') {
|
|
329
|
+
return defaultFont;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if(fontCacheName_new && fontCache_new && typeof fontCache_new.glyphsForString === 'function') {
|
|
333
|
+
var glyphList = fontCache_new.glyphsForString(item.text);
|
|
334
|
+
if(glyphList.filter(function(x) {return x.id <= 0;}).length == 0) {
|
|
335
|
+
return fontCacheName_new;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
for(let count in fontSubstituteCache) {
|
|
339
|
+
var fontItem = fontSubstituteCache[count];
|
|
340
|
+
if(fontCacheName_new != fontItem.Name) {
|
|
341
|
+
var glyphList = fontItem.FontObj.glyphsForString(item.text);
|
|
342
|
+
if(glyphList.filter(function(x) {return x.id <= 0;}).length == 0) {
|
|
343
|
+
return fontItem.Name;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return defaultFont;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
350
|
function measure(fontProvider, textArray, styleContextStack) {
|
|
351
351
|
var normalized = normalizeTextArray(textArray, styleContextStack);
|
|
352
352
|
|
|
353
|
+
// Filter out items without valid text (empty objects, etc.)
|
|
354
|
+
normalized = normalized.filter(function(item) {
|
|
355
|
+
return item.text && typeof item.text === 'string';
|
|
356
|
+
});
|
|
357
|
+
|
|
353
358
|
if (normalized.length) {
|
|
354
359
|
var leadingIndent = getStyleProperty(normalized[0], styleContextStack, 'leadingIndent', 0);
|
|
355
360
|
|
|
@@ -358,11 +363,10 @@ function measure(fontProvider, textArray, styleContextStack) {
|
|
|
358
363
|
normalized[0].leadingIndent = leadingIndent;
|
|
359
364
|
}
|
|
360
365
|
}
|
|
361
|
-
|
|
362
366
|
normalized.forEach(function (item) {
|
|
363
|
-
|
|
367
|
+
|
|
368
|
+
var fontName = getFontCompaitible(item, fontProvider, styleContextStack);//getStyleProperty(item, styleContextStack, 'font', 'Roboto');
|
|
364
369
|
var fontSize = getStyleProperty(item, styleContextStack, 'fontSize', 12);
|
|
365
|
-
var fontFeatures = getStyleProperty(item, styleContextStack, 'fontFeatures', null);
|
|
366
370
|
var bold = getStyleProperty(item, styleContextStack, 'bold', false);
|
|
367
371
|
var italics = getStyleProperty(item, styleContextStack, 'italics', false);
|
|
368
372
|
var color = getStyleProperty(item, styleContextStack, 'color', 'black');
|
|
@@ -374,44 +378,34 @@ function measure(fontProvider, textArray, styleContextStack) {
|
|
|
374
378
|
var characterSpacing = getStyleProperty(item, styleContextStack, 'characterSpacing', 0);
|
|
375
379
|
var link = getStyleProperty(item, styleContextStack, 'link', null);
|
|
376
380
|
var linkToPage = getStyleProperty(item, styleContextStack, 'linkToPage', null);
|
|
377
|
-
var linkToDestination = getStyleProperty(item, styleContextStack, 'linkToDestination', null);
|
|
378
381
|
var noWrap = getStyleProperty(item, styleContextStack, 'noWrap', null);
|
|
379
382
|
var preserveLeadingSpaces = getStyleProperty(item, styleContextStack, 'preserveLeadingSpaces', false);
|
|
380
|
-
var preserveTrailingSpaces = getStyleProperty(item, styleContextStack, 'preserveTrailingSpaces', false);
|
|
381
|
-
var opacity = getStyleProperty(item, styleContextStack, 'opacity', 1);
|
|
382
|
-
var sup = getStyleProperty(item, styleContextStack, 'sup', false);
|
|
383
|
-
var sub = getStyleProperty(item, styleContextStack, 'sub', false);
|
|
384
|
-
|
|
385
|
-
if ((sup || sub) && item.fontSize === undefined) {
|
|
386
|
-
// font size reduction taken from here: https://en.wikipedia.org/wiki/Subscript_and_superscript#Desktop_publishing
|
|
387
|
-
fontSize *= 0.58;
|
|
388
|
-
}
|
|
389
383
|
|
|
390
384
|
var font = fontProvider.provideFont(fontName, bold, italics);
|
|
391
385
|
|
|
392
|
-
item.width = widthOfString(item.text, font, fontSize, characterSpacing
|
|
386
|
+
item.width = widthOfString(item.text, font, fontSize, characterSpacing);
|
|
393
387
|
item.height = font.lineHeight(fontSize) * lineHeight;
|
|
394
388
|
|
|
389
|
+
var leadingSpaces = item.text.match(LEADING);
|
|
390
|
+
|
|
395
391
|
if (!item.leadingCut) {
|
|
396
392
|
item.leadingCut = 0;
|
|
397
393
|
}
|
|
398
394
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
item.leadingCut += widthOfString(leadingSpaces[0], font, fontSize, characterSpacing, fontFeatures);
|
|
395
|
+
if (leadingSpaces && !preserveLeadingSpaces) {
|
|
396
|
+
item.leadingCut += widthOfString(leadingSpaces[0], font, fontSize, characterSpacing);
|
|
402
397
|
}
|
|
403
398
|
|
|
404
|
-
var trailingSpaces;
|
|
405
|
-
if (
|
|
406
|
-
item.trailingCut = widthOfString(trailingSpaces[0], font, fontSize, characterSpacing
|
|
407
|
-
} else
|
|
399
|
+
var trailingSpaces = item.text.match(TRAILING);
|
|
400
|
+
if (trailingSpaces) {
|
|
401
|
+
item.trailingCut = widthOfString(trailingSpaces[0], font, fontSize, characterSpacing);
|
|
402
|
+
} else {
|
|
408
403
|
item.trailingCut = 0;
|
|
409
404
|
}
|
|
410
405
|
|
|
411
406
|
item.alignment = getStyleProperty(item, styleContextStack, 'alignment', 'left');
|
|
412
407
|
item.font = font;
|
|
413
408
|
item.fontSize = fontSize;
|
|
414
|
-
item.fontFeatures = fontFeatures;
|
|
415
409
|
item.characterSpacing = characterSpacing;
|
|
416
410
|
item.color = color;
|
|
417
411
|
item.decoration = decoration;
|
|
@@ -420,18 +414,14 @@ function measure(fontProvider, textArray, styleContextStack) {
|
|
|
420
414
|
item.background = background;
|
|
421
415
|
item.link = link;
|
|
422
416
|
item.linkToPage = linkToPage;
|
|
423
|
-
item.linkToDestination = linkToDestination;
|
|
424
417
|
item.noWrap = noWrap;
|
|
425
|
-
item.opacity = opacity;
|
|
426
|
-
item.sup = sup;
|
|
427
|
-
item.sub = sub;
|
|
428
418
|
});
|
|
429
419
|
|
|
430
420
|
return normalized;
|
|
431
421
|
}
|
|
432
422
|
|
|
433
|
-
function widthOfString(text, font, fontSize, characterSpacing
|
|
434
|
-
return font.widthOfString(text, fontSize
|
|
423
|
+
function widthOfString(text, font, fontSize, characterSpacing) {
|
|
424
|
+
return font.widthOfString(text, fontSize) + ((characterSpacing || 0) * (text.length - 1));
|
|
435
425
|
}
|
|
436
426
|
|
|
437
427
|
function exceptTokenizer(word) {
|
|
@@ -449,106 +439,4 @@ function exceptTokenizer(word) {
|
|
|
449
439
|
return listExceptWord.indexOf(word) > -1;
|
|
450
440
|
}
|
|
451
441
|
|
|
452
|
-
function needsTokenizer(word) {
|
|
453
|
-
if (!word) {
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return /[\u0E00-\u0E7F]/.test(word);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function primeFontCaches(styleContextStack) {
|
|
461
|
-
if (styleContextStack && typeof styleContextStack.getProperty === 'function') {
|
|
462
|
-
var stackFont = styleContextStack.getProperty('font');
|
|
463
|
-
if (stackFont) {
|
|
464
|
-
defaultFont = stackFont;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (!defaultFont) {
|
|
469
|
-
defaultFont = 'Roboto';
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (!this.fontProvider || !this.fontProvider.fonts) {
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (defaultFont && fontCacheName !== defaultFont) {
|
|
477
|
-
var defaultFontDef = this.fontProvider.fonts[defaultFont];
|
|
478
|
-
var primaryFont = safeOpenFont(defaultFontDef && defaultFontDef.normal);
|
|
479
|
-
|
|
480
|
-
if (primaryFont) {
|
|
481
|
-
fontCacheName = defaultFont;
|
|
482
|
-
fontCache = primaryFont;
|
|
483
|
-
} else {
|
|
484
|
-
fontCacheName = '';
|
|
485
|
-
fontCache = null;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
for (var fontItem in this.fontProvider.fonts) {
|
|
490
|
-
if (!Object.prototype.hasOwnProperty.call(this.fontProvider.fonts, fontItem)) {
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (fontItem === fontCacheName) {
|
|
495
|
-
continue;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (!fontSubstituteCache[fontItem]) {
|
|
499
|
-
var substitute = safeOpenFont(this.fontProvider.fonts[fontItem].normal);
|
|
500
|
-
if (substitute) {
|
|
501
|
-
fontSubstituteCache[fontItem] = { Name: fontItem, FontObj: substitute };
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function getFontCompaitible(item) {
|
|
508
|
-
var text = item.text || '';
|
|
509
|
-
|
|
510
|
-
if (fontCacheName && fontCache && !hasMissingGlyphs(fontCache.glyphsForString(text))) {
|
|
511
|
-
return fontCacheName;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
for (var key in fontSubstituteCache) {
|
|
515
|
-
if (!Object.prototype.hasOwnProperty.call(fontSubstituteCache, key)) {
|
|
516
|
-
continue;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
var fontItem = fontSubstituteCache[key];
|
|
520
|
-
if (!fontItem || !fontItem.FontObj || fontItem.Name === fontCacheName) {
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (!hasMissingGlyphs(fontItem.FontObj.glyphsForString(text))) {
|
|
525
|
-
return fontItem.Name;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
return defaultFont || 'Roboto';
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
function hasMissingGlyphs(glyphList) {
|
|
533
|
-
for (var i = 0; i < glyphList.length; i++) {
|
|
534
|
-
if (!glyphList[i] || glyphList[i].id <= 0) {
|
|
535
|
-
return true;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
return false;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function safeOpenFont(path) {
|
|
542
|
-
if (!path) {
|
|
543
|
-
return null;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
try {
|
|
547
|
-
return fontkit.openSync(path);
|
|
548
|
-
} catch (error) {
|
|
549
|
-
void error;
|
|
550
|
-
return null;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
442
|
module.exports = TextTools;
|