pdfmake 0.3.0 → 0.3.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-vfs.js CHANGED
@@ -41,4 +41,4 @@ const vfsFileContent = vfsBefore + JSON.stringify(vfs, null, 2) + vfsAfter;
41
41
  fs.writeFileSync(vfsFilename, vfsFileContent);
42
42
 
43
43
  console.log('');
44
- console.log('Builded ' + files.length + ' files to ' + vfsFilename + '.');
44
+ console.log('Built ' + files.length + ' files to ' + vfsFilename + '.');
@@ -717,6 +717,14 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
717
717
  'scroll': 'hidden',
718
718
  'visible': 'visible'
719
719
  }
720
+ },
721
+ 'vector-effect': {
722
+ inherit: true,
723
+ initial: 'none',
724
+ values: {
725
+ 'none': 'none',
726
+ 'non-scaling-stroke': 'non-scaling-stroke'
727
+ }
720
728
  }
721
729
  };
722
730
  function docBeginGroup(bbox) {
@@ -797,11 +805,13 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
797
805
  doc.addContent('/' + name + ' gs');
798
806
  }
799
807
  function docCreatePattern(group, dx, dy, matrix) {
800
- let pattern = new function PDFPattern() {}();
801
- pattern.group = group;
802
- pattern.dx = dx;
803
- pattern.dy = dy;
804
- pattern.matrix = matrix || [1, 0, 0, 1, 0, 0];
808
+ let pattern = {
809
+ type: 'PDFPattern',
810
+ group: group,
811
+ dx: dx,
812
+ dy: dy,
813
+ matrix: matrix || [1, 0, 0, 1, 0, 0]
814
+ };
805
815
  return pattern;
806
816
  }
807
817
  function docUsePattern(pattern, stroke) {
@@ -848,14 +858,62 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
848
858
  let mode = fill && stroke ? 2 : stroke ? 1 : fill ? 0 : 3;
849
859
  doc.addContent(mode + ' Tr');
850
860
  }
851
- function docWriteGlyph(glyph) {
852
- doc.addContent('<' + glyph + '> Tj');
861
+ function docWriteGlyphs(positions, font) {
862
+ let commands = [];
863
+ let commandStr = '';
864
+ const skew = font.fauxItalic ? -0.25 : 0;
865
+
866
+ // Add the given character to the 'TJ' command string.
867
+ function addChar(char) {
868
+ commandStr += char.glyph;
869
+ if (char.kern === 0) return;
870
+ commands.push(`<${commandStr}> ${validateNumber(char.kern)}`);
871
+ commandStr = '';
872
+ }
873
+ ;
874
+
875
+ // Flush the current TJ command string to the output stream.
876
+ function flush() {
877
+ if (commandStr.length) {
878
+ commands.push(`<${commandStr}> 0`);
879
+ commandStr = '';
880
+ }
881
+ if (commands.length) {
882
+ doc.addContent(`[${commands.join(' ')}] TJ`);
883
+ commands = [];
884
+ }
885
+ }
886
+ ;
887
+ for (let i = 0; i < positions.length; i++) {
888
+ const pos = positions[i];
889
+ if (pos.hidden || isEqual(pos.width, 0)) {
890
+ flush();
891
+ continue;
892
+ }
893
+ if (pos.continuous) {
894
+ addChar(pos);
895
+ continue;
896
+ }
897
+
898
+ // If this character is non-continuous, flush the command buffer.
899
+ flush();
900
+
901
+ // Start a new TJ command after writing a Text Matrix (Tm)
902
+ const cos = Math.cos(pos.rotate);
903
+ const sin = Math.sin(pos.rotate);
904
+ docSetTextMatrix(cos * pos.scale, sin * pos.scale, cos * skew - sin, sin * skew + cos, pos.x, pos.y);
905
+ addChar(pos);
906
+ }
907
+ ;
908
+
909
+ // Flush any remaining characters in the buffer.
910
+ flush();
853
911
  }
854
912
  function docEndText() {
855
913
  doc.addContent('ET');
856
914
  }
857
915
  function docFillColor(color) {
858
- if (color[0].constructor.name === 'PDFPattern') {
916
+ if (color[0].type === 'PDFPattern') {
859
917
  doc.fillOpacity(color[1]);
860
918
  docUsePattern(color[0], false);
861
919
  } else {
@@ -863,13 +921,127 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
863
921
  }
864
922
  }
865
923
  function docStrokeColor(color) {
866
- if (color[0].constructor.name === 'PDFPattern') {
924
+ if (color[0].type === 'PDFPattern') {
867
925
  doc.strokeOpacity(color[1]);
868
926
  docUsePattern(color[0], true);
869
927
  } else {
870
928
  doc.strokeColor(color[0], color[1]);
871
929
  }
872
930
  }
931
+ // PDFKit doesn't accept any 0s in the dash array, but that's perfectly
932
+ // valid in SVG. So this function applys a dash array and offset, detecting
933
+ // any 0s in the dash array and updating it and the dash offset as needed to
934
+ // remove the zeros, but preserve the end result.
935
+ //
936
+ // `dashArray` must have an even number of elements
937
+ function docApplyDash(dashArray, dashOffset) {
938
+ let index;
939
+ // Anytime there's a 0 that isn't the first or last element of the array,
940
+ // we can remove it by combining the previous or next value. If it's a
941
+ // dash, then it's a zero-length dash between two spaces, so the dash can
942
+ // be eliminated and spaces combined by summing them, replacing all three
943
+ // values with the sum of the two spaces. If the 0 value is a space, then
944
+ // it's a zero-length space between two dashes, and the dashes can be
945
+ // similarly combined. So first we run that logic iteratively to remove
946
+ // all the 0s from the dash array that aren't the first or last element.
947
+ // Note that because we replace 3 values with one value, this doesn't
948
+ // change the even-ness of the length of dashArray.
949
+ while ((index = dashArray.slice(1, -1).indexOf(0)) !== -1) {
950
+ let actualIndex = index + 1;
951
+ let replacementValue = dashArray[actualIndex - 1] + dashArray[actualIndex + 1];
952
+ dashArray = dashArray.slice(0, actualIndex - 1).concat([replacementValue]).concat(dashArray.slice(actualIndex + 2));
953
+ }
954
+
955
+ // The stroke array only having two elements (a dash value and space
956
+ // value) is a special case.
957
+ if (dashArray.length === 2) {
958
+ if (dashArray[0] === 0) {
959
+ // Regardless of the space value, the dash length is zero, so we're
960
+ // not actually drawing a stroke. We can't describe that in a
961
+ // doc.dash() call in a way that PDFKit will accept, so we set the
962
+ // stroke opacity to zero as our best approximation.
963
+ doc.strokeOpacity(0);
964
+ return;
965
+ } else if (dashArray[1] === 0) {
966
+ // Regardless of the dash value, the space value is zero, meaning
967
+ // we're actually drawing a solid stroke, not a dashed one. We can
968
+ // make this happen by just emptying out the dash array.
969
+ dashArray = [];
970
+ }
971
+ } else {
972
+ if (dashArray[0] === 0) {
973
+ // The first dash is zero-length. We fix this by combining the first
974
+ // space (just after the first dash) with the last space and updating
975
+ // the dash offset accordingly. For example, if we had
976
+ //
977
+ // [ 0 4 3 2 5 1 ] (dash offset 0)
978
+ //
979
+ // ␣␣␣␣---␣␣-----␣
980
+ // ⎸
981
+ //
982
+ // we'd end up with
983
+ //
984
+ // [ 3 2 5 5 ] (dash offset -4)
985
+ //
986
+ // ---␣␣-----␣␣␣␣␣
987
+ // ⎸
988
+ //
989
+ // Another example where the dash array also ends with a 0:
990
+ //
991
+ // [ 0 4 3 2 5 0 ] (dash offset 0)
992
+ //
993
+ // ␣␣␣␣---␣␣-----
994
+ // ⎸
995
+ //
996
+ // we'd end up with
997
+ //
998
+ // [ 3 2 5 4 ] (dash offset -4)
999
+ //
1000
+ // ---␣␣-----␣␣␣␣
1001
+ // ⎸
1002
+ dashOffset -= dashArray[1];
1003
+ dashArray[dashArray.length - 1] += dashArray[1];
1004
+ dashArray = dashArray.slice(2);
1005
+ }
1006
+ if (dashArray[dashArray.length - 1] === 0) {
1007
+ // The last space is zero-length. We fix this by combining the last dash
1008
+ // (just before the last space) with the first dash and updating the
1009
+ // dash offset accordingly. For example, if we had
1010
+ //
1011
+ // [ 1 4 3 2 5 0 ] (dash offset 0)
1012
+ //
1013
+ // -␣␣␣␣---␣␣-----
1014
+ // ⎸
1015
+ //
1016
+ // we'd end up with
1017
+ //
1018
+ // [ 6 4 3 2 ] (dash offset 5)
1019
+ //
1020
+ // ------␣␣␣␣---␣␣
1021
+ // ⎸
1022
+ //
1023
+ dashOffset += dashArray[dashArray.length - 2];
1024
+ dashArray[0] += dashArray[dashArray.length - 2];
1025
+ dashArray = dashArray.slice(0, -2);
1026
+ }
1027
+ }
1028
+
1029
+ // Ensure the dash offset is non-negative (because of crbug.com/660850).
1030
+ // First compute the total length of the dash array so we can add it to
1031
+ // dash offset until dash offset is non-negative.
1032
+ let length = 0;
1033
+ for (let i = 0; i < dashArray.length; i++) {
1034
+ length += dashArray[i];
1035
+ }
1036
+ if (length > 0) {
1037
+ while (dashOffset < 0) {
1038
+ dashOffset += length;
1039
+ }
1040
+ }
1041
+ doc.dash(dashArray, {
1042
+ phase: dashOffset
1043
+ });
1044
+ }
873
1045
  function docInsertLink(x, y, w, h, url) {
874
1046
  let ref = doc.ref({
875
1047
  Type: 'Annot',
@@ -1033,6 +1205,14 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
1033
1205
  raw = (raw || '').trim();
1034
1206
  if (temp = NamedColors[raw]) {
1035
1207
  result = [temp.slice(), 1];
1208
+ } else if (temp = raw.match(/^cmyk\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
1209
+ temp[1] = parseInt(temp[1]);
1210
+ temp[2] = parseInt(temp[2]);
1211
+ temp[3] = parseInt(temp[3]);
1212
+ temp[4] = parseFloat(temp[4]);
1213
+ if (temp[1] <= 100 && temp[2] <= 100 && temp[3] <= 100 && temp[4] <= 100) {
1214
+ result = [temp.slice(1, 5), 1];
1215
+ }
1036
1216
  } else if (temp = raw.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
1037
1217
  temp[1] = parseInt(temp[1]);
1038
1218
  temp[2] = parseInt(temp[2]);
@@ -1097,6 +1277,11 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
1097
1277
  function getPageBBox() {
1098
1278
  return new SvgShape().M(0, 0).L(doc.page.width, 0).L(doc.page.width, doc.page.height).L(0, doc.page.height).transform(inverseMatrix(getGlobalMatrix())).getBoundingBox();
1099
1279
  }
1280
+ function getPageScale() {
1281
+ const bbox = getPageBBox();
1282
+ const width = doc.page.width;
1283
+ return width / bbox[2];
1284
+ }
1100
1285
  function inverseMatrix(m) {
1101
1286
  let dt = m[0] * m[3] - m[1] * m[2];
1102
1287
  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];
@@ -1357,7 +1542,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
1357
1542
  }
1358
1543
  }
1359
1544
  for (let i = 0; i < selector.classes.length; i++) {
1360
- if (elem.classList.indexOf(selector.classes[i]) === -1) {
1545
+ if (!elem.classList.contains(selector.classes[i])) {
1361
1546
  return false;
1362
1547
  }
1363
1548
  }
@@ -1449,6 +1634,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
1449
1634
  data.push({
1450
1635
  glyph: hex[i],
1451
1636
  unicode: unicode,
1637
+ kern: pos[i].advanceWidth - pos[i].xAdvance,
1452
1638
  width: pos[i].advanceWidth * size / 1000,
1453
1639
  xOffset: pos[i].xOffset * size / 1000,
1454
1640
  yOffset: pos[i].yOffset * size / 1000,
@@ -2253,15 +2439,6 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2253
2439
  break;
2254
2440
  case 'stroke-dashoffset':
2255
2441
  result = this.computeLength(value, this.getViewport());
2256
- if (result != null) {
2257
- if (result < 0) {
2258
- // fix for crbug.com/660850
2259
- let dasharray = this.get('stroke-dasharray');
2260
- for (let j = 0; j < dasharray.length; j++) {
2261
- result += dasharray[j];
2262
- }
2263
- }
2264
- }
2265
2442
  break;
2266
2443
  }
2267
2444
  if (result != null) {
@@ -2315,7 +2492,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2315
2492
  this.clip = function () {
2316
2493
  if (this.get('clip-path') !== 'none') {
2317
2494
  let clipPath = new SvgElemClipPath(this.get('clip-path'), null);
2318
- clipPath.useMask(this.getBoundingBox());
2495
+ clipPath.useMask(clipPath.attr('clipPathUnits') === 'objectBoundingBox' ? this.getBoundingBox() : null);
2319
2496
  return true;
2320
2497
  }
2321
2498
  };
@@ -2788,7 +2965,11 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2788
2965
  return;
2789
2966
  }
2790
2967
  doc.save();
2791
- this.transform();
2968
+ if (this.get('vector-effect') === 'non-scaling-stroke') {
2969
+ this.shape.transform(this.getTransformation());
2970
+ } else {
2971
+ this.transform();
2972
+ }
2792
2973
  this.clip();
2793
2974
  if (!isClip) {
2794
2975
  let masked = this.mask(),
@@ -2801,6 +2982,9 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2801
2982
  stroke = this.getStroke(isClip, isMask),
2802
2983
  lineWidth = this.get('stroke-width'),
2803
2984
  lineCap = this.get('stroke-linecap');
2985
+ if (this.get('vector-effect') === 'non-scaling-stroke') {
2986
+ lineWidth = lineWidth / getPageScale();
2987
+ }
2804
2988
  if (fill || stroke) {
2805
2989
  if (fill) {
2806
2990
  docFillColor(fill);
@@ -2832,9 +3016,8 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2832
3016
  dashOffset *= this.dashScale;
2833
3017
  }
2834
3018
  docStrokeColor(stroke);
2835
- doc.lineWidth(lineWidth).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(lineCap).dash(dashArray, {
2836
- phase: dashOffset
2837
- });
3019
+ doc.lineWidth(lineWidth).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(lineCap);
3020
+ docApplyDash(dashArray, dashOffset);
2838
3021
  }
2839
3022
  for (let j = 0; j < subPaths.length; j++) {
2840
3023
  if (subPaths[j].totalLength > 0) {
@@ -2854,7 +3037,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2854
3037
  markerEnd = this.get('marker-end');
2855
3038
  if (markerStart !== 'none' || markerMid !== 'none' || markerEnd !== 'none') {
2856
3039
  let markersPos = this.shape.getMarkers();
2857
- if (markerStart !== 'none') {
3040
+ if (markerStart !== 'none' && markersPos.length > 0) {
2858
3041
  let marker = new SvgElemMarker(markerStart, null);
2859
3042
  marker.drawMarker(false, isMask, markersPos[0], lineWidth);
2860
3043
  }
@@ -2864,7 +3047,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
2864
3047
  marker.drawMarker(false, isMask, markersPos[i], lineWidth);
2865
3048
  }
2866
3049
  }
2867
- if (markerEnd !== 'none') {
3050
+ if (markerEnd !== 'none' && markersPos.length > 0) {
2868
3051
  let marker = new SvgElemMarker(markerEnd, null);
2869
3052
  marker.drawMarker(false, isMask, markersPos[markersPos.length - 1], lineWidth);
2870
3053
  }
@@ -3027,6 +3210,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3027
3210
  this.useMask = function (bBox) {
3028
3211
  let group = docBeginGroup(getPageBBox());
3029
3212
  doc.save();
3213
+ doc.transform.apply(doc, this.get('transform'));
3030
3214
  if (this.attr('clipPathUnits') === 'objectBoundingBox') {
3031
3215
  doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
3032
3216
  }
@@ -3054,7 +3238,6 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3054
3238
  w = this.getLength('width', this.getVWidth(), 1.2) * (bBox[2] - bBox[0]);
3055
3239
  h = this.getLength('height', this.getVHeight(), 1.2) * (bBox[3] - bBox[1]);
3056
3240
  }
3057
- doc.rect(x, y, w, h).clip();
3058
3241
  if (this.attr('maskContentUnits') === 'objectBoundingBox') {
3059
3242
  doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
3060
3243
  }
@@ -3128,21 +3311,12 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3128
3311
  }
3129
3312
  if (stroke && strokeWidth) {
3130
3313
  docStrokeColor(stroke);
3131
- doc.lineWidth(strokeWidth).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(this.get('stroke-linecap')).dash(this.get('stroke-dasharray'), {
3132
- phase: this.get('stroke-dashoffset')
3133
- });
3314
+ doc.lineWidth(strokeWidth).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(this.get('stroke-linecap'));
3315
+ docApplyDash(this.get('stroke-dasharray'), this.get('stroke-dashoffset'));
3134
3316
  }
3135
3317
  docBeginText(this._font.font, this._font.size);
3136
3318
  docSetTextMode(!!fill, !!stroke);
3137
- for (let j = 0, pos = childElem._pos; j < pos.length; j++) {
3138
- if (!pos[j].hidden && isNotEqual(pos[j].width, 0)) {
3139
- let cos = Math.cos(pos[j].rotate),
3140
- sin = Math.sin(pos[j].rotate),
3141
- skew = this._font.fauxItalic ? -0.25 : 0;
3142
- docSetTextMatrix(cos * pos[j].scale, sin * pos[j].scale, cos * skew - sin, sin * skew + cos, pos[j].x, pos[j].y);
3143
- docWriteGlyph(pos[j].glyph);
3144
- }
3145
- }
3319
+ docWriteGlyphs(childElem._pos, this._font);
3146
3320
  docEndText();
3147
3321
  }
3148
3322
  break;
@@ -3160,9 +3334,8 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3160
3334
  }
3161
3335
  if (stroke) {
3162
3336
  docStrokeColor(stroke);
3163
- doc.lineWidth(this.get('stroke-width')).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(this.get('stroke-linecap')).dash(this.get('stroke-dasharray'), {
3164
- phase: this.get('stroke-dashoffset')
3165
- });
3337
+ doc.lineWidth(this.get('stroke-width')).miterLimit(this.get('stroke-miterlimit')).lineJoin(this.get('stroke-linejoin')).lineCap(this.get('stroke-linecap'));
3338
+ docApplyDash(this.get('stroke-dasharray'), this.get('stroke-dashoffset'));
3166
3339
  }
3167
3340
  for (let j = 0, pos = this._pos; j < pos.length; j++) {
3168
3341
  if (!pos[j].hidden && isNotEqual(pos[j].width, 0)) {
@@ -3246,6 +3419,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3246
3419
  let textScale = length / (endX - startX);
3247
3420
  if (textScale > 0 && textScale < Infinity) {
3248
3421
  for (let j = 0; j < pos.length; j++) {
3422
+ pos[j].continuous = false;
3249
3423
  pos[j].x = startX + textScale * (pos[j].x - startX);
3250
3424
  pos[j].scale *= textScale;
3251
3425
  pos[j].width *= textScale;
@@ -3255,6 +3429,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3255
3429
  if (pos.length >= 2) {
3256
3430
  let spaceDiff = (length - (endX - startX)) / (pos.length - 1);
3257
3431
  for (let j = 0; j < pos.length; j++) {
3432
+ pos[j].continuous = false;
3258
3433
  pos[j].x += j * spaceDiff;
3259
3434
  }
3260
3435
  }
@@ -3279,7 +3454,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3279
3454
  try {
3280
3455
  doc.font(fontNameorLink);
3281
3456
  } catch (e) {
3282
- warningCallback('SVGElemText: failed to open font "' + fontNameorLink + '" in PDFKit');
3457
+ warningCallback('SVGElemText: failed to open font "' + fontNameorLink + '" in PDFKit: ' + e.message);
3283
3458
  }
3284
3459
  currentElem._pos = [];
3285
3460
  currentElem._index = 0;
@@ -3295,7 +3470,22 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3295
3470
  letterSpacing = currentElem.get('letter-spacing'),
3296
3471
  textAnchor = currentElem.get('text-anchor'),
3297
3472
  textDirection = currentElem.get('direction'),
3298
- baseline = getBaseline(currentElem._font.font, currentElem._font.size, currentElem.get('alignment-baseline') || currentElem.get('dominant-baseline'), currentElem.get('baseline-shift'));
3473
+ // `alignment-baseline` and `baseline-shift` have no effect on
3474
+ // `<text>` elements according to the SVG spec. So, detect when
3475
+ // we're styling a `<text>` element and ignore
3476
+ // `alignment-baseline` (only factoring in `dominant-baseline`)
3477
+ // and `baseline-shift` (which can only have the default value of
3478
+ // `baseline`).
3479
+ //
3480
+ // Note that Chrome (as of v99) incorrectly factors in
3481
+ // `alignment-baseline` on `<text>` elements, while Firefox
3482
+ // correctly follows the spec and ignores it. This means that our
3483
+ // output will differ from Chrome's in these cases, but conform to
3484
+ // SVG specification.
3485
+ isTextElem = currentElem.name === 'text',
3486
+ baselineAttr = isTextElem ? currentElem.get('dominant-baseline') : currentElem.get('alignment-baseline') || currentElem.get('dominant-baseline'),
3487
+ baselineShiftAttr = isTextElem ? 'baseline' : currentElem.get('baseline-shift'),
3488
+ baseline = getBaseline(currentElem._font.font, currentElem._font.size, baselineAttr, baselineShiftAttr);
3299
3489
  if (currentElem.name === 'textPath') {
3300
3490
  doAnchoring();
3301
3491
  currentX = currentY = 0;
@@ -3344,6 +3534,12 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3344
3534
  dyAttr = currentElem._dy[index],
3345
3535
  rotAttr = currentElem._rot[index],
3346
3536
  continuous = !(w === 0 && j === 0);
3537
+ if (letterSpacing !== 0) {
3538
+ continuous = false;
3539
+ }
3540
+ if (wordSpacing !== 0) {
3541
+ continuous = false;
3542
+ }
3347
3543
  if (xAttr !== undefined) {
3348
3544
  continuous = false;
3349
3545
  doAnchoring();
@@ -3369,6 +3565,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
3369
3565
  glyph: pos[j].glyph,
3370
3566
  rotate: Math.PI / 180 * currentElem.chooseValue(rotAttr, currentElem._defRot),
3371
3567
  x: currentX + pos[j].xOffset,
3568
+ kern: pos[j].kern,
3372
3569
  y: currentY + baseline + pos[j].yOffset,
3373
3570
  width: pos[j].width,
3374
3571
  ascent: getAscent(currentElem._font.font, currentElem._font.size),
package/js/DocMeasure.js CHANGED
@@ -86,8 +86,9 @@ class DocMeasure {
86
86
  node._width = node._minWidth = node._maxWidth = node.cover.width;
87
87
  node._height = node._minHeight = node._maxHeight = node.cover.height;
88
88
  } else {
89
- node._width = node._minWidth = node._maxWidth = node.width || dimensions.width;
90
- node._height = node.height || dimensions.height * node._width / dimensions.width;
89
+ let ratio = dimensions.width / dimensions.height;
90
+ node._width = node._minWidth = node._maxWidth = node.width || (node.height ? node.height * ratio : dimensions.width);
91
+ node._height = node.height || (node.width ? node.width / ratio : dimensions.height);
91
92
  if ((0, _variableType.isNumber)(node.maxWidth) && node.maxWidth < node._width) {
92
93
  node._width = node._minWidth = node._maxWidth = node.maxWidth;
93
94
  node._height = node._width * dimensions.height / dimensions.width;
@@ -138,6 +139,15 @@ class DocMeasure {
138
139
  this.measureImageWithDimensions(node, dimensions);
139
140
  node.font = this.styleStack.getProperty('font');
140
141
 
142
+ // SVG requires a defined width and height
143
+ if (!(0, _variableType.isNumber)(node._width) && !(0, _variableType.isNumber)(node._height)) {
144
+ throw new Error('SVG is missing defined width and height.');
145
+ } else if (!(0, _variableType.isNumber)(node._width)) {
146
+ throw new Error('SVG is missing defined width.');
147
+ } else if (!(0, _variableType.isNumber)(node._height)) {
148
+ throw new Error('SVG is missing defined height.');
149
+ }
150
+
141
151
  // scale SVG based on final dimension
142
152
  node.svg = this.svgMeasure.writeDimensions(node.svg, {
143
153
  width: node._width,
@@ -216,7 +226,7 @@ class DocMeasure {
216
226
  gapSizeForList() {
217
227
  return this.textInlines.sizeOfText('9. ', this.styleStack);
218
228
  }
219
- buildUnorderedMarker(styleStack, gapSize, type) {
229
+ buildUnorderedMarker(item, styleStack, gapSize, type) {
220
230
  function buildDisc(gapSize, color) {
221
231
  // TODO: ascender-based calculations
222
232
  let radius = gapSize.fontSize / 6;
@@ -260,7 +270,7 @@ class DocMeasure {
260
270
  };
261
271
  }
262
272
  let marker;
263
- let color = styleStack.getProperty('markerColor') || styleStack.getProperty('color') || 'black';
273
+ let color = _StyleContextStack.default.getStyleProperty(item, styleStack, 'markerColor', undefined) || styleStack.getProperty('color') || 'black';
264
274
  switch (type) {
265
275
  case 'circle':
266
276
  marker = buildCircle(gapSize, color);
@@ -280,7 +290,7 @@ class DocMeasure {
280
290
  marker._minHeight = marker._maxHeight = gapSize.height;
281
291
  return marker;
282
292
  }
283
- buildOrderedMarker(counter, styleStack, type, separator) {
293
+ buildOrderedMarker(item, counter, styleStack, type, separator) {
284
294
  function prepareAlpha(counter) {
285
295
  function toAlpha(num) {
286
296
  return (num >= 26 ? toAlpha((num / 26 >> 0) - 1) : '') + 'abcdefghijklmnopqrstuvwxyz'[num % 26 >> 0];
@@ -360,13 +370,11 @@ class DocMeasure {
360
370
  counterText += `${separator} `;
361
371
  }
362
372
  }
373
+ let markerColor = _StyleContextStack.default.getStyleProperty(item, styleStack, 'markerColor', undefined) || styleStack.getProperty('color') || 'black';
363
374
  let textArray = {
364
- text: counterText
375
+ text: counterText,
376
+ color: markerColor
365
377
  };
366
- let markerColor = styleStack.getProperty('markerColor');
367
- if (markerColor) {
368
- textArray.color = markerColor;
369
- }
370
378
  return {
371
379
  _inlines: this.textInlines.buildInlines(textArray, styleStack).items
372
380
  };
@@ -381,7 +389,7 @@ class DocMeasure {
381
389
  for (let i = 0, l = items.length; i < l; i++) {
382
390
  let item = items[i] = this.measureNode(items[i]);
383
391
  if (!item.ol && !item.ul) {
384
- item.listMarker = this.buildUnorderedMarker(style, node._gapSize, item.listType || node.type);
392
+ item.listMarker = this.buildUnorderedMarker(item, style, node._gapSize, item.listType || node.type);
385
393
  }
386
394
  node._minWidth = Math.max(node._minWidth, items[i]._minWidth + node._gapSize.width);
387
395
  node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth + node._gapSize.width);
@@ -405,7 +413,7 @@ class DocMeasure {
405
413
  let item = items[i] = this.measureNode(items[i]);
406
414
  if (!item.ol && !item.ul) {
407
415
  let counterValue = (0, _variableType.isNumber)(item.counter) ? item.counter : counter;
408
- item.listMarker = this.buildOrderedMarker(counterValue, style, item.listType || node.type, node.separator);
416
+ item.listMarker = this.buildOrderedMarker(item, counterValue, style, item.listType || node.type, node.separator);
409
417
  if (item.listMarker._inlines) {
410
418
  node._gapSize.width = Math.max(node._gapSize.width, item.listMarker._inlines[0].width);
411
419
  }
@@ -12,11 +12,18 @@ class OutputDocumentServer extends _OutputDocument.default {
12
12
  */
13
13
  async write(filename) {
14
14
  const stream = await this.getStream();
15
- return new Promise(resolve => {
16
- stream.pipe(_fs.default.createWriteStream(filename));
15
+ const writeStream = _fs.default.createWriteStream(filename);
16
+ const streamEnded = new Promise((resolve, reject) => {
17
17
  stream.on('end', resolve);
18
- stream.end();
18
+ stream.on('error', reject);
19
19
  });
20
+ const writeClosed = new Promise((resolve, reject) => {
21
+ writeStream.on('close', resolve);
22
+ writeStream.on('error', reject);
23
+ });
24
+ stream.pipe(writeStream);
25
+ stream.end();
26
+ await Promise.all([streamEnded, writeClosed]);
20
27
  }
21
28
  }
22
29
  var _default = exports.default = OutputDocumentServer;
package/js/Printer.js CHANGED
@@ -99,12 +99,11 @@ class PdfPrinter {
99
99
 
100
100
  // if pageSize.height is set to Infinity, calculate the actual height of the page that
101
101
  // was laid out using the height of each of the items in the page.
102
- if (pageSize.height === Infinity) {
103
- let pageHeight = calculatePageHeight(pages, docDefinition.pageMargins);
104
- pages.forEach(page => {
105
- page.pageSize.height = pageHeight;
106
- });
107
- }
102
+ pages.forEach(page => {
103
+ if (page.pageSize.height === Infinity) {
104
+ page.pageSize.height = calculatePageHeight(page, page.pageMargins);
105
+ }
106
+ });
108
107
  const renderer = new _Renderer.default(this.pdfKitDoc, options.progressCallback);
109
108
  renderer.renderPages(pages);
110
109
  return this.pdfKitDoc;
@@ -253,7 +252,7 @@ function embedFiles(docDefinition, pdfKitDoc) {
253
252
  }
254
253
  }
255
254
  }
256
- function calculatePageHeight(pages, margins) {
255
+ function calculatePageHeight(page, margins) {
257
256
  function getItemHeight(item) {
258
257
  if (typeof item.item.getHeight === 'function') {
259
258
  return item.item.getHeight();
@@ -277,13 +276,11 @@ function calculatePageHeight(pages, margins) {
277
276
  }
278
277
  let fixedMargins = (0, _PageSize.normalizePageMargin)(margins || 40);
279
278
  let height = fixedMargins.top;
280
- pages.forEach(page => {
281
- page.items.forEach(item => {
282
- let bottomPosition = getBottomPosition(item);
283
- if (bottomPosition > height) {
284
- height = bottomPosition;
285
- }
286
- });
279
+ page.items.forEach(item => {
280
+ let bottomPosition = getBottomPosition(item);
281
+ if (bottomPosition > height) {
282
+ height = bottomPosition;
283
+ }
287
284
  });
288
285
  height += fixedMargins.bottom;
289
286
  return height;
package/js/Renderer.js CHANGED
@@ -127,7 +127,7 @@ class Renderer {
127
127
  textDecorator.drawBackground(line, x, y);
128
128
 
129
129
  //TODO: line.optimizeInlines();
130
- //TOOD: lines without differently styled inlines should be written to pdf as one stream
130
+ //TODO: lines without differently styled inlines should be written to pdf as one stream
131
131
  for (let i = 0, l = line.inlines.length; i < l; i++) {
132
132
  let inline = line.inlines[i];
133
133
  let shiftToBaseline = lineHeight - inline.font.ascender / 1000 * inline.fontSize - descent;
@@ -312,11 +312,13 @@ class Renderer {
312
312
  }
313
313
  }
314
314
  renderSVG(svg) {
315
- let options = Object.assign({
315
+ let options = {
316
316
  width: svg._width,
317
317
  height: svg._height,
318
- assumePt: true
319
- }, svg.options);
318
+ assumePt: true,
319
+ useCSS: !(0, _variableType.isString)(svg.svg),
320
+ ...svg.options
321
+ };
320
322
  options.fontCallback = (family, bold, italic) => {
321
323
  let fontsFamily = family.split(',').map(f => f.trim().replace(/('|")/g, ''));
322
324
  let font = findFont(this.pdfDocument.fonts, fontsFamily, svg.font || 'Roboto');