compress-pdf-lib 1.0.3 → 1.0.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/compress.js +42 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compress-pdf-lib",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Client-side PDF compression: pdf.js render + mozjpeg (WASM) encoding via a parallel worker pool + pdf-lib rebuild. No server, no UI. Ships as raw ESM source for Vite-based apps (React + Vite, Astro).",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/compress.js CHANGED
@@ -276,8 +276,8 @@ function jpegResultToBytes(item) {
276
276
  * ---- PDF filter name plumbing --------------------------------------------
277
277
  */
278
278
 
279
- function filterNamesOf(dict) {
280
- const filter = dict.get(PDFName.of("Filter"));
279
+ function filterNamesOf(context, dict) {
280
+ const filter = context.lookup(dict.get(PDFName.of("Filter")));
281
281
 
282
282
  if (!filter) return [];
283
283
 
@@ -286,19 +286,15 @@ function filterNamesOf(dict) {
286
286
  }
287
287
 
288
288
  if (filter instanceof PDFArray) {
289
- return filter.asArray().map((f) => f.asString().replace(/^\//, ""));
289
+ return filter.asArray().map((f) => {
290
+ const resolved = context.lookup(f);
291
+ return resolved instanceof PDFName ? resolved.asString().replace(/^\//, "") : "";
292
+ }).filter(Boolean);
290
293
  }
291
294
 
292
295
  return [];
293
296
  }
294
297
 
295
- const IMAGE_CODEC_FILTERS = new Set([
296
- "DCTDecode",
297
- "JPXDecode",
298
- "CCITTFaxDecode",
299
- "JBIG2Decode",
300
- ]);
301
-
302
298
  /*
303
299
  * ---- ColorSpace resolution --------------------------------------------
304
300
  * Returns { kind, components, palette?, paletteComponents? }
@@ -518,11 +514,12 @@ async function decodeImageXObject(context, ref) {
518
514
  // values — recompressing would break it, so skip these entirely.
519
515
  if (dict.get(PDFName.of("Mask"))) return null;
520
516
 
521
- const filterNames = filterNamesOf(dict);
522
- const lastFilter = filterNames[filterNames.length - 1];
523
-
524
- if (lastFilter === "JPXDecode" || lastFilter === "CCITTFaxDecode" || lastFilter === "JBIG2Decode") {
525
- return { unsupported: true, reason: lastFilter };
517
+ const filterNames = filterNamesOf(context, dict);
518
+
519
+ // Skip explicitly unhandled compression formats natively
520
+ const unsupported = filterNames.find(f => f === "JPXDecode" || f === "CCITTFaxDecode" || f === "JBIG2Decode");
521
+ if (unsupported) {
522
+ return { unsupported: true, reason: unsupported };
526
523
  }
527
524
 
528
525
  const width = dict.get(PDFName.of("Width"))?.asNumber?.();
@@ -535,10 +532,35 @@ async function decodeImageXObject(context, ref) {
535
532
  let bitmap;
536
533
  let hasAlpha = false;
537
534
 
538
- if (lastFilter === "DCTDecode") {
539
- // Already a JPEG file (decodePDFRawStream only inverts general stream
540
- // filters like Flate/LZW, it leaves the image codec itself alone).
541
- const jpegBytes = decodePDFRawStream(stream).decode();
535
+ if (filterNames.includes("DCTDecode")) {
536
+ // pdf-lib's decodePDFRawStream throws on DCTDecode because it natively lacks a
537
+ // mechanism to decode JPEGs to raw samples. We must extract the bytes dynamically
538
+ // whilst bypassing the "DCTDecode" step inside its pipeline filter.
539
+ let jpegBytes;
540
+ const originalFilterVal = dict.get(PDFName.of("Filter"));
541
+ const filterObj = context.lookup(originalFilterVal);
542
+
543
+ if (filterNames.length > 1 && filterObj instanceof PDFArray) {
544
+ const filtered = filterObj.asArray().filter((f) => {
545
+ const resolved = context.lookup(f);
546
+ return resolved instanceof PDFName && resolved.asString() !== "/DCTDecode";
547
+ });
548
+
549
+ if (filtered.length === 0) {
550
+ jpegBytes = stream.contents;
551
+ } else {
552
+ dict.set(PDFName.of("Filter"), context.obj(filtered));
553
+ try {
554
+ jpegBytes = decodePDFRawStream(stream).decode();
555
+ } finally {
556
+ dict.set(PDFName.of("Filter"), originalFilterVal);
557
+ }
558
+ }
559
+ } else {
560
+ // It's just a raw DCTDecode stream (most common).
561
+ jpegBytes = stream.contents;
562
+ }
563
+
542
564
  const blob = new Blob([jpegBytes], { type: "image/jpeg" });
543
565
  bitmap = await createImageBitmap(blob);
544
566
  } else {
@@ -1217,4 +1239,4 @@ export async function rasterizePDF(input, options = {}) {
1217
1239
 
1218
1240
  pool.destroy();
1219
1241
  }
1220
- }
1242
+ }