pdfkit 0.14.0 → 0.15.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.
@@ -5,6 +5,7 @@ import CryptoJS from 'crypto-js';
5
5
  import fontkit from 'fontkit';
6
6
  import { EventEmitter } from 'events';
7
7
  import LineBreaker from 'linebreak';
8
+ import exif from 'jpeg-exif';
8
9
  import PNG from 'png-js';
9
10
 
10
11
  /*
@@ -2672,6 +2673,11 @@ var VectorMixin = {
2672
2673
 
2673
2674
  transform(m11, m12, m21, m22, dx, dy) {
2674
2675
  // keep track of the current transformation matrix
2676
+ if (m11 === 1 && m12 === 0 && m21 === 0 && m22 === 1 && dx === 0 && dy === 0) {
2677
+ // Ignore identity transforms
2678
+ return this;
2679
+ }
2680
+
2675
2681
  var m = this._ctm;
2676
2682
  var [m0, m1, m2, m3, m4, m5] = m;
2677
2683
  m[0] = m0 * m11 + m2 * m12;
@@ -3304,7 +3310,17 @@ class EmbeddedFont extends PDFFont {
3304
3310
  entries.push("<".concat(encoded.join(' '), ">"));
3305
3311
  }
3306
3312
 
3307
- cmap.end("/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo <<\n /Registry (Adobe)\n /Ordering (UCS)\n /Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000><ffff>\nendcodespacerange\n1 beginbfrange\n<0000> <".concat(toHex(entries.length - 1), "> [").concat(entries.join(' '), "]\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend"));
3313
+ var chunkSize = 256;
3314
+ var chunks = Math.ceil(entries.length / chunkSize);
3315
+ var ranges = [];
3316
+
3317
+ for (var i = 0; i < chunks; i++) {
3318
+ var start = i * chunkSize;
3319
+ var end = Math.min((i + 1) * chunkSize, entries.length);
3320
+ ranges.push("<".concat(toHex(start), "> <").concat(toHex(end - 1), "> [").concat(entries.slice(start, end).join(' '), "]"));
3321
+ }
3322
+
3323
+ cmap.end("/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo <<\n /Registry (Adobe)\n /Ordering (UCS)\n /Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000><ffff>\nendcodespacerange\n1 beginbfrange\n".concat(ranges.join('\n'), "\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend"));
3308
3324
  return cmap;
3309
3325
  }
3310
3326
 
@@ -3433,6 +3449,9 @@ var FontsMixin = {
3433
3449
 
3434
3450
  };
3435
3451
 
3452
+ var SOFT_HYPHEN = '\u00AD';
3453
+ var HYPHEN = '-';
3454
+
3436
3455
  class LineWrapper extends EventEmitter {
3437
3456
  constructor(document, options) {
3438
3457
  super();
@@ -3503,6 +3522,14 @@ class LineWrapper extends EventEmitter {
3503
3522
  return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing;
3504
3523
  }
3505
3524
 
3525
+ canFit(word, w) {
3526
+ if (word[word.length - 1] != SOFT_HYPHEN) {
3527
+ return w <= this.spaceLeft;
3528
+ }
3529
+
3530
+ return w + this.wordWidth(HYPHEN) <= this.spaceLeft;
3531
+ }
3532
+
3506
3533
  eachWord(text, fn) {
3507
3534
  // setup a unicode line breaker
3508
3535
  var bk;
@@ -3633,13 +3660,13 @@ class LineWrapper extends EventEmitter {
3633
3660
  this.spaceLeft = this.lineWidth;
3634
3661
  }
3635
3662
 
3636
- if (w <= this.spaceLeft) {
3663
+ if (this.canFit(word, w)) {
3637
3664
  buffer += word;
3638
3665
  textWidth += w;
3639
3666
  wc++;
3640
3667
  }
3641
3668
 
3642
- if (bk.required || w > this.spaceLeft) {
3669
+ if (bk.required || !this.canFit(word, w)) {
3643
3670
  // if the user specified a max height and an ellipsis, and is about to pass the
3644
3671
  // max height and max columns after the next line, append the ellipsis
3645
3672
  var lh = this.document.currentLineHeight(true);
@@ -3676,6 +3703,12 @@ class LineWrapper extends EventEmitter {
3676
3703
  }
3677
3704
 
3678
3705
  this.emit('lastLine', options, this);
3706
+ } // Previous entry is a soft hyphen - add visible hyphen.
3707
+
3708
+
3709
+ if (buffer[buffer.length - 1] == SOFT_HYPHEN) {
3710
+ buffer = buffer.slice(0, -1) + HYPHEN;
3711
+ this.spaceLeft -= this.wordWidth(HYPHEN);
3679
3712
  }
3680
3713
 
3681
3714
  emitLine(); // if we've reached the edge of the page,
@@ -3881,8 +3914,8 @@ var TextMixin = {
3881
3914
  var flatten = function flatten(list) {
3882
3915
  var n = 1;
3883
3916
 
3884
- for (var _i = 0; _i < list.length; _i++) {
3885
- var item = list[_i];
3917
+ for (var i = 0; i < list.length; i++) {
3918
+ var item = list[i];
3886
3919
 
3887
3920
  if (Array.isArray(item)) {
3888
3921
  level++;
@@ -3914,75 +3947,82 @@ var TextMixin = {
3914
3947
  }
3915
3948
  };
3916
3949
 
3917
- wrapper = new LineWrapper(this, options);
3918
- wrapper.on('line', this._line);
3919
- level = 1;
3920
- var i = 0;
3921
- wrapper.on('firstLine', () => {
3922
- var item, itemType, labelType, bodyType;
3950
+ var drawListItem = function drawListItem(listItem) {
3951
+ wrapper = new LineWrapper(this, options);
3952
+ wrapper.on('line', this._line);
3953
+ level = 1;
3954
+ var i = 0;
3955
+ wrapper.once('firstLine', () => {
3956
+ var item, itemType, labelType, bodyType;
3923
3957
 
3924
- if (options.structParent) {
3925
- if (options.structTypes) {
3926
- [itemType, labelType, bodyType] = options.structTypes;
3927
- } else {
3928
- [itemType, labelType, bodyType] = ['LI', 'Lbl', 'LBody'];
3958
+ if (options.structParent) {
3959
+ if (options.structTypes) {
3960
+ [itemType, labelType, bodyType] = options.structTypes;
3961
+ } else {
3962
+ [itemType, labelType, bodyType] = ['LI', 'Lbl', 'LBody'];
3963
+ }
3929
3964
  }
3930
- }
3931
3965
 
3932
- if (itemType) {
3933
- item = this.struct(itemType);
3934
- options.structParent.add(item);
3935
- } else if (options.structParent) {
3936
- item = options.structParent;
3937
- }
3966
+ if (itemType) {
3967
+ item = this.struct(itemType);
3968
+ options.structParent.add(item);
3969
+ } else if (options.structParent) {
3970
+ item = options.structParent;
3971
+ }
3938
3972
 
3939
- var l;
3973
+ var l;
3940
3974
 
3941
- if ((l = levels[i++]) !== level) {
3942
- var diff = itemIndent * (l - level);
3943
- this.x += diff;
3944
- wrapper.lineWidth -= diff;
3945
- level = l;
3946
- }
3975
+ if ((l = levels[i++]) !== level) {
3976
+ var diff = itemIndent * (l - level);
3977
+ this.x += diff;
3978
+ wrapper.lineWidth -= diff;
3979
+ level = l;
3980
+ }
3947
3981
 
3948
- if (item && (labelType || bodyType)) {
3949
- item.add(this.struct(labelType || bodyType, [this.markStructureContent(labelType || bodyType)]));
3950
- }
3982
+ if (item && (labelType || bodyType)) {
3983
+ item.add(this.struct(labelType || bodyType, [this.markStructureContent(labelType || bodyType)]));
3984
+ }
3951
3985
 
3952
- switch (listType) {
3953
- case 'bullet':
3954
- this.circle(this.x - indent + r, this.y + midLine, r);
3955
- this.fill();
3956
- break;
3986
+ switch (listType) {
3987
+ case 'bullet':
3988
+ this.circle(this.x - indent + r, this.y + midLine, r);
3989
+ this.fill();
3990
+ break;
3957
3991
 
3958
- case 'numbered':
3959
- case 'lettered':
3960
- var text = label(numbers[i - 1]);
3992
+ case 'numbered':
3993
+ case 'lettered':
3994
+ var text = label(numbers[i - 1]);
3961
3995
 
3962
- this._fragment(text, this.x - indent, this.y, options);
3996
+ this._fragment(text, this.x - indent, this.y, options);
3963
3997
 
3964
- break;
3965
- }
3998
+ break;
3999
+ }
3966
4000
 
3967
- if (item && labelType && bodyType) {
3968
- item.add(this.struct(bodyType, [this.markStructureContent(bodyType)]));
3969
- }
4001
+ if (item && labelType && bodyType) {
4002
+ item.add(this.struct(bodyType, [this.markStructureContent(bodyType)]));
4003
+ }
4004
+
4005
+ if (item && item !== options.structParent) {
4006
+ item.end();
4007
+ }
4008
+ });
4009
+ wrapper.on('sectionStart', () => {
4010
+ var pos = indent + itemIndent * (level - 1);
4011
+ this.x += pos;
4012
+ return wrapper.lineWidth -= pos;
4013
+ });
4014
+ wrapper.on('sectionEnd', () => {
4015
+ var pos = indent + itemIndent * (level - 1);
4016
+ this.x -= pos;
4017
+ return wrapper.lineWidth += pos;
4018
+ });
4019
+ wrapper.wrap(listItem, options);
4020
+ };
4021
+
4022
+ for (var i = 0; i < items.length; i++) {
4023
+ drawListItem.call(this, items[i]);
4024
+ }
3970
4025
 
3971
- if (item && item !== options.structParent) {
3972
- item.end();
3973
- }
3974
- });
3975
- wrapper.on('sectionStart', () => {
3976
- var pos = indent + itemIndent * (level - 1);
3977
- this.x += pos;
3978
- return wrapper.lineWidth -= pos;
3979
- });
3980
- wrapper.on('sectionEnd', () => {
3981
- var pos = indent + itemIndent * (level - 1);
3982
- this.x -= pos;
3983
- return wrapper.lineWidth += pos;
3984
- });
3985
- wrapper.wrap(items.join('\n'), options);
3986
4026
  return this;
3987
4027
  },
3988
4028
 
@@ -4337,8 +4377,10 @@ class JPEG {
4337
4377
 
4338
4378
  if (this.data.readUInt16BE(0) !== 0xffd8) {
4339
4379
  throw 'SOI not found in JPEG';
4340
- }
4380
+ } // Parse the EXIF orientation
4381
+
4341
4382
 
4383
+ this.orientation = exif.fromBuffer(this.data).Orientation || 1;
4342
4384
  var pos = 2;
4343
4385
 
4344
4386
  while (pos < this.data.length) {
@@ -4576,7 +4618,7 @@ class PDFImage {
4576
4618
  } else {
4577
4619
  var match;
4578
4620
 
4579
- if (match = /^data:.+;base64,(.*)$/.exec(src)) {
4621
+ if (match = /^data:.+?;base64,(.*)$/.exec(src)) {
4580
4622
  data = Buffer.from(match[1], 'base64');
4581
4623
  } else {
4582
4624
  data = fs.readFileSync(src);
@@ -4606,13 +4648,15 @@ var ImagesMixin = {
4606
4648
 
4607
4649
  image(src, x, y) {
4608
4650
  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
4609
- var bh, bp, bw, image, ip, left, left1;
4651
+ var bh, bp, bw, image, ip, left, left1, rotateAngle, originX, originY;
4610
4652
 
4611
4653
  if (typeof x === 'object') {
4612
4654
  options = x;
4613
4655
  x = null;
4614
- }
4656
+ } // Ignore orientation based on document options or image options
4615
4657
 
4658
+
4659
+ var ignoreOrientation = options.ignoreOrientation || options.ignoreOrientation !== false && this.options.ignoreOrientation;
4616
4660
  x = (left = x != null ? x : options.x) != null ? left : this.x;
4617
4661
  y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
4618
4662
 
@@ -4636,24 +4680,33 @@ var ImagesMixin = {
4636
4680
  this.page.xobjects[image.label] = image.obj;
4637
4681
  }
4638
4682
 
4639
- var w = options.width || image.width;
4640
- var h = options.height || image.height;
4683
+ var {
4684
+ width,
4685
+ height
4686
+ } = image; // If EXIF orientation calls for it, swap width and height
4687
+
4688
+ if (!ignoreOrientation && image.orientation > 4) {
4689
+ [width, height] = [height, width];
4690
+ }
4691
+
4692
+ var w = options.width || width;
4693
+ var h = options.height || height;
4641
4694
 
4642
4695
  if (options.width && !options.height) {
4643
- var wp = w / image.width;
4644
- w = image.width * wp;
4645
- h = image.height * wp;
4696
+ var wp = w / width;
4697
+ w = width * wp;
4698
+ h = height * wp;
4646
4699
  } else if (options.height && !options.width) {
4647
- var hp = h / image.height;
4648
- w = image.width * hp;
4649
- h = image.height * hp;
4700
+ var hp = h / height;
4701
+ w = width * hp;
4702
+ h = height * hp;
4650
4703
  } else if (options.scale) {
4651
- w = image.width * options.scale;
4652
- h = image.height * options.scale;
4704
+ w = width * options.scale;
4705
+ h = height * options.scale;
4653
4706
  } else if (options.fit) {
4654
4707
  [bw, bh] = options.fit;
4655
4708
  bp = bw / bh;
4656
- ip = image.width / image.height;
4709
+ ip = width / height;
4657
4710
 
4658
4711
  if (ip > bp) {
4659
4712
  w = bw;
@@ -4665,7 +4718,7 @@ var ImagesMixin = {
4665
4718
  } else if (options.cover) {
4666
4719
  [bw, bh] = options.cover;
4667
4720
  bp = bw / bh;
4668
- ip = image.width / image.height;
4721
+ ip = width / height;
4669
4722
 
4670
4723
  if (ip > bp) {
4671
4724
  h = bh;
@@ -4688,6 +4741,85 @@ var ImagesMixin = {
4688
4741
  } else if (options.valign === 'bottom') {
4689
4742
  y = y + bh - h;
4690
4743
  }
4744
+ }
4745
+
4746
+ if (!ignoreOrientation) {
4747
+ switch (image.orientation) {
4748
+ // No orientation (need to flip image, though, because of the default transform matrix on the document)
4749
+ default:
4750
+ case 1:
4751
+ h = -h;
4752
+ y -= h;
4753
+ rotateAngle = 0;
4754
+ break;
4755
+ // Flip Horizontal
4756
+
4757
+ case 2:
4758
+ w = -w;
4759
+ h = -h;
4760
+ x -= w;
4761
+ y -= h;
4762
+ rotateAngle = 0;
4763
+ break;
4764
+ // Rotate 180 degrees
4765
+
4766
+ case 3:
4767
+ originX = x;
4768
+ originY = y;
4769
+ h = -h;
4770
+ x -= w;
4771
+ rotateAngle = 180;
4772
+ break;
4773
+ // Flip vertical
4774
+
4775
+ case 4:
4776
+ // Do nothing, image will be flipped
4777
+ break;
4778
+ // Flip horizontally and rotate 270 degrees CW
4779
+
4780
+ case 5:
4781
+ originX = x;
4782
+ originY = y;
4783
+ [w, h] = [h, w];
4784
+ y -= h;
4785
+ rotateAngle = 90;
4786
+ break;
4787
+ // Rotate 90 degrees CW
4788
+
4789
+ case 6:
4790
+ originX = x;
4791
+ originY = y;
4792
+ [w, h] = [h, w];
4793
+ h = -h;
4794
+ rotateAngle = 90;
4795
+ break;
4796
+ // Flip horizontally and rotate 90 degrees CW
4797
+
4798
+ case 7:
4799
+ originX = x;
4800
+ originY = y;
4801
+ [w, h] = [h, w];
4802
+ h = -h;
4803
+ w = -w;
4804
+ x -= w;
4805
+ rotateAngle = 90;
4806
+ break;
4807
+ // Rotate 270 degrees CW
4808
+
4809
+ case 8:
4810
+ originX = x;
4811
+ originY = y;
4812
+ [w, h] = [h, w];
4813
+ h = -h;
4814
+ x -= w;
4815
+ y -= h;
4816
+ rotateAngle = -90;
4817
+ break;
4818
+ }
4819
+ } else {
4820
+ h = -h;
4821
+ y -= h;
4822
+ rotateAngle = 0;
4691
4823
  } // create link annotations if the link option is given
4692
4824
 
4693
4825
 
@@ -4709,7 +4841,14 @@ var ImagesMixin = {
4709
4841
  }
4710
4842
 
4711
4843
  this.save();
4712
- this.transform(w, 0, 0, -h, x, y + h);
4844
+
4845
+ if (rotateAngle) {
4846
+ this.rotate(rotateAngle, {
4847
+ origin: [originX, originY]
4848
+ });
4849
+ }
4850
+
4851
+ this.transform(w, 0, 0, h, x, y);
4713
4852
  this.addContent("/".concat(image.label, " Do"));
4714
4853
  this.restore();
4715
4854
  return this;
@@ -5827,7 +5966,10 @@ var AcroFormMixin = {
5827
5966
  var result = 0;
5828
5967
  Object.keys(options).forEach(key => {
5829
5968
  if (FIELD_FLAGS[key]) {
5830
- result |= FIELD_FLAGS[key];
5969
+ if (options[key]) {
5970
+ result |= FIELD_FLAGS[key];
5971
+ }
5972
+
5831
5973
  delete options[key];
5832
5974
  }
5833
5975
  });
@@ -5964,7 +6106,7 @@ var AttachmentsMixin = {
5964
6106
  } else {
5965
6107
  var match;
5966
6108
 
5967
- if (match = /^data:(.*);base64,(.*)$/.exec(src)) {
6109
+ if (match = /^data:(.*?);base64,(.*)$/.exec(src)) {
5968
6110
  if (match[1]) {
5969
6111
  refBody.Subtype = match[1].replace('/', '#2F');
5970
6112
  }
@@ -6102,6 +6244,25 @@ var PDFA = {
6102
6244
 
6103
6245
  };
6104
6246
 
6247
+ var PDFUA = {
6248
+ initPDFUA() {
6249
+ this.subset = 1;
6250
+ },
6251
+
6252
+ endSubset() {
6253
+ this._addPdfuaMetadata();
6254
+ },
6255
+
6256
+ _addPdfuaMetadata() {
6257
+ this.appendXML(this._getPdfuaid());
6258
+ },
6259
+
6260
+ _getPdfuaid() {
6261
+ return "\n <rdf:Description xmlns:pdfuaid=\"http://www.aiim.org/pdfua/ns/id/\" rdf:about=\"\">\n <pdfuaid:part>".concat(this.subset, "</pdfuaid:part>\n </rdf:Description>\n ");
6262
+ }
6263
+
6264
+ };
6265
+
6105
6266
  var SubsetMixin = {
6106
6267
  _importSubset(subset) {
6107
6268
  Object.assign(this, subset);
@@ -6122,6 +6283,12 @@ var SubsetMixin = {
6122
6283
 
6123
6284
  this.initPDFA(options.subset);
6124
6285
  break;
6286
+
6287
+ case 'PDF/UA':
6288
+ this._importSubset(PDFUA);
6289
+
6290
+ this.initPDFUA();
6291
+ break;
6125
6292
  }
6126
6293
  }
6127
6294