@dereekb/firebase-server 13.25.1 → 13.27.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.
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mailgun",
3
- "version": "13.25.1",
3
+ "version": "13.27.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.25.1",
6
- "@dereekb/firebase": "13.25.1",
7
- "@dereekb/firebase-server": "13.25.1",
8
- "@dereekb/date": "13.25.1",
9
- "@dereekb/nestjs": "13.25.1",
10
- "@dereekb/model": "13.25.1",
11
- "@dereekb/rxjs": "13.25.1",
12
- "@dereekb/util": "13.25.1"
5
+ "@dereekb/analytics": "13.27.0",
6
+ "@dereekb/firebase": "13.27.0",
7
+ "@dereekb/firebase-server": "13.27.0",
8
+ "@dereekb/date": "13.27.0",
9
+ "@dereekb/nestjs": "13.27.0",
10
+ "@dereekb/model": "13.27.0",
11
+ "@dereekb/rxjs": "13.27.0",
12
+ "@dereekb/util": "13.27.0"
13
13
  },
14
14
  "exports": {
15
15
  "./package.json": "./package.json",
package/mcp/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mcp",
3
- "version": "13.25.1",
3
+ "version": "13.27.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.25.1",
6
- "@dereekb/date": "13.25.1",
7
- "@dereekb/firebase": "13.25.1",
8
- "@dereekb/firebase-server": "13.25.1",
9
- "@dereekb/firebase-server/oidc": "13.25.1",
10
- "@dereekb/model": "13.25.1",
11
- "@dereekb/nestjs": "13.25.1",
12
- "@dereekb/rxjs": "13.25.1",
13
- "@dereekb/util": "13.25.1",
14
- "@dereekb/zoho": "13.25.1",
5
+ "@dereekb/analytics": "13.27.0",
6
+ "@dereekb/date": "13.27.0",
7
+ "@dereekb/firebase": "13.27.0",
8
+ "@dereekb/firebase-server": "13.27.0",
9
+ "@dereekb/firebase-server/oidc": "13.27.0",
10
+ "@dereekb/model": "13.27.0",
11
+ "@dereekb/nestjs": "13.27.0",
12
+ "@dereekb/rxjs": "13.27.0",
13
+ "@dereekb/util": "13.27.0",
14
+ "@dereekb/zoho": "13.27.0",
15
15
  "@modelcontextprotocol/sdk": "1.29.0",
16
16
  "@nestjs/common": "^11.1.19",
17
17
  "@nestjs/core": "^11.1.19",
@@ -9,6 +9,7 @@ var common = require('@nestjs/common');
9
9
  var config = require('@nestjs/config');
10
10
  var makeError = require('make-error');
11
11
  var sharp = require('sharp');
12
+ var node_zlib = require('node:zlib');
12
13
  var pdfLib = require('@cantoo/pdf-lib');
13
14
  var archiver = require('archiver');
14
15
 
@@ -8245,7 +8246,7 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8245
8246
  * ```
8246
8247
  */ function compressImageBufferToTargetSize(input, config) {
8247
8248
  return _async_to_generator$6(function() {
8248
- var _config_maxDimension, _config_initialQuality, _config_minQuality, _config_qualityStep, _config_format, _metadata_width, _metadata_height, targetSizeBytes, maxDimension, initialQuality, minQuality, qualityStep, format, metadata, originalWidth, originalHeight, originalSizeBytes, longestSide, resizeNeeded, scale, workingWidth, workingHeight, bestBuffer, bestSize, bestQuality, bestWidth, bestHeight, hitTarget, qualities, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, quality, encoded, err, result;
8249
+ var _config_maxDimension, _config_initialQuality, _config_minQuality, _config_qualityStep, _config_format, _config_rawPixelInput, _ref, _ref1, _ref2, _ref3, targetSizeBytes, maxDimension, initialQuality, minQuality, qualityStep, format, rawPixelInput, inputMetadata, _tmp, originalWidth, originalHeight, originalSizeBytes, longestSide, resizeNeeded, scale, workingWidth, workingHeight, bestBuffer, bestSize, bestQuality, bestWidth, bestHeight, hitTarget, qualities, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, quality, encoded, err, finalChannels, _ref4, _ref5, _channels, result;
8249
8250
  return _ts_generator$6(this, function(_state) {
8250
8251
  switch(_state.label){
8251
8252
  case 0:
@@ -8255,14 +8256,28 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8255
8256
  minQuality = (_config_minQuality = config.minQuality) !== null && _config_minQuality !== void 0 ? _config_minQuality : DEFAULT_COMPRESS_IMAGE_MIN_QUALITY;
8256
8257
  qualityStep = (_config_qualityStep = config.qualityStep) !== null && _config_qualityStep !== void 0 ? _config_qualityStep : DEFAULT_COMPRESS_IMAGE_QUALITY_STEP;
8257
8258
  format = (_config_format = config.format) !== null && _config_format !== void 0 ? _config_format : DEFAULT_COMPRESS_IMAGE_FORMAT;
8259
+ rawPixelInput = (_config_rawPixelInput = config.rawPixelInput) !== null && _config_rawPixelInput !== void 0 ? _config_rawPixelInput : undefined;
8260
+ if (!(rawPixelInput == null)) return [
8261
+ 3,
8262
+ 2
8263
+ ];
8258
8264
  return [
8259
8265
  4,
8260
8266
  sharp(input).metadata()
8261
8267
  ];
8262
8268
  case 1:
8263
- metadata = _state.sent();
8264
- originalWidth = (_metadata_width = metadata.width) !== null && _metadata_width !== void 0 ? _metadata_width : 0;
8265
- originalHeight = (_metadata_height = metadata.height) !== null && _metadata_height !== void 0 ? _metadata_height : 0;
8269
+ _tmp = _state.sent();
8270
+ return [
8271
+ 3,
8272
+ 3
8273
+ ];
8274
+ case 2:
8275
+ _tmp = undefined;
8276
+ _state.label = 3;
8277
+ case 3:
8278
+ inputMetadata = _tmp;
8279
+ originalWidth = (_ref = (_ref1 = rawPixelInput === null || rawPixelInput === void 0 ? void 0 : rawPixelInput.width) !== null && _ref1 !== void 0 ? _ref1 : inputMetadata === null || inputMetadata === void 0 ? void 0 : inputMetadata.width) !== null && _ref !== void 0 ? _ref : 0;
8280
+ originalHeight = (_ref2 = (_ref3 = rawPixelInput === null || rawPixelInput === void 0 ? void 0 : rawPixelInput.height) !== null && _ref3 !== void 0 ? _ref3 : inputMetadata === null || inputMetadata === void 0 ? void 0 : inputMetadata.height) !== null && _ref2 !== void 0 ? _ref2 : 0;
8266
8281
  originalSizeBytes = input.byteLength;
8267
8282
  longestSide = Math.max(originalWidth, originalHeight);
8268
8283
  resizeNeeded = longestSide > maxDimension && longestSide > 0;
@@ -8270,14 +8285,15 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8270
8285
  workingWidth = Math.max(1, Math.round(originalWidth * scale));
8271
8286
  workingHeight = Math.max(1, Math.round(originalHeight * scale));
8272
8287
  bestBuffer = input;
8273
- bestSize = originalSizeBytes;
8288
+ // raw input must always be re-encoded, so no encoding is compared against the raw byte size
8289
+ bestSize = rawPixelInput == null ? originalSizeBytes : Number.MAX_SAFE_INTEGER;
8274
8290
  bestQuality = 100;
8275
8291
  bestWidth = originalWidth;
8276
8292
  bestHeight = originalHeight;
8277
- hitTarget = bestSize <= targetSizeBytes && !resizeNeeded;
8293
+ hitTarget = rawPixelInput == null && bestSize <= targetSizeBytes && !resizeNeeded;
8278
8294
  if (!!hitTarget) return [
8279
8295
  3,
8280
- 9
8296
+ 11
8281
8297
  ];
8282
8298
  qualities = qualityIterationSteps({
8283
8299
  format: format,
@@ -8286,20 +8302,20 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8286
8302
  qualityStep: qualityStep
8287
8303
  });
8288
8304
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
8289
- _state.label = 2;
8290
- case 2:
8305
+ _state.label = 4;
8306
+ case 4:
8291
8307
  _state.trys.push([
8292
- 2,
8293
- 7,
8294
- 8,
8295
- 9
8308
+ 4,
8309
+ 9,
8310
+ 10,
8311
+ 11
8296
8312
  ]);
8297
8313
  _iterator = qualities[Symbol.iterator]();
8298
- _state.label = 3;
8299
- case 3:
8314
+ _state.label = 5;
8315
+ case 5:
8300
8316
  if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
8301
8317
  3,
8302
- 6
8318
+ 8
8303
8319
  ];
8304
8320
  quality = _step.value;
8305
8321
  return [
@@ -8310,10 +8326,11 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8310
8326
  quality: quality,
8311
8327
  resizeNeeded: resizeNeeded,
8312
8328
  workingWidth: workingWidth,
8313
- workingHeight: workingHeight
8329
+ workingHeight: workingHeight,
8330
+ rawPixelInput: rawPixelInput
8314
8331
  })
8315
8332
  ];
8316
- case 4:
8333
+ case 6:
8317
8334
  encoded = _state.sent();
8318
8335
  if (encoded.byteLength < bestSize) {
8319
8336
  bestBuffer = encoded;
@@ -8326,30 +8343,30 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8326
8343
  hitTarget = true;
8327
8344
  return [
8328
8345
  3,
8329
- 6
8346
+ 8
8330
8347
  ];
8331
8348
  }
8332
- _state.label = 5;
8333
- case 5:
8349
+ _state.label = 7;
8350
+ case 7:
8334
8351
  _iteratorNormalCompletion = true;
8335
8352
  return [
8336
8353
  3,
8337
- 3
8354
+ 5
8338
8355
  ];
8339
- case 6:
8356
+ case 8:
8340
8357
  return [
8341
8358
  3,
8342
- 9
8359
+ 11
8343
8360
  ];
8344
- case 7:
8361
+ case 9:
8345
8362
  err = _state.sent();
8346
8363
  _didIteratorError = true;
8347
8364
  _iteratorError = err;
8348
8365
  return [
8349
8366
  3,
8350
- 9
8367
+ 11
8351
8368
  ];
8352
- case 8:
8369
+ case 10:
8353
8370
  try {
8354
8371
  if (!_iteratorNormalCompletion && _iterator.return != null) {
8355
8372
  _iterator.return();
@@ -8362,14 +8379,33 @@ var DEFAULT_COMPRESS_IMAGE_FORMAT = 'jpeg';
8362
8379
  return [
8363
8380
  7
8364
8381
  ];
8365
- case 9:
8382
+ case 11:
8383
+ if (!(bestBuffer === input)) return [
8384
+ 3,
8385
+ 12
8386
+ ];
8387
+ finalChannels = (_ref4 = (_ref5 = rawPixelInput === null || rawPixelInput === void 0 ? void 0 : rawPixelInput.channels) !== null && _ref5 !== void 0 ? _ref5 : inputMetadata === null || inputMetadata === void 0 ? void 0 : inputMetadata.channels) !== null && _ref4 !== void 0 ? _ref4 : 0;
8388
+ return [
8389
+ 3,
8390
+ 14
8391
+ ];
8392
+ case 12:
8393
+ return [
8394
+ 4,
8395
+ sharp(bestBuffer).metadata()
8396
+ ];
8397
+ case 13:
8398
+ finalChannels = (_channels = _state.sent().channels) !== null && _channels !== void 0 ? _channels : 0;
8399
+ _state.label = 14;
8400
+ case 14:
8366
8401
  result = {
8367
8402
  buffer: bestBuffer,
8368
8403
  originalSizeBytes: originalSizeBytes,
8369
- compressedSizeBytes: bestSize,
8404
+ compressedSizeBytes: bestBuffer.byteLength,
8370
8405
  finalQuality: bestQuality,
8371
8406
  finalWidth: bestWidth,
8372
8407
  finalHeight: bestHeight,
8408
+ finalChannels: finalChannels,
8373
8409
  hitTarget: hitTarget
8374
8410
  };
8375
8411
  return [
@@ -8395,11 +8431,17 @@ function qualityIterationSteps(input) {
8395
8431
  }
8396
8432
  function encodeImage(input) {
8397
8433
  return _async_to_generator$6(function() {
8398
- var pipeline, result, _;
8434
+ var _input_rawPixelInput, pipeline, result, _;
8399
8435
  return _ts_generator$6(this, function(_state) {
8400
8436
  switch(_state.label){
8401
8437
  case 0:
8402
- pipeline = sharp(input.input);
8438
+ pipeline = input.rawPixelInput == null ? sharp(input.input) : sharp(input.input, {
8439
+ raw: input.rawPixelInput
8440
+ });
8441
+ // grayscale raw input stays grayscale — sharp otherwise converts raw pipelines to sRGB on encode
8442
+ if (((_input_rawPixelInput = input.rawPixelInput) === null || _input_rawPixelInput === void 0 ? void 0 : _input_rawPixelInput.channels) === 1) {
8443
+ pipeline.toColourspace('b-w');
8444
+ }
8403
8445
  if (input.resizeNeeded) {
8404
8446
  pipeline.resize({
8405
8447
  width: input.workingWidth,
@@ -8668,10 +8710,15 @@ var DEFAULT_COMPRESS_PDF_IMAGE_SIZE_THRESHOLD_BYTES = 100 * 1024;
8668
8710
  /**
8669
8711
  * Recompresses image XObjects embedded in a PDF to shrink its overall size.
8670
8712
  *
8671
- * Only DCTDecode (JPEG) image streams are touched in this implementation — the
8672
- * dominant case for "phone photo PDFs". Images using other filters (FlateDecode,
8673
- * CCITTFax, JBIG2, JPEG2000), indexed/CMYK color spaces, or that carry a
8674
- * `/SMask` (soft mask) are skipped and counted in `imagesSkipped`.
8713
+ * Two stream kinds are recompressed to JPEG (DCTDecode):
8714
+ * - DCTDecode (JPEG) image streams — the dominant case for "phone photo PDFs".
8715
+ * - FlateDecode raster image streams (zlib-deflated raw pixels, 8 bits per
8716
+ * component, DeviceRGB/DeviceGray/CalRGB/CalGray/ICCBased color spaces, with
8717
+ * optional PNG/TIFF predictors) — the dominant case for scanner-produced PDFs.
8718
+ *
8719
+ * Images using other filters (CCITTFax, JBIG2, JPEG2000), indexed/CMYK/other
8720
+ * color spaces, non-8-bit rasters, or that carry a `/SMask` or `/Mask` are
8721
+ * skipped and counted in `imagesSkipped`.
8675
8722
  *
8676
8723
  * Throws on a malformed PDF — callers should catch and fall back to the original
8677
8724
  * bytes.
@@ -8828,7 +8875,7 @@ var DEFAULT_COMPRESS_PDF_IMAGE_SIZE_THRESHOLD_BYTES = 100 * 1024;
8828
8875
  }
8829
8876
  function processPdfStream(input) {
8830
8877
  return _async_to_generator$5(function() {
8831
- var pdfDoc, ref, obj, counters, imageMaxDimension, imageQuality, imageSizeThresholdBytes, _counters_imageStreamsByFilter_filterKey, filterKey, compressedImage;
8878
+ var pdfDoc, ref, obj, counters, imageMaxDimension, imageQuality, imageSizeThresholdBytes, _counters_imageStreamsByFilter_filterKey, filterKey, streamType, compressedImage, decoded;
8832
8879
  return _ts_generator$5(this, function(_state) {
8833
8880
  switch(_state.label){
8834
8881
  case 0:
@@ -8838,7 +8885,8 @@ function processPdfStream(input) {
8838
8885
  filterKey = imageStreamFilterKey(obj);
8839
8886
  counters.imageStreamsByFilter[filterKey] = ((_counters_imageStreamsByFilter_filterKey = counters.imageStreamsByFilter[filterKey]) !== null && _counters_imageStreamsByFilter_filterKey !== void 0 ? _counters_imageStreamsByFilter_filterKey : 0) + 1;
8840
8887
  }
8841
- if (!isCompressibleImageStream(obj)) {
8888
+ streamType = compressibleImageStreamType(obj);
8889
+ if (streamType == null) {
8842
8890
  return [
8843
8891
  2
8844
8892
  ];
@@ -8852,10 +8900,14 @@ function processPdfStream(input) {
8852
8900
  case 1:
8853
8901
  _state.trys.push([
8854
8902
  1,
8855
- 3,
8903
+ 7,
8856
8904
  ,
8857
- 4
8905
+ 8
8858
8906
  ]);
8907
+ if (!(streamType === 'jpeg')) return [
8908
+ 3,
8909
+ 3
8910
+ ];
8859
8911
  return [
8860
8912
  4,
8861
8913
  compressImageBufferToTargetSize(Buffer.from(obj.contents), {
@@ -8867,7 +8919,41 @@ function processPdfStream(input) {
8867
8919
  ];
8868
8920
  case 2:
8869
8921
  compressedImage = _state.sent();
8870
- if (compressedImage.compressedSizeBytes >= obj.contents.byteLength) {
8922
+ return [
8923
+ 3,
8924
+ 6
8925
+ ];
8926
+ case 3:
8927
+ decoded = decodeFlateRasterImage(pdfDoc, obj);
8928
+ if (!(decoded == null)) return [
8929
+ 3,
8930
+ 4
8931
+ ];
8932
+ counters.imagesSkipped += 1; // unsupported raster layout (color space, bit depth, predictor, or decode array)
8933
+ return [
8934
+ 3,
8935
+ 6
8936
+ ];
8937
+ case 4:
8938
+ return [
8939
+ 4,
8940
+ compressImageBufferToTargetSize(decoded.data, {
8941
+ targetSizeBytes: obj.contents.byteLength,
8942
+ maxDimension: imageMaxDimension,
8943
+ initialQuality: imageQuality,
8944
+ format: 'jpeg',
8945
+ rawPixelInput: {
8946
+ width: decoded.width,
8947
+ height: decoded.height,
8948
+ channels: decoded.channels
8949
+ }
8950
+ })
8951
+ ];
8952
+ case 5:
8953
+ compressedImage = _state.sent();
8954
+ _state.label = 6;
8955
+ case 6:
8956
+ if (compressedImage == null || compressedImage.compressedSizeBytes >= obj.contents.byteLength) {
8871
8957
  return [
8872
8958
  2
8873
8959
  ]; // no gain — leave it alone
@@ -8877,21 +8963,22 @@ function processPdfStream(input) {
8877
8963
  ref: ref,
8878
8964
  newImageBytes: compressedImage.buffer,
8879
8965
  newWidth: compressedImage.finalWidth,
8880
- newHeight: compressedImage.finalHeight
8966
+ newHeight: compressedImage.finalHeight,
8967
+ colorSpace: compressedImage.finalChannels === 1 ? 'DeviceGray' : 'DeviceRGB'
8881
8968
  });
8882
8969
  counters.imagesCompressed += 1;
8883
8970
  return [
8884
8971
  3,
8885
- 4
8972
+ 8
8886
8973
  ];
8887
- case 3:
8974
+ case 7:
8888
8975
  _state.sent();
8889
8976
  counters.imagesSkipped += 1;
8890
8977
  return [
8891
8978
  3,
8892
- 4
8979
+ 8
8893
8980
  ];
8894
- case 4:
8981
+ case 8:
8895
8982
  return [
8896
8983
  2
8897
8984
  ];
@@ -8935,11 +9022,28 @@ var PDF_NAME_SUBTYPE = pdfLib.PDFName.of('Subtype');
8935
9022
  var PDF_NAME_IMAGE = pdfLib.PDFName.of('Image');
8936
9023
  var PDF_NAME_FILTER = pdfLib.PDFName.of('Filter');
8937
9024
  var PDF_NAME_DCTDECODE = pdfLib.PDFName.of('DCTDecode');
9025
+ var PDF_NAME_FLATEDECODE = pdfLib.PDFName.of('FlateDecode');
8938
9026
  var PDF_NAME_SMASK = pdfLib.PDFName.of('SMask');
8939
9027
  var PDF_NAME_MASK = pdfLib.PDFName.of('Mask');
8940
- function isCompressibleImageStream(stream) {
9028
+ var PDF_NAME_WIDTH = pdfLib.PDFName.of('Width');
9029
+ var PDF_NAME_HEIGHT = pdfLib.PDFName.of('Height');
9030
+ var PDF_NAME_BITS_PER_COMPONENT = pdfLib.PDFName.of('BitsPerComponent');
9031
+ var PDF_NAME_COLOR_SPACE = pdfLib.PDFName.of('ColorSpace');
9032
+ var PDF_NAME_DECODE = pdfLib.PDFName.of('Decode');
9033
+ var PDF_NAME_IMAGE_MASK = pdfLib.PDFName.of('ImageMask');
9034
+ var PDF_NAME_DECODE_PARMS = pdfLib.PDFName.of('DecodeParms');
9035
+ var PDF_NAME_PREDICTOR = pdfLib.PDFName.of('Predictor');
9036
+ var PDF_NAME_COLORS = pdfLib.PDFName.of('Colors');
9037
+ var PDF_NAME_COLUMNS = pdfLib.PDFName.of('Columns');
9038
+ var PDF_NAME_DEVICE_RGB = pdfLib.PDFName.of('DeviceRGB');
9039
+ var PDF_NAME_DEVICE_GRAY = pdfLib.PDFName.of('DeviceGray');
9040
+ var PDF_NAME_CAL_RGB = pdfLib.PDFName.of('CalRGB');
9041
+ var PDF_NAME_CAL_GRAY = pdfLib.PDFName.of('CalGray');
9042
+ var PDF_NAME_ICC_BASED = pdfLib.PDFName.of('ICCBased');
9043
+ var PDF_NAME_N = pdfLib.PDFName.of('N');
9044
+ function compressibleImageStreamType(stream) {
8941
9045
  var dict = stream.dict;
8942
- var result = false;
9046
+ var result;
8943
9047
  // Must be Type /XObject (when present) and Subtype /Image
8944
9048
  var type = dict.get(PDF_NAME_TYPE);
8945
9049
  var typeOk = type === undefined || pdfNameEquals(type, PDF_NAME_XOBJECT);
@@ -8949,13 +9053,15 @@ function isCompressibleImageStream(stream) {
8949
9053
  // Skip images with masks; replacing them risks breaking the mask alignment.
8950
9054
  var hasMask = dict.get(PDF_NAME_SMASK) !== undefined || dict.get(PDF_NAME_MASK) !== undefined;
8951
9055
  if (subtypeOk && !hasMask) {
8952
- // Only DCTDecode (JPEG) streams in v1.
8953
9056
  var filter = dict.get(PDF_NAME_FILTER);
8954
- if (filter !== undefined) {
8955
- if (_instanceof$1(filter, pdfLib.PDFArray)) {
8956
- result = filter.size() === 1 && pdfNameEquals(filter.get(0), PDF_NAME_DCTDECODE);
8957
- } else {
8958
- result = pdfNameEquals(filter, PDF_NAME_DCTDECODE);
9057
+ if (_instanceof$1(filter, pdfLib.PDFArray) && filter.size() === 1) {
9058
+ filter = filter.get(0);
9059
+ }
9060
+ if (_instanceof$1(filter, pdfLib.PDFName)) {
9061
+ if (pdfNameEquals(filter, PDF_NAME_DCTDECODE)) {
9062
+ result = 'jpeg';
9063
+ } else if (pdfNameEquals(filter, PDF_NAME_FLATEDECODE)) {
9064
+ result = 'flateRaster';
8959
9065
  }
8960
9066
  }
8961
9067
  }
@@ -8965,15 +9071,217 @@ function isCompressibleImageStream(stream) {
8965
9071
  function pdfNameEquals(candidate, name) {
8966
9072
  return _instanceof$1(candidate, pdfLib.PDFName) && candidate.asString() === name.asString();
8967
9073
  }
9074
+ function lookupPdfObject(pdfDoc, value) {
9075
+ return _instanceof$1(value, pdfLib.PDFRef) ? pdfDoc.context.lookup(value) : value;
9076
+ }
9077
+ function dictNumber(dict, name) {
9078
+ var value = dict.get(name);
9079
+ return _instanceof$1(value, pdfLib.PDFNumber) ? value.asNumber() : undefined;
9080
+ }
9081
+ function resolveImageColorSpaceChannels(pdfDoc, colorSpaceValue) {
9082
+ var colorSpace = lookupPdfObject(pdfDoc, colorSpaceValue);
9083
+ var result;
9084
+ if (_instanceof$1(colorSpace, pdfLib.PDFName)) {
9085
+ if (pdfNameEquals(colorSpace, PDF_NAME_DEVICE_RGB) || pdfNameEquals(colorSpace, PDF_NAME_CAL_RGB)) {
9086
+ result = 3;
9087
+ } else if (pdfNameEquals(colorSpace, PDF_NAME_DEVICE_GRAY) || pdfNameEquals(colorSpace, PDF_NAME_CAL_GRAY)) {
9088
+ result = 1;
9089
+ }
9090
+ } else if (_instanceof$1(colorSpace, pdfLib.PDFArray) && colorSpace.size() > 0) {
9091
+ var family = colorSpace.get(0);
9092
+ if (pdfNameEquals(family, PDF_NAME_ICC_BASED) && colorSpace.size() > 1) {
9093
+ var iccStream = lookupPdfObject(pdfDoc, colorSpace.get(1));
9094
+ if (_instanceof$1(iccStream, pdfLib.PDFStream)) {
9095
+ var componentCount = dictNumber(iccStream.dict, PDF_NAME_N);
9096
+ if (componentCount === 3) {
9097
+ result = 3;
9098
+ } else if (componentCount === 1) {
9099
+ result = 1;
9100
+ }
9101
+ }
9102
+ } else if (pdfNameEquals(family, PDF_NAME_CAL_RGB)) {
9103
+ result = 3;
9104
+ } else if (pdfNameEquals(family, PDF_NAME_CAL_GRAY)) {
9105
+ result = 1;
9106
+ }
9107
+ }
9108
+ return result;
9109
+ }
9110
+ function readFlatePredictorParams(pdfDoc, dict) {
9111
+ var _ref, _ref1, _ref2, _ref3;
9112
+ var parmsValue = lookupPdfObject(pdfDoc, dict.get(PDF_NAME_DECODE_PARMS));
9113
+ // A filter array pairs with a DecodeParms array; a single flate filter uses the first entry.
9114
+ if (_instanceof$1(parmsValue, pdfLib.PDFArray)) {
9115
+ parmsValue = lookupPdfObject(pdfDoc, parmsValue.size() > 0 ? parmsValue.get(0) : undefined);
9116
+ }
9117
+ var parms = _instanceof$1(parmsValue, pdfLib.PDFDict) ? parmsValue : undefined;
9118
+ var result = {
9119
+ predictor: (_ref = parms ? dictNumber(parms, PDF_NAME_PREDICTOR) : undefined) !== null && _ref !== void 0 ? _ref : 1,
9120
+ colors: (_ref1 = parms ? dictNumber(parms, PDF_NAME_COLORS) : undefined) !== null && _ref1 !== void 0 ? _ref1 : 1,
9121
+ columns: (_ref2 = parms ? dictNumber(parms, PDF_NAME_COLUMNS) : undefined) !== null && _ref2 !== void 0 ? _ref2 : 1,
9122
+ bitsPerComponent: (_ref3 = parms ? dictNumber(parms, PDF_NAME_BITS_PER_COMPONENT) : undefined) !== null && _ref3 !== void 0 ? _ref3 : 8
9123
+ };
9124
+ return result;
9125
+ }
9126
+ function unpredictFlateImageData(data, params) {
9127
+ var predictor = params.predictor, colors = params.colors, columns = params.columns, bitsPerComponent = params.bitsPerComponent;
9128
+ var result;
9129
+ if (predictor === 1) {
9130
+ result = data;
9131
+ } else if (bitsPerComponent !== 8) {
9132
+ result = undefined; // only 8-bit predictor data is supported
9133
+ } else if (predictor === 2) {
9134
+ result = unpredictTiffImageData(data, colors, columns);
9135
+ } else if (predictor >= 10 && predictor <= 15) {
9136
+ result = unpredictPngImageData(data, colors, columns);
9137
+ }
9138
+ return result;
9139
+ }
9140
+ /**
9141
+ * Reverses TIFF Predictor 2 (horizontal differencing) on 8-bit sample data, in place.
9142
+ *
9143
+ * @param data - Predictor-encoded sample bytes.
9144
+ * @param colors - Samples per pixel.
9145
+ * @param columns - Pixels per row.
9146
+ * @returns The same buffer with the differencing undone, or `undefined` when the length is not a whole number of rows.
9147
+ */ function unpredictTiffImageData(data, colors, columns) {
9148
+ var rowLength = colors * columns;
9149
+ var result;
9150
+ if (rowLength > 0 && data.byteLength % rowLength === 0) {
9151
+ for(var rowStart = 0; rowStart < data.byteLength; rowStart += rowLength){
9152
+ for(var i = colors; i < rowLength; i += 1){
9153
+ data[rowStart + i] = data[rowStart + i] + data[rowStart + i - colors] & 0xff;
9154
+ }
9155
+ }
9156
+ result = data;
9157
+ }
9158
+ return result;
9159
+ }
9160
+ /**
9161
+ * Reverses PNG row filters (predictors 10–15) on 8-bit sample data.
9162
+ *
9163
+ * @param data - Predictor-encoded bytes: each row is one filter-type byte followed by `colors * columns` filtered bytes.
9164
+ * @param colors - Samples per pixel.
9165
+ * @param columns - Pixels per row.
9166
+ * @returns The unfiltered raster bytes, or `undefined` for a malformed length or an unknown filter type.
9167
+ */ function unpredictPngImageData(data, colors, columns) {
9168
+ var rowLength = colors * columns;
9169
+ var encodedRowLength = rowLength + 1;
9170
+ var result;
9171
+ if (rowLength > 0 && data.byteLength % encodedRowLength === 0) {
9172
+ var rows = data.byteLength / encodedRowLength;
9173
+ var output = Buffer.alloc(rows * rowLength);
9174
+ var valid = true;
9175
+ for(var row = 0; row < rows && valid; row += 1){
9176
+ valid = unpredictPngRow({
9177
+ filterType: data[row * encodedRowLength],
9178
+ rowIn: data.subarray(row * encodedRowLength + 1, (row + 1) * encodedRowLength),
9179
+ output: output,
9180
+ outStart: row * rowLength,
9181
+ prevStart: (row - 1) * rowLength,
9182
+ colors: colors,
9183
+ isFirstRow: row === 0
9184
+ });
9185
+ }
9186
+ result = valid ? output : undefined;
9187
+ }
9188
+ return result;
9189
+ }
9190
+ function unpredictPngRow(input) {
9191
+ var filterType = input.filterType, rowIn = input.rowIn, output = input.output, outStart = input.outStart, prevStart = input.prevStart, colors = input.colors, isFirstRow = input.isFirstRow;
9192
+ var valid = true;
9193
+ for(var i = 0; i < rowIn.length && valid; i += 1){
9194
+ var left = i >= colors ? output[outStart + i - colors] : 0;
9195
+ var up = isFirstRow ? 0 : output[prevStart + i];
9196
+ var value = 0;
9197
+ switch(filterType){
9198
+ case 0:
9199
+ value = rowIn[i];
9200
+ break;
9201
+ case 1:
9202
+ value = rowIn[i] + left;
9203
+ break;
9204
+ case 2:
9205
+ value = rowIn[i] + up;
9206
+ break;
9207
+ case 3:
9208
+ value = rowIn[i] + (left + up >> 1);
9209
+ break;
9210
+ case 4:
9211
+ {
9212
+ // Paeth
9213
+ var upLeft = !isFirstRow && i >= colors ? output[prevStart + i - colors] : 0;
9214
+ value = rowIn[i] + paethPredictor(left, up, upLeft);
9215
+ break;
9216
+ }
9217
+ default:
9218
+ valid = false;
9219
+ }
9220
+ output[outStart + i] = value & 0xff;
9221
+ }
9222
+ return valid;
9223
+ }
9224
+ function paethPredictor(left, up, upLeft) {
9225
+ var initial = left + up - upLeft;
9226
+ var distanceLeft = Math.abs(initial - left);
9227
+ var distanceUp = Math.abs(initial - up);
9228
+ var distanceUpLeft = Math.abs(initial - upLeft);
9229
+ var result;
9230
+ if (distanceLeft <= distanceUp && distanceLeft <= distanceUpLeft) {
9231
+ result = left;
9232
+ } else if (distanceUp <= distanceUpLeft) {
9233
+ result = up;
9234
+ } else {
9235
+ result = upLeft;
9236
+ }
9237
+ return result;
9238
+ }
9239
+ /**
9240
+ * Inflates a FlateDecode image stream into raw interleaved 8-bit pixel data.
9241
+ *
9242
+ * @param pdfDoc - Document the stream belongs to, used to resolve indirect references.
9243
+ * @param stream - The FlateDecode image XObject stream.
9244
+ * @returns The decoded raster, or `undefined` when the stream's raster layout is
9245
+ * unsupported: a non-8-bit depth, an image mask, a custom `/Decode` array, an
9246
+ * unresolvable or non-gray/RGB color space, an unsupported predictor, or a decoded
9247
+ * length that does not match the declared dimensions.
9248
+ */ function decodeFlateRasterImage(pdfDoc, stream) {
9249
+ var dict = stream.dict;
9250
+ var width = dictNumber(dict, PDF_NAME_WIDTH);
9251
+ var height = dictNumber(dict, PDF_NAME_HEIGHT);
9252
+ var bitsPerComponent = dictNumber(dict, PDF_NAME_BITS_PER_COMPONENT);
9253
+ var hasDecodeArray = dict.get(PDF_NAME_DECODE) !== undefined;
9254
+ var isImageMask = dict.get(PDF_NAME_IMAGE_MASK) !== undefined;
9255
+ var channels = resolveImageColorSpaceChannels(pdfDoc, dict.get(PDF_NAME_COLOR_SPACE));
9256
+ var result;
9257
+ if (width != null && width > 0 && height != null && height > 0 && bitsPerComponent === 8 && !hasDecodeArray && !isImageMask && channels != null) {
9258
+ var predictorParams = readFlatePredictorParams(pdfDoc, dict);
9259
+ // when a predictor is in play its params must describe the same raster layout the image dict declares
9260
+ var predictorParamsOk = predictorParams.predictor === 1 || predictorParams.colors === channels && predictorParams.columns === width && predictorParams.bitsPerComponent === 8;
9261
+ if (predictorParamsOk) {
9262
+ var inflated = node_zlib.inflateSync(stream.contents);
9263
+ var data = unpredictFlateImageData(inflated, predictorParams);
9264
+ if ((data === null || data === void 0 ? void 0 : data.byteLength) === width * height * channels) {
9265
+ result = {
9266
+ data: data,
9267
+ width: width,
9268
+ height: height,
9269
+ channels: channels
9270
+ };
9271
+ }
9272
+ }
9273
+ }
9274
+ return result;
9275
+ }
8968
9276
  function replaceImageStream(input) {
8969
- var pdfDoc = input.pdfDoc, ref = input.ref, newImageBytes = input.newImageBytes, newWidth = input.newWidth, newHeight = input.newHeight;
9277
+ var pdfDoc = input.pdfDoc, ref = input.ref, newImageBytes = input.newImageBytes, newWidth = input.newWidth, newHeight = input.newHeight, colorSpace = input.colorSpace;
8970
9278
  var context = pdfDoc.context;
8971
9279
  var newDict = context.obj({
8972
9280
  Type: 'XObject',
8973
9281
  Subtype: 'Image',
8974
9282
  Width: newWidth,
8975
9283
  Height: newHeight,
8976
- ColorSpace: 'DeviceRGB',
9284
+ ColorSpace: colorSpace,
8977
9285
  BitsPerComponent: 8,
8978
9286
  Filter: 'DCTDecode',
8979
9287
  Length: newImageBytes.length