Adding LibreOffice conversion support (WIP)
This commit is contained in:
@@ -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() {
|
||||
<Route index element={<Home />} />
|
||||
<Route path="about" element={<About />} />
|
||||
<Route path="dashboard" element={<Dashboard />} />
|
||||
<Route path="to-pdf" element={<ToPdf />} />
|
||||
|
||||
{/* Using path="*"" means "match anything", so this route
|
||||
acts like a catch-all for URLs that we don't have explicit
|
||||
|
||||
@@ -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') },
|
||||
|
||||
16
client-tauri/src/pages/convert/ToPdf.tsx
Normal file
16
client-tauri/src/pages/convert/ToPdf.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
import { isLibreOfficeInstalled } from "../../utils/libre-office-utils";
|
||||
|
||||
const hasLibreOffice = await isLibreOfficeInstalled();
|
||||
console.log(hasLibreOffice)
|
||||
|
||||
function About() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Convert to PDF</h2>
|
||||
{"hasLibreOffice: "+hasLibreOffice}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default About;
|
||||
46
client-tauri/src/utils/libre-office-utils.tsx
Normal file
46
client-tauri/src/utils/libre-office-utils.tsx
Normal file
@@ -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<PdfFile> {
|
||||
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;
|
||||
}
|
||||
@@ -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<TauriBrows
|
||||
selected = [selected];
|
||||
}
|
||||
|
||||
const files:TauriBrowserFile[] = [];
|
||||
const files: TauriBrowserFile[] = [];
|
||||
for (const s of selected) {
|
||||
const contents = await readBinaryFile(s);
|
||||
const res = byteArrayToFile(contents, s);
|
||||
@@ -73,7 +74,7 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows
|
||||
input.onchange = async () => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<void> {
|
||||
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}`)
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user