pdfkit 0.17.0 → 0.17.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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### [v0.17.2] - 2025-08-30
6
+
7
+ - Fix rendering lists that spans across pages
8
+
9
+ ### [v0.17.1] - 2025-05-02
10
+
11
+ - Fix null values in table cells rendering as `[object Object]`
12
+ - Fix further LineWrapper precision issues
13
+ - Optmize standard font handling. Less code, less memory usage
14
+
5
15
  ### [v0.17.0] - 2025-04-12
6
16
 
7
17
  - Fix precision rounding issues in LineWrapper
package/js/pdfkit.es.js CHANGED
@@ -222,8 +222,18 @@ class PDFReference extends PDFAbstractReference {
222
222
  }
223
223
  }
224
224
 
225
+ const fArray = new Float32Array(1);
226
+ const uArray = new Uint32Array(fArray.buffer);
225
227
  function PDFNumber(n) {
226
- return Math.fround(n);
228
+ const rounded = Math.fround(n);
229
+ if (rounded <= n) return rounded;
230
+ fArray[0] = n;
231
+ if (n <= 0) {
232
+ uArray[0] += 1;
233
+ } else {
234
+ uArray[0] -= 1;
235
+ }
236
+ return fArray[0];
227
237
  }
228
238
  function normalizeSides(sides) {
229
239
  let defaultDefinition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
@@ -2155,20 +2165,12 @@ oslash ugrave uacute ucircumflex
2155
2165
  udieresis yacute thorn ydieresis\
2156
2166
  `.split(/\s+/);
2157
2167
  class AFMFont {
2158
- static open(filename) {
2159
- return new AFMFont(fs.readFileSync(filename, 'utf8'));
2160
- }
2161
2168
  constructor(contents) {
2162
- this.contents = contents;
2163
2169
  this.attributes = {};
2164
2170
  this.glyphWidths = {};
2165
2171
  this.boundingBoxes = {};
2166
2172
  this.kernPairs = {};
2167
- this.parse();
2168
- this.charWidths = new Array(256);
2169
- for (let char = 0; char <= 255; char++) {
2170
- this.charWidths[char] = this.glyphWidths[characters[char]];
2171
- }
2173
+ this.parse(contents);
2172
2174
  this.bbox = this.attributes['FontBBox'].split(/\s+/).map(e => +e);
2173
2175
  this.ascender = +(this.attributes['Ascender'] || 0);
2174
2176
  this.descender = +(this.attributes['Descender'] || 0);
@@ -2176,9 +2178,9 @@ class AFMFont {
2176
2178
  this.capHeight = +(this.attributes['CapHeight'] || 0);
2177
2179
  this.lineGap = this.bbox[3] - this.bbox[1] - (this.ascender - this.descender);
2178
2180
  }
2179
- parse() {
2181
+ parse(contents) {
2180
2182
  let section = '';
2181
- for (let line of this.contents.split('\n')) {
2183
+ for (let line of contents.split('\n')) {
2182
2184
  var match;
2183
2185
  var a;
2184
2186
  if (match = line.match(/^Start(\w+)/)) {
@@ -2838,7 +2840,7 @@ class LineWrapper extends EventEmitter {
2838
2840
  });
2839
2841
  }
2840
2842
  wordWidth(word) {
2841
- return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing;
2843
+ return PDFNumber(this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing);
2842
2844
  }
2843
2845
  canFit(word, w) {
2844
2846
  if (word[word.length - 1] != SOFT_HYPHEN) {
@@ -2979,12 +2981,14 @@ class LineWrapper extends EventEmitter {
2979
2981
  }
2980
2982
  emitLine();
2981
2983
  if (PDFNumber(this.document.y + lh) > this.maxY) {
2984
+ this.emit('sectionEnd', options, this);
2982
2985
  const shouldContinue = this.nextSection();
2983
2986
  if (!shouldContinue) {
2984
2987
  wc = 0;
2985
2988
  buffer = '';
2986
2989
  return false;
2987
2990
  }
2991
+ this.emit('sectionStart', options, this);
2988
2992
  }
2989
2993
  if (bk.required) {
2990
2994
  this.spaceLeft = this.lineWidth;
@@ -3017,7 +3021,6 @@ class LineWrapper extends EventEmitter {
3017
3021
  }
3018
3022
  }
3019
3023
  nextSection(options) {
3020
- this.emit('sectionEnd', options, this);
3021
3024
  if (++this.column > this.columns) {
3022
3025
  if (this.height != null) {
3023
3026
  return false;
@@ -3036,7 +3039,6 @@ class LineWrapper extends EventEmitter {
3036
3039
  this.document.y = this.startY;
3037
3040
  this.emit('columnBreak', options, this);
3038
3041
  }
3039
- this.emit('sectionStart', options, this);
3040
3042
  return true;
3041
3043
  }
3042
3044
  }
@@ -3044,6 +3046,15 @@ class LineWrapper extends EventEmitter {
3044
3046
  const {
3045
3047
  number
3046
3048
  } = PDFObject;
3049
+ function formatListLabel(n, listType) {
3050
+ if (listType === 'numbered') {
3051
+ return `${n}.`;
3052
+ }
3053
+ var letter = String.fromCharCode((n - 1) % 26 + 65);
3054
+ var times = Math.floor((n - 1) / 26 + 1);
3055
+ var text = Array(times + 1).join(letter);
3056
+ return `${text}.`;
3057
+ }
3047
3058
  var TextMixin = {
3048
3059
  initText() {
3049
3060
  this._line = this._line.bind(this);
@@ -3221,7 +3232,7 @@ var TextMixin = {
3221
3232
  this.y = y;
3222
3233
  return height;
3223
3234
  },
3224
- list(list, x, y, options, wrapper) {
3235
+ list(list, x, y, options) {
3225
3236
  options = this._initOptions(x, y, options);
3226
3237
  const listType = options.listType || 'bullet';
3227
3238
  const unit = Math.round(this._font.ascender / 1000 * this._fontSize);
@@ -3251,19 +3262,8 @@ var TextMixin = {
3251
3262
  }
3252
3263
  };
3253
3264
  flatten(list);
3254
- const label = function (n) {
3255
- switch (listType) {
3256
- case 'numbered':
3257
- return `${n}.`;
3258
- case 'lettered':
3259
- var letter = String.fromCharCode((n - 1) % 26 + 65);
3260
- var times = Math.floor((n - 1) / 26 + 1);
3261
- var text = Array(times + 1).join(letter);
3262
- return `${text}.`;
3263
- }
3264
- };
3265
3265
  const drawListItem = function (listItem, i) {
3266
- wrapper = new LineWrapper(this, options);
3266
+ const wrapper = new LineWrapper(this, options);
3267
3267
  wrapper.on('line', this._line);
3268
3268
  level = 1;
3269
3269
  wrapper.once('firstLine', () => {
@@ -3298,7 +3298,7 @@ var TextMixin = {
3298
3298
  break;
3299
3299
  case 'numbered':
3300
3300
  case 'lettered':
3301
- var text = label(numbers[i - 1]);
3301
+ var text = formatListLabel(numbers[i - 1], listType);
3302
3302
  this._fragment(text, this.x - indent, this.y, options);
3303
3303
  break;
3304
3304
  }
@@ -3371,11 +3371,11 @@ var TextMixin = {
3371
3371
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3372
3372
  let wrapper = arguments.length > 2 ? arguments[2] : undefined;
3373
3373
  this._fragment(text, this.x, this.y, options);
3374
- const lineGap = options.lineGap || this._lineGap || 0;
3375
- if (!wrapper) {
3376
- this.x += this.widthOfString(text, options);
3377
- } else {
3374
+ if (wrapper) {
3375
+ const lineGap = options.lineGap || this._lineGap || 0;
3378
3376
  this.y += this.currentLineHeight(true) + lineGap;
3377
+ } else {
3378
+ this.x += this.widthOfString(text, options);
3379
3379
  }
3380
3380
  },
3381
3381
  _fragment(text, x, y, options) {
@@ -3762,8 +3762,8 @@ class PDFImage {
3762
3762
  } else if (src instanceof ArrayBuffer) {
3763
3763
  data = Buffer.from(new Uint8Array(src));
3764
3764
  } else {
3765
- let match;
3766
- if (match = /^data:.+?;base64,(.*)$/.exec(src)) {
3765
+ const match = /^data:.+?;base64,(.*)$/.exec(src);
3766
+ if (match) {
3767
3767
  data = Buffer.from(match[1], 'base64');
3768
3768
  } else {
3769
3769
  data = fs.readFileSync(src);
@@ -4884,8 +4884,8 @@ var AttachmentsMixin = {
4884
4884
  } else if (src instanceof ArrayBuffer) {
4885
4885
  data = Buffer.from(new Uint8Array(src));
4886
4886
  } else {
4887
- let match;
4888
- if (match = /^data:(.*?);base64,(.*)$/.exec(src)) {
4887
+ const match = /^data:(.*?);base64,(.*)$/.exec(src);
4888
+ if (match) {
4889
4889
  if (match[1]) {
4890
4890
  refBody.Subtype = match[1].replace('/', '#2F');
4891
4891
  }
@@ -5085,7 +5085,7 @@ function deepMerge(target) {
5085
5085
  }
5086
5086
  function deepClone(obj) {
5087
5087
  let result = obj;
5088
- if (typeof obj == 'object') {
5088
+ if (obj && typeof obj == 'object') {
5089
5089
  result = Array.isArray(obj) ? [] : {};
5090
5090
  for (const key in obj) result[key] = deepClone(obj[key]);
5091
5091
  }