⚡ Bolt: Parallelized PDF to JPG conversion with Blob optimization#38
⚡ Bolt: Parallelized PDF to JPG conversion with Blob optimization#38Sumit-5002 wants to merge 1 commit into
Conversation
Refactored the `pdfToJpg` service to use chunked parallel rendering (concurrency of 4) and replaced `canvas.toDataURL()` with `canvas.toBlob()`. Improvements: - Reduces total conversion time for multi-page PDFs by processing pages in parallel chunks. - Eliminates CPU and memory overhead of Base64 encoding by using binary Blobs directly with JSZip. - Maintains accurate progress reporting during concurrent processing.
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/services/pdf/pdfImageConverter.js`:
- Around line 54-60: The progress callback can move backward because the loop
may emit 100 then the ZIP phase unconditionally calls onProgress(95); fix by
tracking the last emitted percent inside the loop (compute lastProgress =
(Math.min(i + CHUNK_SIZE - 1, numPages) / numPages) * 100 each iteration when
onProgress is called) and after the loop call onProgress(Math.max(lastProgress
|| 0, 95)) instead of unconditionally onProgress(95), ensuring onProgress,
CHUNK_SIZE, numPages and the loop index i are used to compute and compare the
last emitted percent so progress is monotonic.
- Around line 45-51: The promise created around canvas.toBlob in
pdfImageConverter.js must reject when toBlob yields a null blob or fails instead
of silently adding an empty entry; update the anonymous callback passed to
canvas.toBlob to check if blob is non-null and call reject(new Error(...)) on
null or on any encoding failure, otherwise call zip.file(`page_${pageNum}.jpg`,
blob) and resolve(); mirror the null-check/reject pattern used in
src/services/pdf/pdfSecurity.js and
src/features/tools/runners/image/HtmlToImageTool.jsx so consumers can handle
errors properly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c7db9530-8a44-4bc5-ba4c-b3997ca92227
📒 Files selected for processing (2)
.jules/bolt.mdsrc/services/pdf/pdfImageConverter.js
| // Use toBlob instead of toDataURL to avoid Base64 encoding overhead (Bolt ⚡) | ||
| return new Promise((resolve) => { | ||
| canvas.toBlob((blob) => { | ||
| zip.file(`page_${pageNum}.jpg`, blob); | ||
| resolve(); | ||
| }, 'image/jpeg', 0.9); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/services/pdf/pdfImageConverter.js | head -60Repository: Sumit-5002/Anyfileforge
Length of output: 2873
🏁 Script executed:
rg -A 15 "pdfToJpg" src/features/tools/runners/pdf/PdfToJpgTool.jsx | head -40Repository: Sumit-5002/Anyfileforge
Length of output: 536
🏁 Script executed:
rg -B 5 -A 10 "zip.file" src/services/pdf/pdfImageConverter.jsRepository: Sumit-5002/Anyfileforge
Length of output: 647
🏁 Script executed:
cat src/services/pdf/pdfImageConverter.js | tail -20Repository: Sumit-5002/Anyfileforge
Length of output: 789
🌐 Web query:
HTMLCanvasElement.toBlob callback null specification
💡 Result:
The HTMLCanvasElement.toBlob method invokes a BlobCallback with a Blob? (nullable Blob) parameter. The specification explicitly defines the callback as callback BlobCallback = undefined (Blob? blob);, allowing null to be passed to the callback if the image cannot be created for any reason, such as when the serialization result is null (e.g., unsupported type, zero-sized canvas, or other failure conditions). MDN documentation confirms: "null may be passed if the image cannot be created for any reason." The method processes asynchronously: it queues a task on the "canvas blob serialization task source" to invoke the callback with the result, which is set to a new Blob if non-null, or remains null otherwise. Developers must handle the null case in the callback, as seen in TypeScript usage where the parameter is typed as Blob | null.
Citations:
- 1: https://html.spec.whatwg.org/canvas.html
- 2: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
- 3: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toBlob
- 4: https://html.spec.whatwg.org/C/canvas.html
- 5: https://html.spec.whatwg.org/multipage/canvas.html
🌐 Web query:
JSZip file method null blob handling
💡 Result:
In JSZip, the file(name, data, [options]) method explicitly supports passing null as the data parameter when creating directories. The official documentation provides this example: zip.file("folder/", null, { dir: true }); This creates an empty directory entry named "folder/" in the zip archive. The dir: true option is required to mark it as a directory rather than a regular file with null/empty content. Passing null for regular files (without dir: true) is not explicitly documented as supported and may result in an empty file (0 bytes) rather than an error, as JSZip handles various data types including promises, strings, ArrayBuffers, Blobs, etc., and converts them appropriately during generation. However, for Blob specifically, null is not a valid Blob constructor argument, so using null without dir: true likely creates an empty regular file. No errors or special null Blob handling (like rejection) are mentioned in the docs or issues for the file method. The folder(name) method always succeeds (creates if missing), unlike some type definitions suggesting null returns. Sources confirm this behavior in official API docs (file method, examples) and GitHub repo.
Citations:
- 1: https://stuk.github.io/jszip/documentation/api_jszip/file_data.html
- 2: https://github.com/Stuk/jszip
- 3: https://stuk.github.io/jszip/documentation/api_jszip.html
- 4: https://www.npmjs.com/package/jszip
🏁 Script executed:
rg "toBlob" src/ -B 2 -A 5Repository: Sumit-5002/Anyfileforge
Length of output: 9984
Add proper error handling to canvas.toBlob to reject on null blob or encoding failures.
Lines 46–51 silently accept null from toBlob callback and pass it to zip.file(), which creates empty 0-byte entries instead of rejecting the promise. This causes silent data corruption in the output ZIP. Similar code in src/services/pdf/pdfSecurity.js and src/features/tools/runners/image/HtmlToImageTool.jsx validates the blob and rejects on null; adopt the same pattern here.
💡 Suggested fix
- return new Promise((resolve) => {
+ return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
- zip.file(`page_${pageNum}.jpg`, blob);
- resolve();
+ if (!blob) {
+ reject(new Error(`Failed to encode page ${pageNum} as JPEG`));
+ return;
+ }
+
+ try {
+ zip.file(`page_${pageNum}.jpg`, blob);
+ resolve();
+ } catch (error) {
+ reject(error);
+ }
}, 'image/jpeg', 0.9);
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/services/pdf/pdfImageConverter.js` around lines 45 - 51, The promise
created around canvas.toBlob in pdfImageConverter.js must reject when toBlob
yields a null blob or fails instead of silently adding an empty entry; update
the anonymous callback passed to canvas.toBlob to check if blob is non-null and
call reject(new Error(...)) on null or on any encoding failure, otherwise call
zip.file(`page_${pageNum}.jpg`, blob) and resolve(); mirror the
null-check/reject pattern used in src/services/pdf/pdfSecurity.js and
src/features/tools/runners/image/HtmlToImageTool.jsx so consumers can handle
errors properly.
| if (onProgress) { | ||
| const completed = Math.min(i + CHUNK_SIZE - 1, numPages); | ||
| onProgress((completed / numPages) * 100); | ||
| } | ||
| } | ||
| if (onProgress) onProgress(95); // Zipping takes a bit | ||
|
|
||
| if (onProgress) onProgress(95); // Zipping phase |
There was a problem hiding this comment.
Keep progress monotonic through the ZIP phase.
Line 56 can already emit 100 on the last chunk, so Line 60 sends the consumer in src/features/tools/runners/pdf/PdfToJpgTool.jsx:19-33 backward to 95 right before completion. Reserve the last few percent for zipping so the UI only moves forward.
💡 Minimal fix
- onProgress((completed / numPages) * 100);
+ onProgress((completed / numPages) * 95);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/services/pdf/pdfImageConverter.js` around lines 54 - 60, The progress
callback can move backward because the loop may emit 100 then the ZIP phase
unconditionally calls onProgress(95); fix by tracking the last emitted percent
inside the loop (compute lastProgress = (Math.min(i + CHUNK_SIZE - 1, numPages)
/ numPages) * 100 each iteration when onProgress is called) and after the loop
call onProgress(Math.max(lastProgress || 0, 95)) instead of unconditionally
onProgress(95), ensuring onProgress, CHUNK_SIZE, numPages and the loop index i
are used to compute and compare the last emitted percent so progress is
monotonic.
I have optimized the
pdfToJpgfunction insrc/services/pdf/pdfImageConverter.jsto significantly improve its performance and efficiency.What
canvas.toDataURL()withcanvas.toBlob().toDataURLcreates large Base64 strings which are memory-intensive and require expensive encoding.JSZipcan handleBlobobjects directly, making this much more efficient.Why
Impact
Verification
pnpm linton the modified file..jules/bolt.mdwith the new learning.PR created automatically by Jules for task 4280440450733164263 started by @Sumit-5002
Summary by CodeRabbit