-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcompression-worker.js
More file actions
210 lines (169 loc) · 6.12 KB
/
Copy pathcompression-worker.js
File metadata and controls
210 lines (169 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Web Worker for multi-threaded compression
// This worker handles compression tasks in a separate thread for better performance
self.importScripts(
'https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js'
);
// Set PDF.js worker source
if (typeof pdfjsLib !== 'undefined') {
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
}
// Listen for messages from main thread
self.addEventListener('message', async (e) => {
const { type, fileData, options, id } = e.data;
try {
let result;
switch (type) {
case 'compress-pdf':
result = await compressPDF(fileData, options);
break;
case 'compress-image':
result = await compressImage(fileData, options);
break;
case 'compress-text':
result = await compressText(fileData, options);
break;
default:
throw new Error(`Unknown compression type: ${type}`);
}
self.postMessage({
id,
success: true,
result
});
} catch (error) {
self.postMessage({
id,
success: false,
error: error.message
});
}
});
// PDF Compression with advanced techniques
async function compressPDF(arrayBuffer, options) {
const { level, targetSize, quality } = options;
// Load PDF
const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
const pdf = await loadingTask.promise;
// Determine compression settings
let imageQuality = quality || 0.7;
let scale = 1.0;
if (level === 'high') {
imageQuality = 0.3;
scale = 0.5;
} else if (level === 'medium') {
imageQuality = 0.5;
scale = 0.7;
} else if (level === 'low') {
imageQuality = 0.8;
scale = 0.9;
}
if (targetSize) {
const ratio = targetSize / arrayBuffer.byteLength;
imageQuality = Math.max(0.1, Math.min(0.9, ratio * 0.8));
scale = Math.max(0.3, Math.sqrt(ratio));
}
// Create new PDF
const newPdf = await PDFLib.PDFDocument.create();
// Process all pages
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const page = await pdf.getPage(pageNum);
const viewport = page.getViewport({ scale: scale });
// Create off-screen canvas with fallback
let canvas, context;
if (typeof OffscreenCanvas !== 'undefined') {
canvas = new OffscreenCanvas(viewport.width, viewport.height);
context = canvas.getContext('2d');
} else {
// Fallback: return error if OffscreenCanvas not supported
throw new Error('OffscreenCanvas not supported in this environment');
}
if (!context) {
throw new Error('Failed to get 2d context from canvas');
}
// Render page
await page.render({
canvasContext: context,
viewport: viewport
}).promise;
// Convert to JPEG with compression
const blob = await canvas.convertToBlob({
type: 'image/jpeg',
quality: imageQuality
});
if (!blob) {
throw new Error('Failed to convert canvas to blob');
}
const imageBytes = new Uint8Array(await blob.arrayBuffer());
const image = await newPdf.embedJpg(imageBytes);
const newPage = newPdf.addPage([viewport.width, viewport.height]);
newPage.drawImage(image, {
x: 0,
y: 0,
width: viewport.width,
height: viewport.height
});
}
// Save with compression
const pdfBytes = await newPdf.save({
useObjectStreams: true,
addDefaultPage: false
});
return pdfBytes.buffer;
}
// Image Compression
async function compressImage(arrayBuffer, options) {
const { level, targetSize, quality, format } = options;
// Create image bitmap
const blob = new Blob([arrayBuffer]);
if (typeof createImageBitmap === 'undefined') {
throw new Error('createImageBitmap not supported in this environment');
}
const imageBitmap = await createImageBitmap(blob);
// Calculate dimensions
let scale = 1.0;
if (level === 'high') scale = 0.5;
else if (level === 'medium') scale = 0.7;
else if (level === 'low') scale = 0.9;
if (targetSize) {
const ratio = targetSize / arrayBuffer.byteLength;
scale = Math.max(0.2, Math.min(1.0, Math.sqrt(ratio)));
}
const width = Math.floor(imageBitmap.width * scale);
const height = Math.floor(imageBitmap.height * scale);
// Create canvas and draw scaled image with OffscreenCanvas check
if (typeof OffscreenCanvas === 'undefined') {
throw new Error('OffscreenCanvas not supported in this environment');
}
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('Failed to get 2d context from canvas');
}
ctx.drawImage(imageBitmap, 0, 0, width, height);
// Determine output format and quality
let outputFormat = format || 'image/jpeg';
let outputQuality = quality || 0.85;
if (level === 'high') outputQuality = 0.6;
else if (level === 'medium') outputQuality = 0.75;
// Convert to blob
const outputBlob = await canvas.convertToBlob({
type: outputFormat,
quality: outputQuality
});
return await outputBlob.arrayBuffer();
}
// Text/Document Compression using gzip/deflate
async function compressText(arrayBuffer, options) {
const uint8Array = new Uint8Array(arrayBuffer);
// Check if pako library is loaded
if (typeof pako === 'undefined') {
throw new Error('pako library not loaded');
}
// Use pako for gzip compression
const compressed = pako.gzip(uint8Array, {
level: options.level === 'high' ? 9 : options.level === 'medium' ? 6 : 3
});
return compressed.buffer;
}