diff --git a/package-lock.json b/package-lock.json
index e501cc8..ff64e27 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,7 +39,7 @@
"notistack": "^3.0.1",
"omggif": "^1.0.10",
"pdf-lib": "^1.17.1",
- "pdfjs-dist": "^5.2.133",
+ "pdfjs-dist": "^5.3.31",
"playwright": "^1.45.0",
"qrcode": "^1.5.4",
"rc-slider": "^11.1.8",
@@ -8680,9 +8680,9 @@
"license": "0BSD"
},
"node_modules/pdfjs-dist": {
- "version": "5.2.133",
- "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.2.133.tgz",
- "integrity": "sha512-abE6ZWDxztt+gGFzfm4bX2ggfxUk9wsDEoFzIJm9LozaY3JdXR7jyLK4Bjs+XLXplCduuWS1wGhPC4tgTn/kzg==",
+ "version": "5.3.31",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.3.31.tgz",
+ "integrity": "sha512-EhPdIjNX0fcdwYQO+e3BAAJPXt+XI29TZWC7COhIXs/K0JHcUt1Gdz1ITpebTwVMFiLsukdUZ3u0oTO7jij+VA==",
"license": "Apache-2.0",
"engines": {
"node": ">=20.16.0 || >=22.3.0"
diff --git a/package.json b/package.json
index ed7ebcf..6adcd2e 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
"notistack": "^3.0.1",
"omggif": "^1.0.10",
"pdf-lib": "^1.17.1",
- "pdfjs-dist": "^5.2.133",
+ "pdfjs-dist": "^5.3.31",
"playwright": "^1.45.0",
"qrcode": "^1.5.4",
"rc-slider": "^11.1.8",
diff --git a/src/components/result/ToolMultiFileResult.tsx b/src/components/result/ToolMultiFileResult.tsx
new file mode 100644
index 0000000..bba516b
--- /dev/null
+++ b/src/components/result/ToolMultiFileResult.tsx
@@ -0,0 +1,152 @@
+import {
+ Box,
+ CircularProgress,
+ Typography,
+ useTheme,
+ Button
+} from '@mui/material';
+import InputHeader from '../InputHeader';
+import greyPattern from '@assets/grey-pattern.png';
+import { globalInputHeight } from '../../config/uiConfig';
+import ResultFooter from './ResultFooter';
+
+export default function ToolFileResult({
+ title = 'Result',
+ value,
+ zipFile,
+ loading,
+ loadingText
+}: {
+ title?: string;
+ value: File[];
+ zipFile?: File | null;
+ loading?: boolean;
+ loadingText?: string;
+}) {
+ const theme = useTheme();
+
+ const getFileType = (
+ file: File
+ ): 'image' | 'video' | 'audio' | 'pdf' | 'unknown' => {
+ if (file.type.startsWith('image/')) return 'image';
+ if (file.type.startsWith('video/')) return 'video';
+ if (file.type.startsWith('audio/')) return 'audio';
+ if (file.type.startsWith('application/pdf')) return 'pdf';
+ return 'unknown';
+ };
+
+ const handleDownload = (file: File) => {
+ const url = URL.createObjectURL(file);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = file.name;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ };
+
+ return (
+
+
+
+
+ {loading ? (
+
+
+
+ {loadingText}... This may take a moment.
+
+
+ ) : value.length > 0 ? (
+ value.map((file, idx) => {
+ const preview = URL.createObjectURL(file);
+ const fileType = getFileType(file);
+
+ return (
+
+ {fileType === 'image' && (
+
+ )}
+ {fileType === 'video' && (
+
+ )}
+ {fileType === 'audio' && (
+
+ )}
+ {fileType === 'pdf' && (
+
+ )}
+ {fileType === 'unknown' && (
+ File ready. Click below to download.
+ )}
+
+
+ );
+ })
+ ) : (
+ No output available yet.
+ )}
+
+
+ {zipFile && (
+
+
+
+ )}
+
+ {}}
+ handleDownload={() => {}}
+ />
+
+ );
+}
diff --git a/src/pages/tools/pdf/pdf-to-png/index.tsx b/src/pages/tools/pdf/pdf-to-png/index.tsx
index 05add93..d06a61c 100644
--- a/src/pages/tools/pdf/pdf-to-png/index.tsx
+++ b/src/pages/tools/pdf/pdf-to-png/index.tsx
@@ -1,62 +1,70 @@
-import { Box } from '@mui/material';
-import React, { useState } from 'react';
+import { useState } from 'react';
import ToolContent from '@components/ToolContent';
+import ToolPdfInput from '@components/input/ToolPdfInput';
import { ToolComponentProps } from '@tools/defineTool';
-import ToolTextInput from '@components/input/ToolTextInput';
-import ToolTextResult from '@components/result/ToolTextResult';
-import { GetGroupsType } from '@components/options/ToolOptions';
-import { CardExampleType } from '@components/examples/ToolExamples';
-import { main } from './service';
-import { InitialValuesType } from './types';
+import { convertPdfToPngImages } from './service';
+import ToolMultiFileResult from '@components/result/ToolMultiFileResult';
-const initialValues: InitialValuesType = {
- // splitSeparator: '\n'
+type ImagePreview = {
+ blob: Blob;
+ url: string;
+ filename: string;
};
-const exampleCards: CardExampleType[] = [
- {
- title: 'Split a String',
- description: 'This example shows how to split a string into multiple lines',
- sampleText: 'Hello World,Hello World',
- sampleResult: `Hello World
-Hello World`,
- sampleOptions: {
- // splitSeparator: ','
- }
- }
-];
-export default function PdfToPng({
- title,
- longDescription
-}: ToolComponentProps) {
- const [input, setInput] = useState('');
- const [result, setResult] = useState('');
+export default function PdfToPng({ title }: ToolComponentProps) {
+ const [input, setInput] = useState(null);
+ const [images, setImages] = useState([]);
+ const [zipBlob, setZipBlob] = useState(null);
+ const [loading, setLoading] = useState(false);
- const compute = (values: InitialValuesType, input: string) => {
- setResult(main(input, values));
+ const compute = async (_: {}, file: File | null) => {
+ if (!file) return;
+ setLoading(true);
+ setImages([]);
+ setZipBlob(null);
+ try {
+ const { images, zipFile } = await convertPdfToPngImages(file);
+ setImages(images);
+ setZipBlob(zipFile);
+ } catch (err) {
+ console.error('Conversion failed:', err);
+ } finally {
+ setLoading(false);
+ }
};
- const getGroups: GetGroupsType | null = ({
- values,
- updateField
- }) => [
- {
- title: 'Example Settings',
- component:
- }
- ];
return (
}
- resultComponent={}
- initialValues={initialValues}
- exampleCards={exampleCards}
- getGroups={getGroups}
setInput={setInput}
+ initialValues={{}}
compute={compute}
- toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
+ inputComponent={
+
+ }
+ resultComponent={
+ {
+ return new File([img.blob], img.filename, { type: 'image/png' });
+ })}
+ zipFile={zipBlob}
+ loading={loading}
+ loadingText="Converting PDF pages"
+ />
+ }
+ getGroups={null}
+ toolInfo={{
+ title: 'Convert PDF pages into PNG images',
+ description:
+ 'Upload your PDF and get each page rendered as a high-quality PNG. You can preview, download individually, or get all images in a ZIP.'
+ }}
/>
);
}
diff --git a/src/pages/tools/pdf/pdf-to-png/pdf-to-png.service.test.ts b/src/pages/tools/pdf/pdf-to-png/pdf-to-png.service.test.ts
deleted file mode 100644
index 5e7ff29..0000000
--- a/src/pages/tools/pdf/pdf-to-png/pdf-to-png.service.test.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { expect, describe, it } from 'vitest';
-// import { main } from './service';
-//
-// describe('pdf-to-png', () => {
-//
-// })
diff --git a/src/pages/tools/pdf/pdf-to-png/service.ts b/src/pages/tools/pdf/pdf-to-png/service.ts
index 38333df..988bb57 100644
--- a/src/pages/tools/pdf/pdf-to-png/service.ts
+++ b/src/pages/tools/pdf/pdf-to-png/service.ts
@@ -1,5 +1,51 @@
-import { InitialValuesType } from './types';
+import * as pdfjsLib from 'pdfjs-dist';
+import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min?url';
+import JSZip from 'jszip';
-export function main(input: string, options: InitialValuesType): string {
- return input;
+pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
+
+type ImagePreview = {
+ blob: Blob;
+ url: string;
+ filename: string;
+};
+
+export async function convertPdfToPngImages(pdfFile: File): Promise<{
+ images: ImagePreview[];
+ zipFile: File;
+}> {
+ const arrayBuffer = await pdfFile.arrayBuffer();
+ const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
+ const zip = new JSZip();
+ const images: ImagePreview[] = [];
+
+ for (let i = 1; i <= pdf.numPages; i++) {
+ const page = await pdf.getPage(i);
+ const viewport = page.getViewport({ scale: 2 });
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d')!;
+ canvas.width = viewport.width;
+ canvas.height = viewport.height;
+
+ await page.render({ canvasContext: context, viewport }).promise;
+
+ const blob = await new Promise((resolve) =>
+ canvas.toBlob((b) => b && resolve(b), 'image/png')
+ );
+
+ const filename = `page-${i}.png`;
+ const url = URL.createObjectURL(blob);
+ images.push({ blob, url, filename });
+ zip.file(filename, blob);
+ }
+
+ const zipBuffer = await zip.generateAsync({ type: 'arraybuffer' });
+ const zipFile = new File(
+ [zipBuffer],
+ pdfFile.name.replace(/\.pdf$/i, '-pages.zip'),
+ { type: 'application/zip' }
+ );
+
+ return { images, zipFile };
}
diff --git a/src/pages/tools/pdf/pdf-to-png/types.ts b/src/pages/tools/pdf/pdf-to-png/types.ts
deleted file mode 100644
index d4135c9..0000000
--- a/src/pages/tools/pdf/pdf-to-png/types.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export type InitialValuesType = {
- // splitSeparator: string;
-};