pdfmake 0.3.0 → 0.3.1

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/js/TextInlines.js CHANGED
@@ -58,8 +58,8 @@ class TextInlines {
58
58
  let currentLineWidth;
59
59
  let flattenedTextArray = flattenTextArray(textArray);
60
60
  const textBreaker = new _TextBreaker.default();
61
- let breakedText = textBreaker.getBreaks(flattenedTextArray, styleContextStack);
62
- let measuredText = this.measure(breakedText, styleContextStack);
61
+ let brokenText = textBreaker.getBreaks(flattenedTextArray, styleContextStack);
62
+ let measuredText = this.measure(brokenText, styleContextStack);
63
63
  measuredText.forEach(inline => {
64
64
  minWidth = Math.max(minWidth, getTrimmedWidth(inline));
65
65
  if (!currentLineWidth) {
package/js/base.js CHANGED
@@ -5,6 +5,7 @@ exports.default = void 0;
5
5
  var _Printer = _interopRequireDefault(require("./Printer"));
6
6
  var _virtualFs = _interopRequireDefault(require("./virtual-fs"));
7
7
  var _tools = require("./helpers/tools");
8
+ var _variableType = require("./helpers/variableType");
8
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
10
  class pdfmake {
10
11
  constructor() {
@@ -18,6 +19,12 @@ class pdfmake {
18
19
  * @returns {object}
19
20
  */
20
21
  createPdf(docDefinition, options = {}) {
22
+ if (!(0, _variableType.isObject)(docDefinition)) {
23
+ throw new Error("Parameter 'docDefinition' has an invalid type. Object expected.");
24
+ }
25
+ if (!(0, _variableType.isObject)(options)) {
26
+ throw new Error("Parameter 'options' has an invalid type. Object expected.");
27
+ }
21
28
  options.progressCallback = this.progressCallback;
22
29
  options.tableLayouts = this.tableLayouts;
23
30
  let printer = new _Printer.default(this.fonts, this.virtualfs, this.urlResolver());
package/js/qrEnc.js CHANGED
@@ -98,7 +98,7 @@ var MASKFUNCS = [function (i, j) {
98
98
  return ((i + j) % 2 + i * j % 3) % 2 === 0;
99
99
  }];
100
100
 
101
- // returns true when the version information has to be embeded.
101
+ // returns true when the version information has to be embedded.
102
102
  var needsverinfo = function (ver) {
103
103
  return ver > 6;
104
104
  };
@@ -291,7 +291,7 @@ var encode = function (ver, mode, data, maxbuflen) {
291
291
  //
292
292
  // this is quite similar to CRC calculation as both Reed-Solomon and CRC use
293
293
  // the certain kind of cyclic codes, which is effectively the division of
294
- // zero-augumented polynomial by the generator polynomial. the only difference
294
+ // zero-augmented polynomial by the generator polynomial. the only difference
295
295
  // is that Reed-Solomon uses GF(2^8), instead of CRC's GF(2), and Reed-Solomon
296
296
  // uses the different generator polynomial than CRC's.
297
297
  var calculateecc = function (poly, genpoly) {
@@ -310,10 +310,10 @@ var calculateecc = function (poly, genpoly) {
310
310
  return modulus.slice(polylen);
311
311
  };
312
312
 
313
- // auguments ECC code words to given code words. the resulting words are
313
+ // augments ECC code words to given code words. the resulting words are
314
314
  // ready to be encoded in the matrix.
315
315
  //
316
- // the much of actual augumenting procedure follows JIS X 0510:2004 sec 8.7.
316
+ // the much of actual augmenting procedure follows JIS X 0510:2004 sec 8.7.
317
317
  // the code is simplified using the fact that the size of each code & ECC
318
318
  // blocks is almost same; for example, when we have 4 blocks and 46 data words
319
319
  // the number of code words in those blocks are 11, 11, 12, 12 respectively.
@@ -353,7 +353,7 @@ var augumenteccs = function (poly, nblocks, genpoly) {
353
353
  return result;
354
354
  };
355
355
 
356
- // auguments BCH(p+q,q) code to the polynomial over GF(2), given the proper
356
+ // augments BCH(p+q,q) code to the polynomial over GF(2), given the proper
357
357
  // genpoly. the both input and output are in binary numbers, and unlike
358
358
  // calculateecc genpoly should include the 1 bit for the highest degree.
359
359
  //
@@ -490,7 +490,7 @@ var putformatinfo = function (matrix, reserved, ecclevel, mask) {
490
490
  // (cf. JIS X 0510:2004 sec 8.8.2)
491
491
  //
492
492
  // the evaluation procedure tries to avoid the problematic patterns naturally
493
- // occuring from the original matrix. for example, it penaltizes the patterns
493
+ // occurring from the original matrix. for example, it penaltizes the patterns
494
494
  // which just look like the finder pattern which will confuse the decoder.
495
495
  // we choose the mask which results in the lowest score among 8 possible ones.
496
496
  //
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdfmake",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Client/server side PDF printing in pure JavaScript",
5
5
  "main": "js/index.js",
6
6
  "esnext": "src/index.js",
@@ -39,7 +39,7 @@
39
39
  "source-map-loader": "^5.0.0",
40
40
  "stream-browserify": "^3.0.0",
41
41
  "string-replace-webpack-plugin": "^0.1.3",
42
- "svg-to-pdfkit": "^0.1.8",
42
+ "svg-to-pdfkit": "github:alafr/SVG-to-PDFKit#b091ebd4e7b7d2310eb1003511cd5de480f7e0e1",
43
43
  "terser-webpack-plugin": "^5.3.16",
44
44
  "transform-loader": "^0.2.4",
45
45
  "util": "^0.12.5",
@@ -60,7 +60,8 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
60
60
  'display': {inherit: false, initial: 'inline', values: {'none':'none', 'inline':'inline', 'block':'inline'}},
61
61
  'clip-path': {inherit: false, initial: 'none'},
62
62
  'mask': {inherit: false, initial: 'none'},
63
- 'overflow': {inherit: false, initial: 'hidden', values: {'hidden':'hidden', 'scroll':'hidden', 'visible':'visible'}}
63
+ 'overflow': {inherit: false, initial: 'hidden', values: {'hidden':'hidden', 'scroll':'hidden', 'visible':'visible'}},
64
+ 'vector-effect': {inherit: true, initial: 'none', values: {'none':'none', 'non-scaling-stroke':'non-scaling-stroke'}}
64
65
  };
65
66
 
66
67
  function docBeginGroup(bbox) {
@@ -113,11 +114,13 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
113
114
  doc.addContent('/' + name + ' gs');
114
115
  }
115
116
  function docCreatePattern(group, dx, dy, matrix) {
116
- let pattern = new (function PDFPattern() {})();
117
- pattern.group = group;
118
- pattern.dx = dx;
119
- pattern.dy = dy;
120
- pattern.matrix = matrix || [1, 0, 0, 1, 0, 0];
117
+ let pattern = {
118
+ type: 'PDFPattern',
119
+ group: group,
120
+ dx: dx,
121
+ dy: dy,
122
+ matrix: matrix || [1, 0, 0, 1, 0, 0],
123
+ }
121
124
  return pattern;
122
125
  }
123
126
  function docUsePattern(pattern, stroke) {
@@ -153,14 +156,62 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
153
156
  let mode = fill && stroke ? 2 : stroke ? 1 : fill ? 0 : 3;
154
157
  doc.addContent(mode + ' Tr');
155
158
  }
156
- function docWriteGlyph(glyph) {
157
- doc.addContent('<' + glyph + '> Tj');
159
+ function docWriteGlyphs(positions, font) {
160
+ let commands = [];
161
+ let commandStr = '';
162
+ const skew = font.fauxItalic ? -0.25 : 0;
163
+
164
+ // Add the given character to the 'TJ' command string.
165
+ function addChar(char) {
166
+ commandStr += char.glyph;
167
+ if (char.kern === 0) return;
168
+ commands.push(`<${commandStr}> ${validateNumber(char.kern)}`);
169
+ commandStr = '';
170
+ };
171
+
172
+ // Flush the current TJ command string to the output stream.
173
+ function flush() {
174
+ if (commandStr.length) {
175
+ commands.push(`<${commandStr}> 0`);
176
+ commandStr = '';
177
+ }
178
+ if (commands.length) {
179
+ doc.addContent(`[${commands.join(' ')}] TJ`);
180
+ commands = [];
181
+ }
182
+ };
183
+
184
+ for (let i = 0; i < positions.length; i++) {
185
+ const pos = positions[i];
186
+
187
+ if (pos.hidden || isEqual(pos.width, 0)) {
188
+ flush();
189
+ continue;
190
+ }
191
+
192
+ if (pos.continuous) {
193
+ addChar(pos);
194
+ continue;
195
+ }
196
+
197
+ // If this character is non-continuous, flush the command buffer.
198
+ flush();
199
+
200
+ // Start a new TJ command after writing a Text Matrix (Tm)
201
+ const cos = Math.cos(pos.rotate);
202
+ const sin = Math.sin(pos.rotate);
203
+ docSetTextMatrix(cos * pos.scale, sin * pos.scale, cos * skew - sin, sin * skew + cos, pos.x, pos.y);
204
+ addChar(pos);
205
+ };
206
+
207
+ // Flush any remaining characters in the buffer.
208
+ flush();
158
209
  }
159
210
  function docEndText() {
160
211
  doc.addContent('ET');
161
212
  }
162
213
  function docFillColor(color) {
163
- if (color[0].constructor.name === 'PDFPattern') {
214
+ if (color[0].type === 'PDFPattern') {
164
215
  doc.fillOpacity(color[1]);
165
216
  docUsePattern(color[0], false);
166
217
  } else {
@@ -168,13 +219,127 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
168
219
  }
169
220
  }
170
221
  function docStrokeColor(color) {
171
- if (color[0].constructor.name === 'PDFPattern') {
222
+ if (color[0].type === 'PDFPattern') {
172
223
  doc.strokeOpacity(color[1]);
173
224
  docUsePattern(color[0], true);
174
225
  } else {
175
226
  doc.strokeColor(color[0], color[1]);
176
227
  }
177
228
  }
229
+ // PDFKit doesn't accept any 0s in the dash array, but that's perfectly
230
+ // valid in SVG. So this function applys a dash array and offset, detecting
231
+ // any 0s in the dash array and updating it and the dash offset as needed to
232
+ // remove the zeros, but preserve the end result.
233
+ //
234
+ // `dashArray` must have an even number of elements
235
+ function docApplyDash(dashArray, dashOffset) {
236
+ let index;
237
+ // Anytime there's a 0 that isn't the first or last element of the array,
238
+ // we can remove it by combining the previous or next value. If it's a
239
+ // dash, then it's a zero-length dash between two spaces, so the dash can
240
+ // be eliminated and spaces combined by summing them, replacing all three
241
+ // values with the sum of the two spaces. If the 0 value is a space, then
242
+ // it's a zero-length space between two dashes, and the dashes can be
243
+ // similarly combined. So first we run that logic iteratively to remove
244
+ // all the 0s from the dash array that aren't the first or last element.
245
+ // Note that because we replace 3 values with one value, this doesn't
246
+ // change the even-ness of the length of dashArray.
247
+ while ((index = dashArray.slice(1, -1).indexOf(0)) !== -1) {
248
+ let actualIndex = index + 1;
249
+ let replacementValue = dashArray[actualIndex - 1] + dashArray[actualIndex + 1];
250
+ dashArray = dashArray
251
+ .slice(0, actualIndex - 1)
252
+ .concat([replacementValue])
253
+ .concat(dashArray.slice(actualIndex + 2));
254
+ }
255
+
256
+ // The stroke array only having two elements (a dash value and space
257
+ // value) is a special case.
258
+ if (dashArray.length === 2) {
259
+ if (dashArray[0] === 0) {
260
+ // Regardless of the space value, the dash length is zero, so we're
261
+ // not actually drawing a stroke. We can't describe that in a
262
+ // doc.dash() call in a way that PDFKit will accept, so we set the
263
+ // stroke opacity to zero as our best approximation.
264
+ doc.strokeOpacity(0);
265
+ return;
266
+ } else if (dashArray[1] === 0) {
267
+ // Regardless of the dash value, the space value is zero, meaning
268
+ // we're actually drawing a solid stroke, not a dashed one. We can
269
+ // make this happen by just emptying out the dash array.
270
+ dashArray = [];
271
+ }
272
+ } else {
273
+ if (dashArray[0] === 0) {
274
+ // The first dash is zero-length. We fix this by combining the first
275
+ // space (just after the first dash) with the last space and updating
276
+ // the dash offset accordingly. For example, if we had
277
+ //
278
+ // [ 0 4 3 2 5 1 ] (dash offset 0)
279
+ //
280
+ // ␣␣␣␣---␣␣-----␣
281
+ // ⎸
282
+ //
283
+ // we'd end up with
284
+ //
285
+ // [ 3 2 5 5 ] (dash offset -4)
286
+ //
287
+ // ---␣␣-----␣␣␣␣␣
288
+ // ⎸
289
+ //
290
+ // Another example where the dash array also ends with a 0:
291
+ //
292
+ // [ 0 4 3 2 5 0 ] (dash offset 0)
293
+ //
294
+ // ␣␣␣␣---␣␣-----
295
+ // ⎸
296
+ //
297
+ // we'd end up with
298
+ //
299
+ // [ 3 2 5 4 ] (dash offset -4)
300
+ //
301
+ // ---␣␣-----␣␣␣␣
302
+ // ⎸
303
+ dashOffset -= dashArray[1];
304
+ dashArray[dashArray.length - 1] += dashArray[1];
305
+ dashArray = dashArray.slice(2);
306
+ }
307
+ if (dashArray[dashArray.length - 1] === 0) {
308
+ // The last space is zero-length. We fix this by combining the last dash
309
+ // (just before the last space) with the first dash and updating the
310
+ // dash offset accordingly. For example, if we had
311
+ //
312
+ // [ 1 4 3 2 5 0 ] (dash offset 0)
313
+ //
314
+ // -␣␣␣␣---␣␣-----
315
+ // ⎸
316
+ //
317
+ // we'd end up with
318
+ //
319
+ // [ 6 4 3 2 ] (dash offset 5)
320
+ //
321
+ // ------␣␣␣␣---␣␣
322
+ // ⎸
323
+ //
324
+ dashOffset += dashArray[dashArray.length - 2];
325
+ dashArray[0] += dashArray[dashArray.length - 2];
326
+ dashArray = dashArray.slice(0, -2);
327
+ }
328
+ }
329
+
330
+ // Ensure the dash offset is non-negative (because of crbug.com/660850).
331
+ // First compute the total length of the dash array so we can add it to
332
+ // dash offset until dash offset is non-negative.
333
+ let length = 0;
334
+ for (let i = 0; i < dashArray.length; i++) {length += dashArray[i];}
335
+ if (length > 0) {
336
+ while (dashOffset < 0) {
337
+ dashOffset += length;
338
+ }
339
+ }
340
+
341
+ doc.dash(dashArray, {phase: dashOffset});
342
+ }
178
343
  function docInsertLink(x, y, w, h, url) {
179
344
  let ref = doc.ref({
180
345
  Type: 'Annot',
@@ -308,6 +473,11 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
308
473
  raw = (raw || '').trim();
309
474
  if (temp = NamedColors[raw]) {
310
475
  result = [temp.slice(), 1];
476
+ } else if (temp = raw.match(/^cmyk\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
477
+ temp[1] = parseInt(temp[1]); temp[2] = parseInt(temp[2]); temp[3] = parseInt(temp[3]); temp[4] = parseFloat(temp[4]);
478
+ if (temp[1] <= 100 && temp[2] <= 100 && temp[3] <= 100 && temp[4] <= 100) {
479
+ result = [temp.slice(1, 5), 1];
480
+ }
311
481
  } else if (temp = raw.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
312
482
  temp[1] = parseInt(temp[1]); temp[2] = parseInt(temp[2]); temp[3] = parseInt(temp[3]); temp[4] = parseFloat(temp[4]);
313
483
  if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256 && temp[4] <= 1) {
@@ -367,6 +537,11 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
367
537
  return new SvgShape().M(0, 0).L(doc.page.width, 0).L(doc.page.width, doc.page.height).L(0, doc.page.height)
368
538
  .transform(inverseMatrix(getGlobalMatrix())).getBoundingBox();
369
539
  }
540
+ function getPageScale() {
541
+ const bbox = getPageBBox();
542
+ const width = doc.page.width;
543
+ return width / bbox[2];
544
+ }
370
545
  function inverseMatrix(m) {
371
546
  let dt = m[0] * m[3] - m[1] * m[2];
372
547
  return [m[3] / dt, -m[1] / dt, -m[2] / dt, m[0] / dt, (m[2]*m[5] - m[3]*m[4]) / dt, (m[1]*m[4] - m[0]*m[5]) / dt];
@@ -556,7 +731,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
556
731
  if (selector.ids[i] !== elem.id) {return false;}
557
732
  }
558
733
  for (let i = 0; i < selector.classes.length; i++) {
559
- if (elem.classList.indexOf(selector.classes[i]) === -1) {return false;}
734
+ if (!elem.classList.contains(selector.classes[i])) {return false;}
560
735
  }
561
736
  return true;
562
737
  }
@@ -615,6 +790,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
615
790
  data.push({
616
791
  glyph: hex[i],
617
792
  unicode: unicode,
793
+ kern: pos[i].advanceWidth - pos[i].xAdvance,
618
794
  width: pos[i].advanceWidth * size / 1000,
619
795
  xOffset: pos[i].xOffset * size / 1000,
620
796
  yOffset: pos[i].yOffset * size / 1000,
@@ -1266,12 +1442,6 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1266
1442
  break;
1267
1443
  case 'stroke-dashoffset':
1268
1444
  result = this.computeLength(value, this.getViewport());
1269
- if (result != null) {
1270
- if (result < 0) { // fix for crbug.com/660850
1271
- let dasharray = this.get('stroke-dasharray');
1272
- for (let j = 0; j < dasharray.length; j++) {result += dasharray[j];}
1273
- }
1274
- }
1275
1445
  break;
1276
1446
  }
1277
1447
  if (result != null) {return styleCache[key] = result;}
@@ -1322,7 +1492,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1322
1492
  this.clip = function() {
1323
1493
  if (this.get('clip-path') !== 'none') {
1324
1494
  let clipPath = new SvgElemClipPath(this.get('clip-path'), null);
1325
- clipPath.useMask(this.getBoundingBox());
1495
+ clipPath.useMask((clipPath.attr('clipPathUnits') === 'objectBoundingBox') ? this.getBoundingBox() : null);
1326
1496
  return true;
1327
1497
  }
1328
1498
  };
@@ -1788,7 +1958,11 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1788
1958
  this.drawInDocument = function(isClip, isMask) {
1789
1959
  if (this.get('visibility') === 'hidden' || !this.shape) {return;}
1790
1960
  doc.save();
1791
- this.transform();
1961
+ if (this.get('vector-effect') === 'non-scaling-stroke') {
1962
+ this.shape.transform(this.getTransformation());
1963
+ } else {
1964
+ this.transform();
1965
+ }
1792
1966
  this.clip();
1793
1967
  if (!isClip) {
1794
1968
  let masked = this.mask(),
@@ -1801,6 +1975,11 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1801
1975
  stroke = this.getStroke(isClip, isMask),
1802
1976
  lineWidth = this.get('stroke-width'),
1803
1977
  lineCap = this.get('stroke-linecap');
1978
+
1979
+ if (this.get('vector-effect') === 'non-scaling-stroke') {
1980
+ lineWidth = lineWidth / getPageScale();
1981
+ }
1982
+
1804
1983
  if (fill || stroke) {
1805
1984
  if (fill) {
1806
1985
  docFillColor(fill);
@@ -1835,8 +2014,8 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1835
2014
  doc.lineWidth(lineWidth)
1836
2015
  .miterLimit(this.get('stroke-miterlimit'))
1837
2016
  .lineJoin(this.get('stroke-linejoin'))
1838
- .lineCap(lineCap)
1839
- .dash(dashArray, {phase: dashOffset});
2017
+ .lineCap(lineCap);
2018
+ docApplyDash(dashArray, dashOffset);
1840
2019
  }
1841
2020
  for (let j = 0; j < subPaths.length; j++) {
1842
2021
  if (subPaths[j].totalLength > 0) {
@@ -1856,7 +2035,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1856
2035
  markerEnd = this.get('marker-end');
1857
2036
  if (markerStart !== 'none' || markerMid !== 'none' || markerEnd !== 'none') {
1858
2037
  let markersPos = this.shape.getMarkers();
1859
- if (markerStart !== 'none') {
2038
+ if (markerStart !== 'none' && markersPos.length > 0) {
1860
2039
  let marker = new SvgElemMarker(markerStart, null);
1861
2040
  marker.drawMarker(false, isMask, markersPos[0], lineWidth);
1862
2041
  }
@@ -1866,7 +2045,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
1866
2045
  marker.drawMarker(false, isMask, markersPos[i], lineWidth);
1867
2046
  }
1868
2047
  }
1869
- if (markerEnd !== 'none') {
2048
+ if (markerEnd !== 'none' && markersPos.length > 0) {
1870
2049
  let marker = new SvgElemMarker(markerEnd, null);
1871
2050
  marker.drawMarker(false, isMask, markersPos[markersPos.length - 1], lineWidth);
1872
2051
  }
@@ -2028,6 +2207,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2028
2207
  this.useMask = function(bBox) {
2029
2208
  let group = docBeginGroup(getPageBBox());
2030
2209
  doc.save();
2210
+ doc.transform.apply(doc, this.get('transform'));
2031
2211
  if (this.attr('clipPathUnits') === 'objectBoundingBox') {
2032
2212
  doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
2033
2213
  }
@@ -2056,7 +2236,6 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2056
2236
  w = this.getLength('width', this.getVWidth(), 1.2) * (bBox[2] - bBox[0]);
2057
2237
  h = this.getLength('height', this.getVHeight(), 1.2) * (bBox[3] - bBox[1]);
2058
2238
  }
2059
- doc.rect(x, y, w, h).clip();
2060
2239
  if (this.attr('maskContentUnits') === 'objectBoundingBox') {
2061
2240
  doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
2062
2241
  }
@@ -2125,18 +2304,12 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2125
2304
  doc.lineWidth(strokeWidth)
2126
2305
  .miterLimit(this.get('stroke-miterlimit'))
2127
2306
  .lineJoin(this.get('stroke-linejoin'))
2128
- .lineCap(this.get('stroke-linecap'))
2129
- .dash(this.get('stroke-dasharray'), {phase:this.get('stroke-dashoffset')});
2307
+ .lineCap(this.get('stroke-linecap'));
2308
+ docApplyDash(this.get('stroke-dasharray'), this.get('stroke-dashoffset'));
2130
2309
  }
2131
2310
  docBeginText(this._font.font, this._font.size);
2132
2311
  docSetTextMode(!!fill, !!stroke);
2133
- for (let j = 0, pos = childElem._pos; j < pos.length; j++) {
2134
- if (!pos[j].hidden && isNotEqual(pos[j].width, 0)) {
2135
- let cos = Math.cos(pos[j].rotate), sin = Math.sin(pos[j].rotate), skew = (this._font.fauxItalic ? -0.25 : 0);
2136
- docSetTextMatrix(cos * pos[j].scale, sin * pos[j].scale, cos * skew - sin, sin * skew + cos, pos[j].x, pos[j].y);
2137
- docWriteGlyph(pos[j].glyph);
2138
- }
2139
- }
2312
+ docWriteGlyphs(childElem._pos, this._font);
2140
2313
  docEndText();
2141
2314
  }
2142
2315
  break;
@@ -2157,8 +2330,8 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2157
2330
  doc.lineWidth(this.get('stroke-width'))
2158
2331
  .miterLimit(this.get('stroke-miterlimit'))
2159
2332
  .lineJoin(this.get('stroke-linejoin'))
2160
- .lineCap(this.get('stroke-linecap'))
2161
- .dash(this.get('stroke-dasharray'), {phase:this.get('stroke-dashoffset')});
2333
+ .lineCap(this.get('stroke-linecap'));
2334
+ docApplyDash(this.get('stroke-dasharray'), this.get('stroke-dashoffset'));
2162
2335
  }
2163
2336
  for (let j = 0, pos = this._pos; j < pos.length; j++) {
2164
2337
  if (!pos[j].hidden && isNotEqual(pos[j].width, 0)) {
@@ -2234,6 +2407,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2234
2407
  let textScale = length / (endX - startX);
2235
2408
  if (textScale > 0 && textScale < Infinity) {
2236
2409
  for (let j = 0; j < pos.length; j++) {
2410
+ pos[j].continuous = false;
2237
2411
  pos[j].x = startX + textScale * (pos[j].x - startX);
2238
2412
  pos[j].scale *= textScale;
2239
2413
  pos[j].width *= textScale;
@@ -2243,6 +2417,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2243
2417
  if (pos.length >= 2) {
2244
2418
  let spaceDiff = (length - (endX - startX)) / (pos.length - 1);
2245
2419
  for (let j = 0; j < pos.length; j++) {
2420
+ pos[j].continuous = false;
2246
2421
  pos[j].x += j * spaceDiff;
2247
2422
  }
2248
2423
  }
@@ -2262,7 +2437,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2262
2437
  try {
2263
2438
  doc.font(fontNameorLink);
2264
2439
  } catch(e) {
2265
- warningCallback('SVGElemText: failed to open font "' + fontNameorLink + '" in PDFKit');
2440
+ warningCallback('SVGElemText: failed to open font "' + fontNameorLink + '" in PDFKit: ' + e.message);
2266
2441
  }
2267
2442
  currentElem._pos = [];
2268
2443
  currentElem._index = 0;
@@ -2273,7 +2448,22 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2273
2448
  letterSpacing = currentElem.get('letter-spacing'),
2274
2449
  textAnchor = currentElem.get('text-anchor'),
2275
2450
  textDirection = currentElem.get('direction'),
2276
- baseline = getBaseline(currentElem._font.font, currentElem._font.size, currentElem.get('alignment-baseline') || currentElem.get('dominant-baseline'), currentElem.get('baseline-shift'));
2451
+ // `alignment-baseline` and `baseline-shift` have no effect on
2452
+ // `<text>` elements according to the SVG spec. So, detect when
2453
+ // we're styling a `<text>` element and ignore
2454
+ // `alignment-baseline` (only factoring in `dominant-baseline`)
2455
+ // and `baseline-shift` (which can only have the default value of
2456
+ // `baseline`).
2457
+ //
2458
+ // Note that Chrome (as of v99) incorrectly factors in
2459
+ // `alignment-baseline` on `<text>` elements, while Firefox
2460
+ // correctly follows the spec and ignores it. This means that our
2461
+ // output will differ from Chrome's in these cases, but conform to
2462
+ // SVG specification.
2463
+ isTextElem = currentElem.name === 'text',
2464
+ baselineAttr = isTextElem ? currentElem.get('dominant-baseline') : (currentElem.get('alignment-baseline') || currentElem.get('dominant-baseline')),
2465
+ baselineShiftAttr = isTextElem ? 'baseline' : currentElem.get('baseline-shift'),
2466
+ baseline = getBaseline(currentElem._font.font, currentElem._font.size, baselineAttr, baselineShiftAttr);
2277
2467
  if (currentElem.name === 'textPath') {
2278
2468
  doAnchoring();
2279
2469
  currentX = currentY = 0;
@@ -2313,6 +2503,8 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2313
2503
  dyAttr = currentElem._dy[index],
2314
2504
  rotAttr = currentElem._rot[index],
2315
2505
  continuous = !(w === 0 && j === 0);
2506
+ if (letterSpacing !== 0) {continuous = false}
2507
+ if (wordSpacing !== 0) {continuous = false}
2316
2508
  if (xAttr !== undefined) {continuous = false; doAnchoring(); currentX = xAttr;}
2317
2509
  if (yAttr !== undefined) {continuous = false; doAnchoring(); currentY = yAttr;}
2318
2510
  if (dxAttr !== undefined) {continuous = false; currentX += dxAttr;}
@@ -2322,6 +2514,7 @@ var SVGtoPDF = function(doc, svg, x, y, options) {
2322
2514
  glyph: pos[j].glyph,
2323
2515
  rotate: (Math.PI / 180) * currentElem.chooseValue(rotAttr, currentElem._defRot),
2324
2516
  x: currentX + pos[j].xOffset,
2517
+ kern: pos[j].kern,
2325
2518
  y: currentY + baseline + pos[j].yOffset,
2326
2519
  width: pos[j].width,
2327
2520
  ascent: getAscent(currentElem._font.font, currentElem._font.size),
@@ -9,11 +9,22 @@ class OutputDocumentServer extends OutputDocument {
9
9
  */
10
10
  async write(filename) {
11
11
  const stream = await this.getStream();
12
- return new Promise((resolve) => {
13
- stream.pipe(fs.createWriteStream(filename));
12
+ const writeStream = fs.createWriteStream(filename);
13
+
14
+ const streamEnded = new Promise((resolve, reject) => {
14
15
  stream.on('end', resolve);
15
- stream.end();
16
+ stream.on('error', reject);
17
+ });
18
+
19
+ const writeClosed = new Promise((resolve, reject) => {
20
+ writeStream.on('close', resolve);
21
+ writeStream.on('error', reject);
16
22
  });
23
+
24
+ stream.pipe(writeStream);
25
+ stream.end();
26
+
27
+ await Promise.all([streamEnded, writeClosed]);
17
28
  }
18
29
 
19
30
  }
package/src/Printer.js CHANGED
@@ -102,12 +102,11 @@ class PdfPrinter {
102
102
 
103
103
  // if pageSize.height is set to Infinity, calculate the actual height of the page that
104
104
  // was laid out using the height of each of the items in the page.
105
- if (pageSize.height === Infinity) {
106
- let pageHeight = calculatePageHeight(pages, docDefinition.pageMargins);
107
- pages.forEach(page => {
108
- page.pageSize.height = pageHeight;
109
- });
110
- }
105
+ pages.forEach(page => {
106
+ if (page.pageSize.height === Infinity) {
107
+ page.pageSize.height = calculatePageHeight(page, page.pageMargins);
108
+ }
109
+ });
111
110
 
112
111
  const renderer = new Renderer(this.pdfKitDoc, options.progressCallback);
113
112
  renderer.renderPages(pages);
@@ -265,7 +264,7 @@ function embedFiles(docDefinition, pdfKitDoc) {
265
264
  }
266
265
  }
267
266
 
268
- function calculatePageHeight(pages, margins) {
267
+ function calculatePageHeight(page, margins) {
269
268
  function getItemHeight(item) {
270
269
  if (typeof item.item.getHeight === 'function') {
271
270
  return item.item.getHeight();
@@ -292,13 +291,11 @@ function calculatePageHeight(pages, margins) {
292
291
  let fixedMargins = normalizePageMargin(margins || 40);
293
292
  let height = fixedMargins.top;
294
293
 
295
- pages.forEach(page => {
296
- page.items.forEach(item => {
297
- let bottomPosition = getBottomPosition(item);
298
- if (bottomPosition > height) {
299
- height = bottomPosition;
300
- }
301
- });
294
+ page.items.forEach(item => {
295
+ let bottomPosition = getBottomPosition(item);
296
+ if (bottomPosition > height) {
297
+ height = bottomPosition;
298
+ }
302
299
  });
303
300
 
304
301
  height += fixedMargins.bottom;
package/src/Renderer.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import TextDecorator from './TextDecorator';
2
2
  import TextInlines from './TextInlines';
3
- import { isNumber } from './helpers/variableType';
3
+ import { isNumber, isString } from './helpers/variableType';
4
4
  import SVGtoPDF from './3rd-party/svg-to-pdfkit';
5
5
 
6
6
  const findFont = (fonts, requiredFonts, defaultFont) => {
@@ -138,7 +138,7 @@ class Renderer {
138
138
  textDecorator.drawBackground(line, x, y);
139
139
 
140
140
  //TODO: line.optimizeInlines();
141
- //TOOD: lines without differently styled inlines should be written to pdf as one stream
141
+ //TODO: lines without differently styled inlines should be written to pdf as one stream
142
142
  for (let i = 0, l = line.inlines.length; i < l; i++) {
143
143
  let inline = line.inlines[i];
144
144
  let shiftToBaseline = lineHeight - ((inline.font.ascender / 1000) * inline.fontSize) - descent;
@@ -329,7 +329,13 @@ class Renderer {
329
329
  }
330
330
 
331
331
  renderSVG(svg) {
332
- let options = Object.assign({ width: svg._width, height: svg._height, assumePt: true }, svg.options);
332
+ let options = {
333
+ width: svg._width,
334
+ height: svg._height,
335
+ assumePt: true,
336
+ useCSS: !isString(svg.svg),
337
+ ...svg.options
338
+ };
333
339
  options.fontCallback = (family, bold, italic) => {
334
340
  let fontsFamily = family.split(',').map(f => f.trim().replace(/('|")/g, ''));
335
341
  let font = findFont(this.pdfDocument.fonts, fontsFamily, svg.font || 'Roboto');