fix: corrected pdf.json JSON structure

This commit is contained in:
sayan 2025-07-17 22:09:49 +05:30
commit 335e910322
11 changed files with 372 additions and 66 deletions

178
package-lock.json generated
View file

@ -31,6 +31,7 @@
"cron-validator": "^1.3.1",
"cronstrue": "^3.0.0",
"dayjs": "^1.11.13",
"docx": "^9.5.1",
"fast-xml-parser": "^5.2.5",
"formik": "^2.4.6",
"i18next": "^25.3.2",
@ -47,7 +48,7 @@
"notistack": "^3.0.1",
"omggif": "^1.0.10",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^5.2.133",
"pdfjs-dist": "^5.3.93",
"playwright": "^1.45.0",
"qrcode": "^1.5.4",
"rc-slider": "^11.1.8",
@ -2360,35 +2361,36 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
},
"node_modules/@napi-rs/canvas": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.70.tgz",
"integrity": "sha512-nD6NGa4JbNYSZYsTnLGrqe9Kn/lCkA4ybXt8sx5ojDqZjr2i0TWAHxx/vhgfjX+i3hCdKWufxYwi7CfXqtITSA==",
"license": "MIT",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.74.tgz",
"integrity": "sha512-pOIyzuS+5Bz1vAhD7tdhaw5/936mMJZUn4aVajojUdjYOGSWmfpDYSgt0nQLZPZVN5GLgWgutqXPOi7Jsm3k+Q==",
"optional": true,
"workspaces": [
"e2e/*"
],
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@napi-rs/canvas-android-arm64": "0.1.70",
"@napi-rs/canvas-darwin-arm64": "0.1.70",
"@napi-rs/canvas-darwin-x64": "0.1.70",
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.70",
"@napi-rs/canvas-linux-arm64-gnu": "0.1.70",
"@napi-rs/canvas-linux-arm64-musl": "0.1.70",
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.70",
"@napi-rs/canvas-linux-x64-gnu": "0.1.70",
"@napi-rs/canvas-linux-x64-musl": "0.1.70",
"@napi-rs/canvas-win32-x64-msvc": "0.1.70"
"@napi-rs/canvas-android-arm64": "0.1.74",
"@napi-rs/canvas-darwin-arm64": "0.1.74",
"@napi-rs/canvas-darwin-x64": "0.1.74",
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.74",
"@napi-rs/canvas-linux-arm64-gnu": "0.1.74",
"@napi-rs/canvas-linux-arm64-musl": "0.1.74",
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.74",
"@napi-rs/canvas-linux-x64-gnu": "0.1.74",
"@napi-rs/canvas-linux-x64-musl": "0.1.74",
"@napi-rs/canvas-win32-x64-msvc": "0.1.74"
}
},
"node_modules/@napi-rs/canvas-android-arm64": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.70.tgz",
"integrity": "sha512-I/YOuQ0wbkVYxVaYtCgN42WKTYxNqFA0gTcTrHIGG1jfpDSyZWII/uHcjOo4nzd19io6Y4+/BqP8E5hJgf9OmQ==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.74.tgz",
"integrity": "sha512-aq5ode+9Z/ZR0H485dI2jdRdttg/hl9Ob+iPCt0nj+QFiirpxDrbUHKeTZWQWEtkWyC7vI5R2dMTbDINBfl9eg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
@ -2398,13 +2400,12 @@
}
},
"node_modules/@napi-rs/canvas-darwin-arm64": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.70.tgz",
"integrity": "sha512-4pPGyXetHIHkw2TOJHujt3mkCP8LdDu8+CT15ld9Id39c752RcI0amDHSuMLMQfAjvusA9B5kKxazwjMGjEJpQ==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.74.tgz",
"integrity": "sha512-eO5Miz+ef1dEQyUMWDdcbAb1Wr7yMyxD9/CL9d4frQxO4pTTaCiMBUWup8XDPLr/g7XkSkGCZLP47xiXiyXSpQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -2414,13 +2415,12 @@
}
},
"node_modules/@napi-rs/canvas-darwin-x64": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.70.tgz",
"integrity": "sha512-+2N6Os9LbkmDMHL+raknrUcLQhsXzc5CSXRbXws9C3pv/mjHRVszQ9dhFUUe9FjfPhCJznO6USVdwOtu7pOrzQ==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.74.tgz",
"integrity": "sha512-0EkO0IFkps7C3JpKC7lbM3IL+QDUYeUKagHLDbUry4PeQTghxp6JcgccpmU32ZbpFZgPnm7o0tTJO0J1d8S2rA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -2430,13 +2430,12 @@
}
},
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.70.tgz",
"integrity": "sha512-QjscX9OaKq/990sVhSMj581xuqLgiaPVMjjYvWaCmAJRkNQ004QfoSMEm3FoTqM4DRoquP8jvuEXScVJsc1rqQ==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.74.tgz",
"integrity": "sha512-qAVJEN2JqGayEI1kSpJy1Xr6ZmCFV9QhRyV35yWsS7e9X1jm+T4DAlCxI4PlKIlqVSzYMYhKrxchST20XBSzHg==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -2446,13 +2445,12 @@
}
},
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.70.tgz",
"integrity": "sha512-LNakMOwwqwiHIwMpnMAbFRczQMQ7TkkMyATqFCOtUJNlE6LPP/QiUj/mlFrNbUn/hctqShJ60gWEb52ZTALbVw==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.74.tgz",
"integrity": "sha512-lOnop22qy6MYxI94GGunMMjo6D80I//2W/6pqKUfwXaDQtOfvHsTcVVzDu5cFXUTNrb9ZRfMCeol5YEd+9FJvg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -2462,13 +2460,12 @@
}
},
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.70.tgz",
"integrity": "sha512-wBTOllEYNfJCHOdZj9v8gLzZ4oY3oyPX8MSRvaxPm/s7RfEXxCyZ8OhJ5xAyicsDdbE5YBZqdmaaeP5+xKxvtg==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.74.tgz",
"integrity": "sha512-tfFqLHGtSEabBigOnPUfZviSTGmW2xHv5tYZYPBWmgGiTkoNJ7lEWFUxHjwvV5HXGqLs8ok/O7g1enSpxO6lmQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -2478,13 +2475,12 @@
}
},
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.70.tgz",
"integrity": "sha512-GVUUPC8TuuFqHip0rxHkUqArQnlzmlXmTEBuXAWdgCv85zTCFH8nOHk/YCF5yo0Z2eOm8nOi90aWs0leJ4OE5Q==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.74.tgz",
"integrity": "sha512-j6H9dHTMtr1y3tu/zGm1ythYIL9vTl4EEv9f6CMx0n3Zn2M+OruUUwh9ylCj4afzSNEK9T8cr6zMnmTPzkpBvQ==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -2494,13 +2490,12 @@
}
},
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.70.tgz",
"integrity": "sha512-/kvUa2lZRwGNyfznSn5t1ShWJnr/m5acSlhTV3eXECafObjl0VBuA1HJw0QrilLpb4Fe0VLywkpD1NsMoVDROQ==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.74.tgz",
"integrity": "sha512-73DIV4E7Y9CpIJuUXVl9H6+MEQXyRy4VJQoUGA1tOlcKQiStxqhq6UErL4decI28NxjyQXBhtYZKj5q8AJEuOg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -2510,13 +2505,12 @@
}
},
"node_modules/@napi-rs/canvas-linux-x64-musl": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.70.tgz",
"integrity": "sha512-aqlv8MLpycoMKRmds7JWCfVwNf1fiZxaU7JwJs9/ExjTD8lX2KjsO7CTeAj5Cl4aEuzxUWbJPUUE2Qu9cZ1vfg==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.74.tgz",
"integrity": "sha512-FgDMEFdGIJT3I2xejflRJ82/ZgDphyirS43RgtoLaIXI6zihLiZcQ7rczpqeWgAwlJNjR0He2EustsKe1SkUOg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -2526,13 +2520,12 @@
}
},
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.70.tgz",
"integrity": "sha512-Q9QU3WIpwBTVHk4cPfBjGHGU4U0llQYRXgJtFtYqqGNEOKVN4OT6PQ+ve63xwIPODMpZ0HHyj/KLGc9CWc3EtQ==",
"version": "0.1.74",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.74.tgz",
"integrity": "sha512-x6bhwlhn0wU7dfiP46mt5Bi6PowSUH4CJ4PTzGj58LRQ1HVasEIJgoMx7MLC48F738eJpzbfg3WR/D8+e9CeTA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
@ -5463,6 +5456,52 @@
"node": ">=6.0.0"
}
},
"node_modules/docx": {
"version": "9.5.1",
"resolved": "https://registry.npmjs.org/docx/-/docx-9.5.1.tgz",
"integrity": "sha512-ABDI7JEirFD2+bHhOBlsGZxaG1UgZb2M/QMKhLSDGgVNhxDesTCDcP+qoDnDGjZ4EOXTRfUjUgwHVuZ6VSTfWQ==",
"dependencies": {
"@types/node": "^24.0.1",
"hash.js": "^1.1.7",
"jszip": "^3.10.1",
"nanoid": "^5.1.3",
"xml": "^1.0.1",
"xml-js": "^1.6.8"
},
"engines": {
"node": ">=10"
}
},
"node_modules/docx/node_modules/@types/node": {
"version": "24.0.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz",
"integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==",
"dependencies": {
"undici-types": "~7.8.0"
}
},
"node_modules/docx/node_modules/nanoid": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/docx/node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="
},
"node_modules/dom-accessibility-api": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
@ -6989,6 +7028,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"dependencies": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -8841,6 +8889,11 @@
"node": ">=4"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
@ -9541,15 +9594,14 @@
"license": "0BSD"
},
"node_modules/pdfjs-dist": {
"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",
"version": "5.3.93",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.3.93.tgz",
"integrity": "sha512-w3fQKVL1oGn8FRyx5JUG5tnbblggDqyx2XzA5brsJ5hSuS+I0NdnJANhmeWKLjotdbPQucLBug5t0MeWr0AAdg==",
"engines": {
"node": ">=20.16.0 || >=22.3.0"
},
"optionalDependencies": {
"@napi-rs/canvas": "^0.1.67"
"@napi-rs/canvas": "^0.1.71"
}
},
"node_modules/peek-readable": {
@ -12754,11 +12806,15 @@
"node": ">=0.8"
}
},
"node_modules/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="
},
"node_modules/xml-js": {
"version": "1.6.11",
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"sax": "^1.2.4"

View file

@ -50,6 +50,7 @@
"cron-validator": "^1.3.1",
"cronstrue": "^3.0.0",
"dayjs": "^1.11.13",
"docx": "^9.5.1",
"fast-xml-parser": "^5.2.5",
"formik": "^2.4.6",
"i18next": "^25.3.2",
@ -66,7 +67,7 @@
"notistack": "^3.0.1",
"omggif": "^1.0.10",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^5.2.133",
"pdfjs-dist": "^5.3.93",
"playwright": "^1.45.0",
"qrcode": "^1.5.4",
"rc-slider": "^11.1.8",

View file

@ -52,7 +52,7 @@
}
},
"pdfToEpub": {
"description": "Transform PDF documents into EPUB files for better e-reader compatibility.', icon: 'material-symbols:import-contacts', component: lazy(() => import('./index')), keywords: ['pdf', 'epub', 'convert', 'ebook'], path: 'pdf-to-epub', i18n: { name: 'pdf:pdfToEpub.title', description: 'pdf:pdfToEpub.description",
"description": "Transform PDF documents into EPUB files for better e-reader compatibility.",
"shortDescription": "Convert PDF files to EPUB format",
"title": "PDF to EPUB"
},
@ -109,5 +109,10 @@
"description": "This tool allows you to extract specific pages from a PDF document. You can specify individual pages or ranges of pages to extract.",
"title": "Split PDF"
}
},
"pdfToWord": {
"name": "PDF to Word",
"description": "Convert PDF documents to editable Word files (.docx)",
"shortDescription": "Convert PDF to Word"
}
}

View file

@ -127,6 +127,7 @@ export default function Hero() {
<Typography sx={{ textAlign: 'center' }} fontSize={{ xs: 25, md: 30 }}>
{t('translation:hero.title')}{' '}
<Typography
component="span"
fontSize={{ xs: 25, md: 30 }}
display={'inline'}
color={'primary'}

View file

@ -152,8 +152,8 @@ const Navbar: React.FC<NavbarProps> = ({
<ListItemText primary={navItem.label} />
</ListItemButton>
))}
{buttons.map((button) => (
<ListItem>{button}</ListItem>
{buttons.map((button, index) => (
<ListItem key={index}>{button}</ListItem>
))}
</List>
);

60
src/pages/index.tsx Normal file
View file

@ -0,0 +1,60 @@
import { useState } from 'react';
import ToolContent from '@components/ToolContent';
import ToolPdfInput from '@components/input/ToolPdfInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { ToolComponentProps } from '@tools/defineTool';
import { convertPdfToDocx } from './service';
export default function PdfToWord({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [outputFile, setOutputFile] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
const compute = async (_: {}, file: File | null) => {
if (!file) return;
setLoading(true);
setOutputFile(null);
try {
const blob = await convertPdfToDocx(file);
const docxFile = new File([blob], file.name.replace(/\.pdf$/, '.docx'), {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});
setOutputFile(docxFile);
} catch (err) {
console.error('PDF to Word conversion failed:', err);
} finally {
setLoading(false);
}
};
return (
<ToolContent
title={title}
input={input}
setInput={setInput}
initialValues={{}}
compute={compute}
inputComponent={
<ToolPdfInput
value={input}
onChange={setInput}
accept={['application/pdf']}
title="Upload a PDF"
/>
}
resultComponent={
<ToolFileResult
title="Converted Word Document"
file={outputFile}
loading={loading}
loadingText="Converting PDF to Word..."
/>
}
getGroups={null}
toolInfo={{
title: 'Convert PDF to Word',
description: 'Upload your PDF and get a .docx file with extracted text.'
}}
/>
);
}

View file

@ -7,6 +7,7 @@ import { tool as compressPdfTool } from './compress-pdf/meta';
import { tool as protectPdfTool } from './protect-pdf/meta';
import { meta as pdfToEpub } from './pdf-to-epub/meta';
import { tool as pdfEditor } from './editor/meta';
import { tool as pdfToWord } from './pdf-to-word/meta';
export const pdfTools: DefinedTool[] = [
pdfEditor,
@ -16,5 +17,6 @@ export const pdfTools: DefinedTool[] = [
protectPdfTool,
mergePdf,
pdfToEpub,
pdfPdfToPng
pdfPdfToPng,
pdfToWord
];

View file

@ -0,0 +1,63 @@
import { useState } from 'react';
import ToolContent from '@components/ToolContent';
import ToolPdfInput from '@components/input/ToolPdfInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { ToolComponentProps } from '@tools/defineTool';
import { convertPdfToDocx } from './service';
export default function PdfToWord({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [outputFile, setOutputFile] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
const compute = async (_: {}, file: File | null) => {
if (!file) return;
setLoading(true);
setOutputFile(null);
try {
const blob = await convertPdfToDocx(file);
const docxFile = new File([blob], file.name.replace(/\.pdf$/, '.docx'), {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
setOutputFile(docxFile);
} catch (err) {
console.error('PDF to Word conversion failed:', err);
} finally {
setLoading(false);
}
};
console.log('outputFile:', outputFile);
return (
<ToolContent
title={title}
input={input}
setInput={setInput}
initialValues={{}}
compute={compute}
inputComponent={
<ToolPdfInput
value={input}
onChange={setInput}
accept={['application/pdf']}
title="Upload a PDF"
/>
}
resultComponent={
<ToolFileResult
title="Converted Word Document"
file={outputFile}
loading={loading}
loadingText="Converting PDF to Word..."
/>
}
getGroups={null}
toolInfo={{
title: 'Convert PDF to Word',
description: 'Upload your PDF and get a .docx file with extracted text.',
}}
/>
);
}

View file

@ -0,0 +1,69 @@
import { useState } from 'react';
import ToolContent from '@components/ToolContent';
import ToolPdfInput from '@components/input/ToolPdfInput';
import { ToolComponentProps } from '@tools/defineTool';
import { convertPdfToDocx } from './service';
export default function PdfToWord({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [outputFile, setOutputFile] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
const compute = async (_: {}, file: File | null) => {
if (!file) return;
setLoading(true);
setOutputFile(null);
try {
const result = await convertPdfToDocx(file);
console.log('outputFile:', result);
setOutputFile(result);
} catch (err) {
console.error('PDF to Word conversion failed:', err);
} finally {
setLoading(false);
}
};
return (
<ToolContent
title={title}
input={input}
setInput={setInput}
initialValues={{}}
compute={compute}
inputComponent={
<ToolPdfInput
value={input}
onChange={setInput}
accept={['application/pdf']}
title="Upload a PDF"
/>
}
resultComponent={
outputFile ? (
<a
href={URL.createObjectURL(outputFile)}
download={outputFile.name}
style={{
padding: '10px 15px',
backgroundColor: '#1976d2',
color: 'white',
borderRadius: '6px',
textDecoration: 'none',
display: 'inline-block',
marginTop: '20px'
}}
>
Download {outputFile.name}
</a>
) : null
}
getGroups={null}
toolInfo={{
title: 'Convert PDF to Word',
description:
'Upload your PDF and get a real .docx Word document with extracted text.'
}}
/>
);
}

View file

@ -0,0 +1,15 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('pdf', {
i18n: {
name: 'pdf:pdfToWord.name',
description: 'pdf:pdfToWord.description',
shortDescription: 'pdf:pdfToWord.shortDescription',
longDescription: 'pdf:pdfToWord.description'
},
path: 'pdf-to-word',
icon: 'mdi:file-word', // You can change this icon ID if needed
keywords: ['pdf', 'word', 'convert', 'docx'],
component: lazy(() => import('./index'))
});

View file

@ -0,0 +1,34 @@
import * as pdfjsLib from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min?url';
import { Document, Packer, Paragraph } from 'docx';
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
export async function convertPdfToDocx(pdfFile: File): Promise<File> {
const arrayBuffer = await pdfFile.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const textChunks: string[] = [];
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const textContent = await page.getTextContent();
const strings = textContent.items.map((item: any) => item.str);
textChunks.push(strings.join(' '));
}
const paragraphs = textChunks.map((text) => new Paragraph(text));
const doc = new Document({
sections: [
{
properties: {},
children: paragraphs
}
]
});
const blob = await Packer.toBlob(doc);
return new File([blob], pdfFile.name.replace(/\.pdf$/i, '.docx'), {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});
}