pdfkit 0.12.3 → 0.14.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.
package/js/pdfkit.js CHANGED
@@ -401,6 +401,11 @@ class PDFPage {
401
401
  return data.Pattern != null ? data.Pattern : data.Pattern = {};
402
402
  }
403
403
 
404
+ get colorSpaces() {
405
+ const data = this.resources.data;
406
+ return data.ColorSpace || (data.ColorSpace = {});
407
+ }
408
+
404
409
  get annotations() {
405
410
  const data = this.dictionary.data;
406
411
  return data.Annots != null ? data.Annots : data.Annots = [];
@@ -1462,7 +1467,7 @@ class PDFGradient {
1462
1467
  return pattern;
1463
1468
  }
1464
1469
 
1465
- apply(op) {
1470
+ apply(stroke) {
1466
1471
  // apply gradient transform to existing document ctm
1467
1472
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1468
1473
  const [m11, m12, m21, m22, dx, dy] = this.transform;
@@ -1472,6 +1477,9 @@ class PDFGradient {
1472
1477
  this.embed(m);
1473
1478
  }
1474
1479
 
1480
+ this.doc._setColorSpace('Pattern', stroke);
1481
+
1482
+ const op = stroke ? 'SCN' : 'scn';
1475
1483
  return this.doc.addContent(`/${this.id} ${op}`);
1476
1484
  }
1477
1485
 
@@ -1536,24 +1544,119 @@ var Gradient = {
1536
1544
  PDFRadialGradient
1537
1545
  };
1538
1546
 
1547
+ /*
1548
+ PDF tiling pattern support. Uncolored only.
1549
+ */
1550
+ const underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB'];
1551
+
1552
+ class PDFTilingPattern {
1553
+ constructor(doc, bBox, xStep, yStep, stream) {
1554
+ this.doc = doc;
1555
+ this.bBox = bBox;
1556
+ this.xStep = xStep;
1557
+ this.yStep = yStep;
1558
+ this.stream = stream;
1559
+ }
1560
+
1561
+ createPattern() {
1562
+ // no resources needed for our current usage
1563
+ // required entry
1564
+ const resources = this.doc.ref();
1565
+ resources.end(); // apply default transform matrix (flipped in the default doc._ctm)
1566
+ // see document.js & gradient.js
1567
+
1568
+ const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1569
+ const [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0];
1570
+ const m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5];
1571
+ const pattern = this.doc.ref({
1572
+ Type: 'Pattern',
1573
+ PatternType: 1,
1574
+ // tiling
1575
+ PaintType: 2,
1576
+ // 1-colored, 2-uncolored
1577
+ TilingType: 2,
1578
+ // 2-no distortion
1579
+ BBox: this.bBox,
1580
+ XStep: this.xStep,
1581
+ YStep: this.yStep,
1582
+ Matrix: m.map(v => +v.toFixed(5)),
1583
+ Resources: resources
1584
+ });
1585
+ pattern.end(this.stream);
1586
+ return pattern;
1587
+ }
1588
+
1589
+ embedPatternColorSpaces() {
1590
+ // map each pattern to an underlying color space
1591
+ // and embed on each page
1592
+ underlyingColorSpaces.forEach(csName => {
1593
+ const csId = this.getPatternColorSpaceId(csName);
1594
+ if (this.doc.page.colorSpaces[csId]) return;
1595
+ const cs = this.doc.ref(['Pattern', csName]);
1596
+ cs.end();
1597
+ this.doc.page.colorSpaces[csId] = cs;
1598
+ });
1599
+ }
1600
+
1601
+ getPatternColorSpaceId(underlyingColorspace) {
1602
+ return `CsP${underlyingColorspace}`;
1603
+ }
1604
+
1605
+ embed() {
1606
+ if (!this.id) {
1607
+ this.doc._patternCount = this.doc._patternCount + 1;
1608
+ this.id = 'P' + this.doc._patternCount;
1609
+ this.pattern = this.createPattern();
1610
+ } // patterns are embedded in each page
1611
+
1612
+
1613
+ if (!this.doc.page.patterns[this.id]) {
1614
+ this.doc.page.patterns[this.id] = this.pattern;
1615
+ }
1616
+ }
1617
+
1618
+ apply(stroke, patternColor) {
1619
+ // do any embedding/creating that might be needed
1620
+ this.embedPatternColorSpaces();
1621
+ this.embed();
1622
+
1623
+ const normalizedColor = this.doc._normalizeColor(patternColor);
1624
+
1625
+ if (!normalizedColor) throw Error(`invalid pattern color. (value: ${patternColor})`); // select one of the pattern color spaces
1626
+
1627
+ const csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor));
1628
+
1629
+ this.doc._setColorSpace(csId, stroke); // stroke/fill using the pattern and color (in the above underlying color space)
1630
+
1631
+
1632
+ const op = stroke ? 'SCN' : 'scn';
1633
+ return this.doc.addContent(`${normalizedColor.join(' ')} /${this.id} ${op}`);
1634
+ }
1635
+
1636
+ }
1637
+
1638
+ var pattern = {
1639
+ PDFTilingPattern
1640
+ };
1641
+
1539
1642
  const {
1540
1643
  PDFGradient: PDFGradient$1,
1541
1644
  PDFLinearGradient: PDFLinearGradient$1,
1542
1645
  PDFRadialGradient: PDFRadialGradient$1
1543
1646
  } = Gradient;
1647
+ const {
1648
+ PDFTilingPattern: PDFTilingPattern$1
1649
+ } = pattern;
1544
1650
  var ColorMixin = {
1545
1651
  initColor() {
1546
1652
  // The opacity dictionaries
1547
1653
  this._opacityRegistry = {};
1548
1654
  this._opacityCount = 0;
1655
+ this._patternCount = 0;
1549
1656
  return this._gradCount = 0;
1550
1657
  },
1551
1658
 
1552
1659
  _normalizeColor(color) {
1553
- if (color instanceof PDFGradient$1) {
1554
- return color;
1555
- }
1556
-
1557
1660
  if (typeof color === 'string') {
1558
1661
  if (color.charAt(0) === '#') {
1559
1662
  if (color.length === 4) {
@@ -1582,6 +1685,19 @@ var ColorMixin = {
1582
1685
  },
1583
1686
 
1584
1687
  _setColor(color, stroke) {
1688
+ if (color instanceof PDFGradient$1) {
1689
+ color.apply(stroke);
1690
+ return true; // see if tiling pattern, decode & apply it it
1691
+ } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern$1) {
1692
+ color[0].apply(stroke, color[1]);
1693
+ return true;
1694
+ } // any other case should be a normal color and not a pattern
1695
+
1696
+
1697
+ return this._setColorCore(color, stroke);
1698
+ },
1699
+
1700
+ _setColorCore(color, stroke) {
1585
1701
  color = this._normalizeColor(color);
1586
1702
 
1587
1703
  if (!color) {
@@ -1590,19 +1706,12 @@ var ColorMixin = {
1590
1706
 
1591
1707
  const op = stroke ? 'SCN' : 'scn';
1592
1708
 
1593
- if (color instanceof PDFGradient$1) {
1594
- this._setColorSpace('Pattern', stroke);
1709
+ const space = this._getColorSpace(color);
1595
1710
 
1596
- color.apply(op);
1597
- } else {
1598
- const space = color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
1599
-
1600
- this._setColorSpace(space, stroke);
1601
-
1602
- color = color.join(' ');
1603
- this.addContent(`${color} ${op}`);
1604
- }
1711
+ this._setColorSpace(space, stroke);
1605
1712
 
1713
+ color = color.join(' ');
1714
+ this.addContent(`${color} ${op}`);
1606
1715
  return true;
1607
1716
  },
1608
1717
 
@@ -1611,6 +1720,10 @@ var ColorMixin = {
1611
1720
  return this.addContent(`/${space} ${op}`);
1612
1721
  },
1613
1722
 
1723
+ _getColorSpace(color) {
1724
+ return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
1725
+ },
1726
+
1614
1727
  fillColor(color, opacity) {
1615
1728
  const set = this._setColor(color, false);
1616
1729
 
@@ -1701,6 +1814,10 @@ var ColorMixin = {
1701
1814
 
1702
1815
  radialGradient(x1, y1, r1, x2, y2, r2) {
1703
1816
  return new PDFRadialGradient$1(this, x1, y1, r1, x2, y2, r2);
1817
+ },
1818
+
1819
+ pattern(bbox, xStep, yStep, stream) {
1820
+ return new PDFTilingPattern$1(this, bbox, xStep, yStep, stream);
1704
1821
  }
1705
1822
 
1706
1823
  };
@@ -3173,6 +3290,14 @@ class EmbeddedFont extends PDFFont {
3173
3290
  descriptor.data.FontFile2 = fontFile;
3174
3291
  }
3175
3292
 
3293
+ if (this.document.subset) {
3294
+ const CIDSet = Buffer.from('FFFFFFFFC0', 'hex');
3295
+ const CIDSetRef = this.document.ref();
3296
+ CIDSetRef.write(CIDSet);
3297
+ CIDSetRef.end();
3298
+ descriptor.data.CIDSet = CIDSetRef;
3299
+ }
3300
+
3176
3301
  descriptor.end();
3177
3302
  const descendantFontData = {
3178
3303
  Type: 'Font',
@@ -5944,6 +6069,219 @@ function isEqual(a, b) {
5944
6069
  return a.Subtype === b.Subtype && a.Params.CheckSum.toString() === b.Params.CheckSum.toString() && a.Params.Size === b.Params.Size && a.Params.CreationDate === b.Params.CreationDate && a.Params.ModDate === b.Params.ModDate;
5945
6070
  }
5946
6071
 
6072
+ var PDFA = {
6073
+ initPDFA(pSubset) {
6074
+ if (pSubset.charAt(pSubset.length - 3) === '-') {
6075
+ this.subset_conformance = pSubset.charAt(pSubset.length - 1).toUpperCase();
6076
+ this.subset = parseInt(pSubset.charAt(pSubset.length - 2));
6077
+ } else {
6078
+ // Default to Basic conformance when user doesn't specify
6079
+ this.subset_conformance = 'B';
6080
+ this.subset = parseInt(pSubset.charAt(pSubset.length - 1));
6081
+ }
6082
+ },
6083
+
6084
+ endSubset() {
6085
+ this._addPdfaMetadata();
6086
+
6087
+ const jsPath = `${__dirname}/data/sRGB_IEC61966_2_1.icc`;
6088
+ const jestPath = `${__dirname}/../color_profiles/sRGB_IEC61966_2_1.icc`;
6089
+
6090
+ this._addColorOutputIntent(fs.existsSync(jsPath) ? jsPath : jestPath);
6091
+ },
6092
+
6093
+ _addColorOutputIntent(pICCPath) {
6094
+ const iccProfile = fs.readFileSync(pICCPath);
6095
+ const colorProfileRef = this.ref({
6096
+ Length: iccProfile.length,
6097
+ N: 3
6098
+ });
6099
+ colorProfileRef.write(iccProfile);
6100
+ colorProfileRef.end();
6101
+ const intentRef = this.ref({
6102
+ Type: 'OutputIntent',
6103
+ S: 'GTS_PDFA1',
6104
+ Info: new String('sRGB IEC61966-2.1'),
6105
+ OutputConditionIdentifier: new String('sRGB IEC61966-2.1'),
6106
+ DestOutputProfile: colorProfileRef
6107
+ });
6108
+ intentRef.end();
6109
+ this._root.data.OutputIntents = [intentRef];
6110
+ },
6111
+
6112
+ _getPdfaid() {
6113
+ return `
6114
+ <rdf:Description xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/" rdf:about="">
6115
+ <pdfaid:part>${this.subset}</pdfaid:part>
6116
+ <pdfaid:conformance>${this.subset_conformance}</pdfaid:conformance>
6117
+ </rdf:Description>
6118
+ `;
6119
+ },
6120
+
6121
+ _addPdfaMetadata() {
6122
+ this.appendXML(this._getPdfaid());
6123
+ }
6124
+
6125
+ };
6126
+
6127
+ var SubsetMixin = {
6128
+ _importSubset(subset) {
6129
+ Object.assign(this, subset);
6130
+ },
6131
+
6132
+ initSubset(options) {
6133
+ switch (options.subset) {
6134
+ case 'PDF/A-1':
6135
+ case 'PDF/A-1a':
6136
+ case 'PDF/A-1b':
6137
+ case 'PDF/A-2':
6138
+ case 'PDF/A-2a':
6139
+ case 'PDF/A-2b':
6140
+ case 'PDF/A-3':
6141
+ case 'PDF/A-3a':
6142
+ case 'PDF/A-3b':
6143
+ this._importSubset(PDFA);
6144
+
6145
+ this.initPDFA(options.subset);
6146
+ break;
6147
+ }
6148
+ }
6149
+
6150
+ };
6151
+
6152
+ class PDFMetadata {
6153
+ constructor() {
6154
+ this._metadata = `
6155
+ <?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?>
6156
+ <x:xmpmeta xmlns:x="adobe:ns:meta/">
6157
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
6158
+ `;
6159
+ }
6160
+
6161
+ _closeTags() {
6162
+ this._metadata = this._metadata.concat(`
6163
+ </rdf:RDF>
6164
+ </x:xmpmeta>
6165
+ <?xpacket end="w"?>
6166
+ `);
6167
+ }
6168
+
6169
+ append(xml, newline = true) {
6170
+ this._metadata = this._metadata.concat(xml);
6171
+ if (newline) this._metadata = this._metadata.concat('\n');
6172
+ }
6173
+
6174
+ getXML() {
6175
+ return this._metadata;
6176
+ }
6177
+
6178
+ getLength() {
6179
+ return this._metadata.length;
6180
+ }
6181
+
6182
+ end() {
6183
+ this._closeTags();
6184
+
6185
+ this._metadata = this._metadata.trim();
6186
+ }
6187
+
6188
+ }
6189
+
6190
+ var MetadataMixin = {
6191
+ initMetadata() {
6192
+ this.metadata = new PDFMetadata();
6193
+ },
6194
+
6195
+ appendXML(xml, newline = true) {
6196
+ this.metadata.append(xml, newline);
6197
+ },
6198
+
6199
+ _addInfo() {
6200
+ this.appendXML(`
6201
+ <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
6202
+ <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + "Z"}</xmp:CreateDate>
6203
+ <xmp:CreatorTool>${this.info.Creator}</xmp:CreatorTool>
6204
+ </rdf:Description>
6205
+ `);
6206
+
6207
+ if (this.info.Title || this.info.Author || this.info.Subject) {
6208
+ this.appendXML(`
6209
+ <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
6210
+ `);
6211
+
6212
+ if (this.info.Title) {
6213
+ this.appendXML(`
6214
+ <dc:title>
6215
+ <rdf:Alt>
6216
+ <rdf:li xml:lang="x-default">${this.info.Title}</rdf:li>
6217
+ </rdf:Alt>
6218
+ </dc:title>
6219
+ `);
6220
+ }
6221
+
6222
+ if (this.info.Author) {
6223
+ this.appendXML(`
6224
+ <dc:creator>
6225
+ <rdf:Seq>
6226
+ <rdf:li>${this.info.Author}</rdf:li>
6227
+ </rdf:Seq>
6228
+ </dc:creator>
6229
+ `);
6230
+ }
6231
+
6232
+ if (this.info.Subject) {
6233
+ this.appendXML(`
6234
+ <dc:description>
6235
+ <rdf:Alt>
6236
+ <rdf:li xml:lang="x-default">${this.info.Subject}</rdf:li>
6237
+ </rdf:Alt>
6238
+ </dc:description>
6239
+ `);
6240
+ }
6241
+
6242
+ this.appendXML(`
6243
+ </rdf:Description>
6244
+ `);
6245
+ }
6246
+
6247
+ this.appendXML(`
6248
+ <rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
6249
+ <pdf:Producer>${this.info.Creator}</pdf:Producer>`, false);
6250
+
6251
+ if (this.info.Keywords) {
6252
+ this.appendXML(`
6253
+ <pdf:Keywords>${this.info.Keywords}</pdf:Keywords>`, false);
6254
+ }
6255
+
6256
+ this.appendXML(`
6257
+ </rdf:Description>
6258
+ `);
6259
+ },
6260
+
6261
+ endMetadata() {
6262
+ this._addInfo();
6263
+
6264
+ this.metadata.end();
6265
+ /*
6266
+ Metadata was introduced in PDF 1.4, so adding it to 1.3
6267
+ will likely only take up more space.
6268
+ */
6269
+
6270
+ if (this.version != 1.3) {
6271
+ this.metadataRef = this.ref({
6272
+ length: this.metadata.getLength(),
6273
+ Type: 'Metadata',
6274
+ Subtype: 'XML'
6275
+ });
6276
+ this.metadataRef.compress = false;
6277
+ this.metadataRef.write(Buffer.from(this.metadata.getXML(), 'utf-8'));
6278
+ this.metadataRef.end();
6279
+ this._root.data.Metadata = this.metadataRef;
6280
+ }
6281
+ }
6282
+
6283
+ };
6284
+
5947
6285
  /*
5948
6286
  PDFDocument - represents an entire PDF document
5949
6287
  By Devon Govett
@@ -6007,13 +6345,15 @@ class PDFDocument extends stream.Readable {
6007
6345
 
6008
6346
  this.page = null; // Initialize mixins
6009
6347
 
6348
+ this.initMetadata();
6010
6349
  this.initColor();
6011
6350
  this.initVector();
6012
6351
  this.initFonts(options.font);
6013
6352
  this.initText();
6014
6353
  this.initImages();
6015
6354
  this.initOutline();
6016
- this.initMarkings(options); // Initialize the metadata
6355
+ this.initMarkings(options);
6356
+ this.initSubset(options); // Initialize the metadata
6017
6357
 
6018
6358
  this.info = {
6019
6359
  Producer: 'PDFKit',
@@ -6234,6 +6574,12 @@ Please pipe the document into a Node stream.\
6234
6574
  this.endOutline();
6235
6575
  this.endMarkings();
6236
6576
 
6577
+ if (this.subset) {
6578
+ this.endSubset();
6579
+ }
6580
+
6581
+ this.endMetadata();
6582
+
6237
6583
  this._root.end();
6238
6584
 
6239
6585
  this._root.data.Pages.end();
@@ -6309,6 +6655,7 @@ const mixin = methods => {
6309
6655
  Object.assign(PDFDocument.prototype, methods);
6310
6656
  };
6311
6657
 
6658
+ mixin(MetadataMixin);
6312
6659
  mixin(ColorMixin);
6313
6660
  mixin(VectorMixin);
6314
6661
  mixin(FontsMixin);
@@ -6319,6 +6666,7 @@ mixin(OutlineMixin);
6319
6666
  mixin(MarkingsMixin);
6320
6667
  mixin(AcroFormMixin);
6321
6668
  mixin(AttachmentsMixin);
6669
+ mixin(SubsetMixin);
6322
6670
  PDFDocument.LineWrapper = LineWrapper;
6323
6671
 
6324
6672
  module.exports = PDFDocument;