diff --git a/README.md b/README.md
index 9465f064..fa5714d7 100644
--- a/README.md
+++ b/README.md
@@ -88,66 +88,136 @@ If you are interested in learning about this, take a look at the Example workflo
Current functions of spdf and their progress in this repo.
+#### Page Operations
| Status | Feature | Description |
| ------ | ------------------------ | ----------- |
-| ✔️ | Merge | |
-| ✔️ | Split | |
-| ✔️ | Rotate | |
-| ✔️ | Multi-Page-Layout | |
-| ✔️ | Adjust page size/scale | |
-| ✔️ | Organize | |
-| ✔️ | Change Metadata | |
-| ✔️ | Auto Rename | |
-| ❌ | Add Watermark | |
+| 🚧A | Merge | |
+| 🚧A | Split | |
+| 🚧A | Organize | |
+| 🚧S | Rotate | |
+| 🚧A | Remove Pages | |
+| 🚧A | Multi-Page Layout | |
+| ❌ | Adjust page size/scale | |
+| 🚧A | Auto Split Pages | |
+| ❌ | Adjust Colours/Contrast | |
+| ❌ | Crop | |
+| 🚧A | Extract Pages | |
| ❌ | PDF to Single large Page | |
-| ❌ | Auto Redact | |
-| ❌ | Remove Pages | |
-| Status | Feature | Description |
-| ------ | ------------------ | ----------- |
-| ✔️ | Remove Blank Pages | |
-| ✔️ | Auto Split Pages | |
-| Status | Feature | Description |
-| ------ | ------------ | ----------- |
-| ❌ | Repair | |
-| ❌ | Compress | |
-| ❌ | Flatten | |
-| ❌ | Compare/Diff | |
-| ❌ | Sanitize | |
-| ❌ | Get info | |
-| ❌ | Show JS | |
+#### Convert
+| Status | Feature | Description |
+| ------ | ------------------- | ----------- |
+| ❌ | Image to PDF | |
+| 🚧S | Convert file to PDF | |
+| ❌ | URL to PDF | |
+| ❌ | HTML to PDF | |
+| ❌ | Markdown to PDF | |
+| ❌ | PDF to Image | |
+| ❌ | PDF to Word | |
+| ❌ | PDF to Presentation | |
+| ❌ | PDF to Text/RTF | |
+| ❌ | PDF to HTML | |
+| ❌ | PDF to PDF/A | |
+#### Security
| Status | Feature | Description |
| ------ | --------------------- | ----------- |
-| ❌ | Sign | |
-| ❌ | Sign with Certificate | |
| ❌ | Add Password | |
| ❌ | Remove Password | |
| ❌ | Change Permissions | |
+| ❌ | Add Watermark | |
+| ❌ | Sign with Certificate | |
+| ❌ | Sanitize | |
+| ❌ | Auto Redact | |
+#### Miscellaneous
| Status | Feature | Description |
| ------ | --------------------------- | ----------- |
-| ❌ | Image to PDF | |
-| ❌ | Add image | |
-| ❌ | Extract Images | |
-| ❌ | PDF to Image | |
| ❌ | OCR | |
-| ❌ | Detect/Split Scanned photos | |
+| ❌ | Add image | |
+| ❌ | Compress | |
+| ❌ | Extract Images | |
+| 🚧S | Change Metadata | |
+| 🚧A | Detect/Split Scanned photos | |
+| ❌ | Sign | |
+| ❌ | Flatten | |
+| ❌ | Repair | |
+| 🚧A | Remove Blank Pages | |
+| ❌ | Compare/Diff | |
+| ❌ | Add Page Numbers | |
+| ❌ | Auto Rename | |
+| ❌ | Get info | |
+| ❌ | Show JS | |
+
-| Status | Feature | Description |
-| ------ | ------------------------ | ----------- |
-| ❌ | Convert file to PDF | |
-| ❌ | PDF to Text/RTF | |
-| ❌ | PDF to HTML | |
-| ❌ | PDF to XML | |
-| ❌ | URL to PDF | |
-| ❌ | HTML to PDF | |
-| ❌ | Markdown to PDF | |
✔️: Done, 🚧: Started Developement, ❌: Planned Feature
+A: Available in the internal API, S: Available on the node server, C: Available in the client
## Contribute
For initial instructions look at [CONTRIBUTE.md](./CONTRIBUTE.md)
+
+
+
+/*
+///// CONVERT 2 pdf
+file2pdf
+url2pdf
+html2pdf
+md2pdf
+image2pdf
+
+///// CONVERT from pdf
+pdf2image
+flatten
+pdf2pdf/a
+pdf2word
+pdf2presentation
+pdf2rtf
+pdf2html
+pdf2xml
+
+///// SINGLE
+merge
+rotate
+crop
+pageNumbers
+colours/contrast
+addPassword
+removePassword
+compress
+changeMetadata
+change Permissions
+OCR
+sanitise
+repair
+compare
+extract images
+signWith certificate
+impose
+adjust page size/scale
+auto rename
+getAllInfo
+showJS
+redact
+pdf2singleLargePage
+
+///// SPLITTING
+split
+auto split
+detect/split scanned
+
+///// REARRANGE
+- organise pages (remove/re-arrange)
+- removePages
+- removeBlank
+- extractPages
+
+///// ADD OBJECTS
+add image
+add watermark
+sign
+*/
\ No newline at end of file
diff --git a/client-tauri/package.json b/client-tauri/package.json
index 466785cd..04a0fd96 100644
--- a/client-tauri/package.json
+++ b/client-tauri/package.json
@@ -17,13 +17,15 @@
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"path-browserify": "^1.0.1",
+ "pdfjs-dist": "^4.0.189",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0",
"react-i18next": "^13.3.1",
"react-icons": "^4.11.0",
"react-router-bootstrap": "^0.26.2",
- "react-router-dom": "^6.18.0"
+ "react-router-dom": "^6.18.0",
+ "vite-plugin-top-level-await": "^1.3.1"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.0",
diff --git a/client-tauri/src-tauri/Cargo.lock b/client-tauri/src-tauri/Cargo.lock
index 2eeb383d..31cd7ff3 100644
--- a/client-tauri/src-tauri/Cargo.lock
+++ b/client-tauri/src-tauri/Cargo.lock
@@ -1711,6 +1711,16 @@ dependencies = [
"windows-sys 0.42.0",
]
+[[package]]
+name = "os_pipe"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "overload"
version = "0.1.1"
@@ -2436,6 +2446,16 @@ dependencies = [
"lazy_static",
]
+[[package]]
+name = "shared_child"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "simd-adler32"
version = "0.3.7"
@@ -2693,6 +2713,7 @@ dependencies = [
"objc",
"once_cell",
"open",
+ "os_pipe",
"percent-encoding",
"rand 0.8.5",
"raw-window-handle",
@@ -2703,6 +2724,7 @@ dependencies = [
"serde_json",
"serde_repr",
"serialize-to-javascript",
+ "shared_child",
"state",
"tar",
"tauri-macros",
diff --git a/client-tauri/src-tauri/Cargo.toml b/client-tauri/src-tauri/Cargo.toml
index d4a9a4e0..8c60e697 100644
--- a/client-tauri/src-tauri/Cargo.toml
+++ b/client-tauri/src-tauri/Cargo.toml
@@ -13,7 +13,7 @@ edition = "2021"
tauri-build = { version = "1.5", features = [] }
[dependencies]
-tauri = { version = "1.5", features = [ "fs-write-file", "fs-read-file", "dialog-save", "dialog-open", "shell-open"] }
+tauri = { version = "1.5", features = [ "fs-remove-dir", "fs-create-dir", "shell-all", "fs-write-file", "fs-read-file", "dialog-save", "dialog-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
diff --git a/client-tauri/src-tauri/tauri.conf.json b/client-tauri/src-tauri/tauri.conf.json
index 2efa9aaf..23ada3f1 100644
--- a/client-tauri/src-tauri/tauri.conf.json
+++ b/client-tauri/src-tauri/tauri.conf.json
@@ -14,8 +14,19 @@
"allowlist": {
"all": false,
"shell": {
- "all": false,
- "open": true
+ "all": true,
+ "open": true,
+ "scope": [
+ {
+ "name": "libreoffice-version",
+ "cmd": "libreoffice",
+ "args": ["--version"]
+ },{
+ "name": "libreoffice-convert",
+ "cmd": "libreoffice",
+ "args": ["--headless","--convert-to",{ "validator": "\\S+" },{ "validator": "\\S+" },"--outdir",{ "validator": "\\S+" }]
+ }
+ ]
},
"dialog": {
"all": false,
@@ -31,8 +42,8 @@
"writeFile": true,
"readDir": false,
"copyFile": false,
- "createDir": false,
- "removeDir": false,
+ "createDir": true,
+ "removeDir": true,
"removeFile": false,
"renameFile": false,
"exists": false
diff --git a/client-tauri/src/App.tsx b/client-tauri/src/App.tsx
index fe7bda09..94bb6007 100644
--- a/client-tauri/src/App.tsx
+++ b/client-tauri/src/App.tsx
@@ -4,6 +4,7 @@ import { Routes, Route, Outlet } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Dashboard from "./pages/Dashboard";
+import ToPdf from "./pages/convert/ToPdf"
import NoMatch from "./pages/NoMatch";
import NavBar from "./components/NavBar";
@@ -38,6 +39,7 @@ export default function App() {
} />
} />
} />
+ } />
{/* Using path="*"" means "match anything", so this route
acts like a catch-all for URLs that we don't have explicit
diff --git a/client-tauri/src/components/NavBar.tsx b/client-tauri/src/components/NavBar.tsx
index 45978fe5..7073910c 100644
--- a/client-tauri/src/components/NavBar.tsx
+++ b/client-tauri/src/components/NavBar.tsx
@@ -86,7 +86,7 @@ function NavBar() {
]},
{displayText: t('navbar.convert'), icon: BsArrowLeftRight, sublist: [
{ displayText: t('home.imageToPdf.title'), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t('home.imageToPdf.desc') },
- { displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/nothing-here", tooltip: t('home.fileToPDF.desc') },
+ { displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/to-pdf", tooltip: t('home.fileToPDF.desc') },
{ displayText: t('home.HTMLToPDF.title'), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t('home.HTMLToPDF.desc') },
{ displayText: t('home.URLToPDF.title'), icon: BsLink, dest: "/nothing-here", tooltip: t('home.URLToPDF.desc') },
{ displayText: t('home.MarkdownToPDF.title'), icon: BsFiletypeMd, dest: "/nothing-here", tooltip: t('home.MarkdownToPDF.desc') },
diff --git a/client-tauri/src/pages/convert/ToPdf.tsx b/client-tauri/src/pages/convert/ToPdf.tsx
new file mode 100644
index 00000000..41d9e6f4
--- /dev/null
+++ b/client-tauri/src/pages/convert/ToPdf.tsx
@@ -0,0 +1,16 @@
+
+import { isLibreOfficeInstalled } from "../../utils/libre-office-utils";
+
+const hasLibreOffice = await isLibreOfficeInstalled();
+console.log(hasLibreOffice)
+
+function About() {
+ return (
+
+
Convert to PDF
+ {"hasLibreOffice: "+hasLibreOffice}
+
+ );
+}
+
+export default About;
diff --git a/client-tauri/src/utils/libre-office-utils.tsx b/client-tauri/src/utils/libre-office-utils.tsx
new file mode 100644
index 00000000..47f5c432
--- /dev/null
+++ b/client-tauri/src/utils/libre-office-utils.tsx
@@ -0,0 +1,46 @@
+
+import { readBinaryFile, writeBinaryFile, removeDir, BaseDirectory } from '@tauri-apps/api/fs';
+import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
+import { runShell } from './tauri-wrapper';
+
+export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise {
+ const randUuid = crypto.randomUUID();
+ const tempDir = "StirlingPDF/"+randUuid;
+ const srcFile = tempDir+"/"+filename;
+
+ await writeBinaryFile(srcFile, byteArray);
+ await writeBinaryFile(srcFile, new Uint8Array([]), { dir: BaseDirectory.Temp });
+
+ const messageList: string[] = [];
+ await runShell("libreoffice-convert", ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir], (message, stream) => {
+ if (stream === "stdout") {
+ messageList.push(message);
+ }
+ console.debug(`${stream}, ${randUuid}: ${message}`);
+ });
+ const lastMessage = messageList[messageList.length-1]
+ const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
+ const outputFilePathSplit = outputFilePath.toString().split("[\\/]")
+ const outputFileName = outputFilePathSplit[outputFilePathSplit.length-1];
+ const outputBytes = await readBinaryFile(outputFilePath);
+
+ await removeDir(tempDir);
+
+ return fromUint8Array(outputBytes, outputFileName);
+}
+
+export async function isLibreOfficeInstalled() {
+ const messageList: string[] = [];
+ try {
+ await runShell("libreoffice-version", ["--version"], (message, stream) => {
+ if (stream === "stdout") {
+ messageList.push(message);
+ }
+ });
+ } catch (error) {
+ return false;
+ }
+ console.log("messageList", messageList)
+ const result = messageList[0].match("LibreOffice ([0-9]+\.){4}.*");
+ return result ? true : false;
+}
diff --git a/client-tauri/src/utils/tauri-wrapper.ts b/client-tauri/src/utils/tauri-wrapper.ts
index 7e5aea84..3f71d837 100644
--- a/client-tauri/src/utils/tauri-wrapper.ts
+++ b/client-tauri/src/utils/tauri-wrapper.ts
@@ -1,6 +1,7 @@
import { open, save } from '@tauri-apps/api/dialog';
import { readBinaryFile, writeBinaryFile } from '@tauri-apps/api/fs';
+import { Command } from '@tauri-apps/api/shell'
export type TauriBrowserFile = {
name: string,
@@ -52,7 +53,7 @@ export function openFiles(options: SelectFilesDialogOptions): Promise {
if (input.files && input.files.length) {
console.log("input.files", input.files)
- const files:TauriBrowserFile[] = [];
+ const files: TauriBrowserFile[] = [];
for (const f of input.files) {
const contents = new Uint8Array(await f.arrayBuffer());
const res = byteArrayToFile(contents, f.name);
@@ -138,4 +139,31 @@ export async function downloadFile(fileData: Uint8Array, options: DownloadFilesD
downloadLink.click();
}
}
-}
\ No newline at end of file
+}
+
+/**
+ * Dont forget to whitelist the Command in src-tauri/tauri.conf.json! (tauri.allowlist.shell.scope)
+ * @param commandName The name of the command to run. Must be defined in tauri.allowlist.shell.scope[].name
+ * @param args The args to pass into the command
+ * @param callback A callback function that is called when output is logged
+ * @returns A log of all the outputs logged
+ */
+export function runShell(commandName: string, args: string[], callback: (message: any, stream:"stdout"|"stderr"|"error") => void): Promise {
+ return new Promise(async (resolve, reject) => {
+
+ const comm = new Command(commandName, args);
+ comm.on('close', data => {
+ if (data.code === 0) {
+ resolve();
+ } else {
+ reject(new Error(`Command failed with exit code ${data.code} and signal ${data.signal}`));
+ }
+ });
+ comm.on('error', error => callback(error, "error"));
+ comm.stdout.on('data', line => callback(line, "stdout"));
+ comm.stderr.on('data', line => callback(line, "stderr"));
+
+ const child = await comm.spawn();
+ console.debug(`Started child process with pid: ${child.pid}`)
+ });
+}
diff --git a/client-tauri/vite.config.ts b/client-tauri/vite.config.ts
index 833f3307..510ba1da 100644
--- a/client-tauri/vite.config.ts
+++ b/client-tauri/vite.config.ts
@@ -1,9 +1,18 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
+import topLevelAwait from "vite-plugin-top-level-await";
// https://vitejs.dev/config/
export default defineConfig(async () => ({
- plugins: [react()],
+ plugins: [
+ react(),
+ topLevelAwait({
+ // The export name of top-level await promise for each chunk module
+ promiseExportName: "__tla",
+ // The function to generate import names of top-level await promise in each chunk module
+ promiseImportName: i => `__tla_${i}`
+ }),
+ ],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
diff --git a/package-lock.json b/package-lock.json
index 54a33146..0210a23a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -74,13 +74,15 @@
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"path-browserify": "^1.0.1",
+ "pdfjs-dist": "^4.0.189",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0",
"react-i18next": "^13.3.1",
"react-icons": "^4.11.0",
"react-router-bootstrap": "^0.26.2",
- "react-router-dom": "^6.18.0"
+ "react-router-dom": "^6.18.0",
+ "vite-plugin-top-level-await": "^1.3.1"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.0",
@@ -2579,7 +2581,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -2595,7 +2596,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -2611,7 +2611,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -2627,7 +2626,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -2643,7 +2641,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -2659,7 +2656,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -2675,7 +2671,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -2691,7 +2686,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2707,7 +2701,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2723,7 +2716,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2739,7 +2731,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2755,7 +2746,6 @@
"cpu": [
"mips64el"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2771,7 +2761,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2787,7 +2776,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2803,7 +2791,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2819,7 +2806,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -2835,7 +2821,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"netbsd"
@@ -2851,7 +2836,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"openbsd"
@@ -2867,7 +2851,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"sunos"
@@ -2883,7 +2866,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -2899,7 +2881,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -2915,7 +2896,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -3605,7 +3585,7 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -3619,7 +3599,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6.0.0"
}
@@ -3628,7 +3608,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6.0.0"
}
@@ -3637,7 +3617,7 @@
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@@ -3647,13 +3627,13 @@
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
+ "devOptional": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@@ -3833,6 +3813,22 @@
"react": ">=16.14.0"
}
},
+ "node_modules/@rollup/plugin-virtual": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz",
+ "integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==",
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@sideway/address": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
@@ -3906,6 +3902,198 @@
"sourcemap-codec": "^1.4.8"
}
},
+ "node_modules/@swc/core": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.96.tgz",
+ "integrity": "sha512-zwE3TLgoZwJfQygdv2SdCK9mRLYluwDOM53I+dT6Z5ZvrgVENmY3txvWDvduzkV+/8IuvrRbVezMpxcojadRdQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@swc/counter": "^0.1.1",
+ "@swc/types": "^0.1.5"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-darwin-arm64": "1.3.96",
+ "@swc/core-darwin-x64": "1.3.96",
+ "@swc/core-linux-arm-gnueabihf": "1.3.96",
+ "@swc/core-linux-arm64-gnu": "1.3.96",
+ "@swc/core-linux-arm64-musl": "1.3.96",
+ "@swc/core-linux-x64-gnu": "1.3.96",
+ "@swc/core-linux-x64-musl": "1.3.96",
+ "@swc/core-win32-arm64-msvc": "1.3.96",
+ "@swc/core-win32-ia32-msvc": "1.3.96",
+ "@swc/core-win32-x64-msvc": "1.3.96"
+ },
+ "peerDependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/helpers": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@swc/core-darwin-arm64": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.96.tgz",
+ "integrity": "sha512-8hzgXYVd85hfPh6mJ9yrG26rhgzCmcLO0h1TIl8U31hwmTbfZLzRitFQ/kqMJNbIBCwmNH1RU2QcJnL3d7f69A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-darwin-x64": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.96.tgz",
+ "integrity": "sha512-mFp9GFfuPg+43vlAdQZl0WZpZSE8sEzqL7sr/7Reul5McUHP0BaLsEzwjvD035ESfkY8GBZdLpMinblIbFNljQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm-gnueabihf": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.96.tgz",
+ "integrity": "sha512-8UEKkYJP4c8YzYIY/LlbSo8z5Obj4hqcv/fUTHiEePiGsOddgGf7AWjh56u7IoN/0uEmEro59nc1ChFXqXSGyg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-gnu": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.96.tgz",
+ "integrity": "sha512-c/IiJ0s1y3Ymm2BTpyC/xr6gOvoqAVETrivVXHq68xgNms95luSpbYQ28rqaZC8bQC8M5zdXpSc0T8DJu8RJGw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-musl": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.96.tgz",
+ "integrity": "sha512-i5/UTUwmJLri7zhtF6SAo/4QDQJDH2fhYJaBIUhrICmIkRO/ltURmpejqxsM/ye9Jqv5zG7VszMC0v/GYn/7BQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-gnu": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.96.tgz",
+ "integrity": "sha512-USdaZu8lTIkm4Yf9cogct/j5eqtdZqTgcTib4I+NloUW0E/hySou3eSyp3V2UAA1qyuC72ld1otXuyKBna0YKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-musl": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.96.tgz",
+ "integrity": "sha512-QYErutd+G2SNaCinUVobfL7jWWjGTI0QEoQ6hqTp7PxCJS/dmKmj3C5ZkvxRYcq7XcZt7ovrYCTwPTHzt6lZBg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-arm64-msvc": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.96.tgz",
+ "integrity": "sha512-hjGvvAduA3Un2cZ9iNP4xvTXOO4jL3G9iakhFsgVhpkU73SGmK7+LN8ZVBEu4oq2SUcHO6caWvnZ881cxGuSpg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-ia32-msvc": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.96.tgz",
+ "integrity": "sha512-Far2hVFiwr+7VPCM2GxSmbh3ikTpM3pDombE+d69hkedvYHYZxtTF+2LTKl/sXtpbUnsoq7yV/32c9R/xaaWfw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.3.96",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.96.tgz",
+ "integrity": "sha512-4VbSAniIu0ikLf5mBX81FsljnfqjoVGleEkCQv4+zRlyZtO3FHoDPkeLVoy6WRlj7tyrRcfUJ4mDdPkbfTO14g==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz",
+ "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw=="
+ },
"node_modules/@swc/helpers": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz",
@@ -3919,6 +4107,11 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
+ "node_modules/@swc/types": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz",
+ "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw=="
+ },
"node_modules/@swiftcarrot/color-fns": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@swiftcarrot/color-fns/-/color-fns-3.2.0.tgz",
@@ -4281,7 +4474,7 @@
"version": "18.18.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz",
"integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -4749,7 +4942,7 @@
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
- "dev": true,
+ "devOptional": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6751,7 +6944,6 @@
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
- "dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@@ -10093,7 +10285,6 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -10810,8 +11001,7 @@
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -10862,7 +11052,6 @@
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -11639,7 +11828,6 @@
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
- "dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -12042,7 +12230,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=0.10.0"
}
@@ -12051,7 +12239,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -12060,7 +12247,7 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -12476,7 +12663,7 @@
"version": "5.22.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz",
"integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
@@ -12494,7 +12681,7 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/text-table": {
"version": "0.2.0",
@@ -13038,7 +13225,7 @@
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true
+ "devOptional": true
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
@@ -13242,7 +13429,6 @@
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
- "dev": true,
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.27",
@@ -13398,6 +13584,31 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/vite-plugin-top-level-await": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.3.1.tgz",
+ "integrity": "sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ==",
+ "dependencies": {
+ "@rollup/plugin-virtual": "^3.0.1",
+ "@swc/core": "^1.3.10",
+ "uuid": "^9.0.0"
+ },
+ "peerDependencies": {
+ "vite": ">=2.8"
+ }
+ },
+ "node_modules/vite-plugin-top-level-await/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/vitest": {
"version": "0.32.4",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz",
diff --git a/server-node/src/routes/api/api-controller.ts b/server-node/src/routes/api/api-controller.ts
index 9524802e..d119b530 100644
--- a/server-node/src/routes/api/api-controller.ts
+++ b/server-node/src/routes/api/api-controller.ts
@@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
//import workflow from './workflow-controller';
import operations from './operations-controller';
+import conversions from './conversions-controller';
const router = express.Router();
@@ -11,6 +12,7 @@ router.get("/", (req: Request, res: Response) => {
});
router.use("/operations", operations);
+router.use("/conversions", conversions);
//router.use("/workflow", workflow);
export default router;
\ No newline at end of file
diff --git a/server-node/src/routes/api/conversions-controller.ts b/server-node/src/routes/api/conversions-controller.ts
new file mode 100644
index 00000000..8deb6144
--- /dev/null
+++ b/server-node/src/routes/api/conversions-controller.ts
@@ -0,0 +1,27 @@
+
+import { respondWithPdfFile, response_mustHaveExactlyOneFile, response_dependencyNotConfigured } from '../../utils/endpoint-utils';
+import { fileToPdf, isLibreOfficeInstalled } from '../../utils/libre-office-utils';
+
+import express, { Request, Response } from 'express';
+const router = express.Router();
+import multer from 'multer';
+const upload = multer();
+import Joi from 'joi';
+
+router.post('/file-to-pdf', upload.single("file"), async function(req: Request, res: Response) {
+ if (!req.file) {
+ response_mustHaveExactlyOneFile(res);
+ return;
+ }
+
+ const isInstalled = await isLibreOfficeInstalled();
+ if (isInstalled) {
+ const outputFile = await fileToPdf(req.file.buffer, req.file.originalname);
+ respondWithPdfFile(res, outputFile);
+ return;
+ }
+
+ response_dependencyNotConfigured(res, "LibreOffice");
+});
+
+export default router;
diff --git a/server-node/src/utils/endpoint-utils.ts b/server-node/src/utils/endpoint-utils.ts
index 2933da1f..53ac17ab 100644
--- a/server-node/src/utils/endpoint-utils.ts
+++ b/server-node/src/utils/endpoint-utils.ts
@@ -2,14 +2,18 @@
import { Response } from 'express';
import { PdfFile } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
+export async function respondWithFile(res: Response, bytes: Uint8Array, name: string, mimeType: string): Promise {
+ res.writeHead(200, {
+ 'Content-Type': mimeType,
+ 'Content-disposition': 'attachment;filename=' + name,
+ 'Content-Length': bytes.length
+ });
+ res.end(bytes);
+}
+
export async function respondWithPdfFile(res: Response, file: PdfFile): Promise {
const byteFile = await file.convertToByteArrayFile();
- res.writeHead(200, {
- 'Content-Type': "application/pdf",
- 'Content-disposition': 'attachment;filename=' + byteFile.filename,
- 'Content-Length': byteFile.byteArray?.length
- });
- res.end(byteFile.byteArray)
+ respondWithFile(res, byteFile.byteArray!, byteFile.filename, "application/pdf");
}
export function response_mustHaveExactlyOneFile(res: Response): void {
@@ -27,3 +31,12 @@ export function response_mustHaveExactlyOneFile(res: Response): void {
}
]);
}
+
+export function response_dependencyNotConfigured(res: Response, dependencyName: string): void {
+ res.status(400).send([
+ {
+ "message": `${dependencyName} is not configured correctly on the server.`,
+ "type": "dependency_error",
+ }
+ ]);
+}
diff --git a/server-node/src/utils/libre-office-utils.ts b/server-node/src/utils/libre-office-utils.ts
new file mode 100644
index 00000000..548876f6
--- /dev/null
+++ b/server-node/src/utils/libre-office-utils.ts
@@ -0,0 +1,99 @@
+
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+import { exec, spawn } from 'child_process'
+import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
+
+export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise {
+ const parentDir = path.join(os.tmpdir(), "StirlingPDF");
+ fs.mkdirSync(parentDir, {recursive: true});
+ const tempDir = fs.mkdtempSync(parentDir+"/");
+ const srcFile = path.join(tempDir, filename);
+ const randFolderName = path.parse(tempDir).base;
+
+ await writeBytesToFile(srcFile, byteArray);
+
+ const messages = await runLibreOfficeCommand(randFolderName, ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir]);
+ const lastMessage = messages[messages.length-1]
+ const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
+ const outputFileName = path.parse(outputFilePath).base;
+ const outputBytes = await readBytesFromFile(outputFilePath);
+
+ fs.rmdirSync(tempDir);
+
+ return fromUint8Array(outputBytes, outputFileName);
+}
+
+export function isLibreOfficeInstalled() {
+ return new Promise((resolve, reject) => {
+ exec("libreoffice --version", (error, stdout, stderr) => {
+ if (error) {
+ resolve(false);
+ return;
+ }
+ if (stderr) {
+ resolve(false);
+ return;
+ }
+ const result = stdout.match("LibreOffice ([0-9]+\.){4}.*");
+ resolve(result ? true : false);
+ });
+ })
+}
+
+function writeBytesToFile(filePath: string, bytes: Uint8Array): Promise {
+ return new Promise((resolve, reject) => {
+ fs.writeFile(filePath, bytes, function(err) {
+ if(err) {
+ reject(err)
+ return;
+ }
+ resolve();
+ });
+ });
+}
+
+function readBytesFromFile(filePath: string): Promise {
+ return new Promise((resolve, reject) => {
+ fs.readFile(filePath, (err, data) => {
+ if (err) {
+ reject(new Error(`Error reading file: ${err.message}`));
+ } else {
+ const uint8Array = new Uint8Array(data);
+ resolve(uint8Array);
+ }
+ });
+ });
+}
+
+function runLibreOfficeCommand(idKey: string, args: string[]): Promise {
+ return new Promise(async (resolve, reject) => {
+ const messageList: string[] = [];
+
+ const process = spawn("libreoffice", args);
+
+ process.stdout.on('data', (data) => {
+ const dataStr = data.toString();
+ console.log(`Progress ${idKey}:`, dataStr);
+ messageList.push(dataStr);
+ });
+
+ process.stderr.on('data', (data) => {
+ console.error(`stderr ${idKey}:`, data.toString());
+ });
+
+ process.on('exit', (code) => {
+ if (code === 0) {
+ resolve(messageList);
+ } else {
+ reject(new Error(`Command failed with exit code ${code}`));
+ }
+ });
+
+ process.on('error', (err) => {
+ reject(err);
+ });
+
+ });
+}
diff --git a/shared-operations/functions/splitPDF.ts b/shared-operations/functions/splitPDF.ts
index edf7597d..20137e80 100644
--- a/shared-operations/functions/splitPDF.ts
+++ b/shared-operations/functions/splitPDF.ts
@@ -1,6 +1,4 @@
-import { PDFDocument } from 'pdf-lib';
-
import { selectPages } from "./subDocumentFunctions";
import { PdfFile } from '../wrappers/PdfFile';
diff --git a/shared-operations/functions/updateMetadata.ts b/shared-operations/functions/updateMetadata.ts
index 5e16019b..97165bcd 100644
--- a/shared-operations/functions/updateMetadata.ts
+++ b/shared-operations/functions/updateMetadata.ts
@@ -1,5 +1,4 @@
-import { PDFDocument, ParseSpeeds } from 'pdf-lib';
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
export type Metadata = {