1/** 2 * Compress PDF Worker 3 * Uses coherentpdf for advanced PDF compression 4 */ 5 6self.importScripts('../coherentpdf.browser.min.js'); 7 8/** 9 * Compress PDF with coherentpdf 10 * @param {ArrayBuffer} pdfData - PDF file data 11 * @param {Object} options - Compression options 12 * @param {string} options.quality - Compression quality: 'low', 'medium', 'high', 'maximum' 13 * @param {boolean} options.removeMetadata - Whether to remove metadata 14 */ 15function compressPdfInWorker(pdfData, options) { 16 try { 17 const uint8Array = new Uint8Array(pdfData); 18 let pdf; 19 20 try { 21 pdf = coherentpdf.fromMemory(uint8Array, ''); 22 } catch (error) { 23 if (error.message && error.message.includes('encrypt')) { 24 self.postMessage({ 25 status: 'error', 26 message: 'The PDF file is encrypted. Please decrypt it first.', 27 }); 28 return; 29 } 30 throw error; 31 } 32 33 self.postMessage({ status: 'progress', progress: 20 }); 34 35 // Remove metadata if requested 36 if (options.removeMetadata) { 37 try { 38 coherentpdf.setTitle(pdf, ''); 39 coherentpdf.setAuthor(pdf, ''); 40 coherentpdf.setSubject(pdf, ''); 41 coherentpdf.setKeywords(pdf, ''); 42 coherentpdf.setCreator(pdf, ''); 43 coherentpdf.setProducer(pdf, ''); 44 } catch (e) { 45 // Ignore metadata errors 46 } 47 } 48 49 self.postMessage({ status: 'progress', progress: 40 }); 50 51 // Apply compression based on quality level 52 // For all quality levels except 'maximum', we apply compression 53 if (options.quality !== 'maximum') { 54 // Compress all uncompressed streams using Flate 55 coherentpdf.compress(pdf); 56 } 57 58 self.postMessage({ status: 'progress', progress: 60 }); 59 60 // Squeeze/optimize the PDF structure for better compression 61 // This removes duplicate objects, optimizes cross-references, etc. 62 if (options.quality === 'low' || options.quality === 'medium') { 63 coherentpdf.squeezeInMemory(pdf); 64 } 65 66 self.postMessage({ status: 'progress', progress: 80 }); 67 68 // Output with object stream settings based on quality 69 let outputBytes; 70 71 if (options.quality === 'low') { 72 // Maximum compression: generate and compress object streams 73 outputBytes = coherentpdf.toMemoryExt( 74 pdf, 75 false, // linearize 76 false, // make_id 77 false, // preserve_objstm 78 true, // generate_objstm - create new object streams 79 true // compress_objstm - compress object streams 80 ); 81 } else if (options.quality === 'medium') { 82 // Balanced: generate object streams with compression 83 outputBytes = coherentpdf.toMemoryExt( 84 pdf, 85 false, // linearize 86 false, // make_id 87 true, // preserve_objstm 88 true, // generate_objstm 89 true // compress_objstm 90 ); 91 } else if (options.quality === 'high') { 92 // Less compression: preserve existing, compress new 93 outputBytes = coherentpdf.toMemoryExt( 94 pdf, 95 false, // linearize 96 false, // make_id 97 true, // preserve_objstm 98 false, // generate_objstm 99 true // compress_objstm 100 ); 101 } else { 102 // Maximum quality (minimal compression): just output normally 103 outputBytes = coherentpdf.toMemory(pdf, false, false); 104 } 105 106 // Note: pdf is invalid after toMemoryExt, no need to delete 107 108 self.postMessage({ status: 'progress', progress: 100 }); 109 110 const outputBuffer = outputBytes.buffer; 111 self.postMessage( 112 { 113 status: 'success', 114 pdfBytes: outputBuffer, 115 originalSize: pdfData.byteLength, 116 compressedSize: outputBytes.length, 117 }, 118 [outputBuffer] 119 ); 120 } catch (error) { 121 self.postMessage({ 122 status: 'error', 123 message: error instanceof Error ? error.message : 'Unknown error during compression.', 124 }); 125 } 126} 127 128self.onmessage = (e) => { 129 if (e.data.command === 'compress') { 130 compressPdfInWorker(e.data.pdfData, e.data.options); 131 } 132};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.