mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 07:55:07 +00:00
refactor: apply eslint
This commit is contained in:
parent
5fd505d4f4
commit
9c1588d150
17
.eslintrc.js
17
.eslintrc.js
@ -4,7 +4,9 @@ module.exports = {
|
|||||||
"es2021": true
|
"es2021": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:@typescript-eslint/strict"
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/strict-type-checked",
|
||||||
|
"plugin:@typescript-eslint/stylistic-type-checked"
|
||||||
],
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
@ -22,7 +24,12 @@ module.exports = {
|
|||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": "latest",
|
"ecmaVersion": "latest",
|
||||||
"sourceType": "module"
|
"sourceType": "module",
|
||||||
|
"project": [
|
||||||
|
"./client-tauri/tsconfig.json",
|
||||||
|
"./server-node/tsconfig.json",
|
||||||
|
"./shared-operations/tsconfig.json"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@typescript-eslint"
|
"@typescript-eslint"
|
||||||
@ -47,7 +54,11 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always"
|
"always",
|
||||||
|
{
|
||||||
|
"omitLastInOneLineBlock": true,
|
||||||
|
"omitLastInOneLineClassBody": true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import { Suspense } from 'react';
|
import { Suspense } from "react";
|
||||||
|
|
||||||
import { Routes, Route, Outlet } from "react-router-dom";
|
import { Routes, Route, Outlet } from "react-router-dom";
|
||||||
import Home from "./pages/Home";
|
import Home from "./pages/Home";
|
||||||
import About from "./pages/About";
|
import About from "./pages/About";
|
||||||
import Dashboard from "./pages/Dashboard";
|
import Dashboard from "./pages/Dashboard";
|
||||||
import ToPdf from "./pages/convert/ToPdf"
|
import ToPdf from "./pages/convert/ToPdf";
|
||||||
import Impose from "./pages/page-operations/Impose"
|
import Impose from "./pages/page-operations/Impose";
|
||||||
import NoMatch from "./pages/NoMatch";
|
import NoMatch from "./pages/NoMatch";
|
||||||
import NavBar from "./components/NavBar";
|
import NavBar from "./components/NavBar";
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import "bootstrap/dist/css/bootstrap.min.css";
|
||||||
import { Container } from "react-bootstrap";
|
import { Container } from "react-bootstrap";
|
||||||
|
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import { useTranslation, initReactI18next } from "react-i18next";
|
import { useTranslation, initReactI18next } from "react-i18next";
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
import ar from './locales/ar.json';
|
import ar from "./locales/ar.json";
|
||||||
import en from './locales/en.json';
|
import en from "./locales/en.json";
|
||||||
|
|
||||||
import './general.css'
|
import "./general.css";
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
@ -62,7 +62,7 @@ export default function App() {
|
|||||||
function Layout() {
|
function Layout() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div lang-direction={t('language.direction')}>
|
<div lang-direction={t("language.direction")}>
|
||||||
<NavBar/>
|
<NavBar/>
|
||||||
|
|
||||||
{/* An <Outlet> renders whatever child route is currently active,
|
{/* An <Outlet> renders whatever child route is currently active,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import Form from 'react-bootstrap/Form';
|
import Form from "react-bootstrap/Form";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
import { FieldConstraint, RecordConstraint } from '@stirling-pdf/shared-operations/src/dynamic-ui/OperatorConstraints'
|
import { FieldConstraint, RecordConstraint } from "@stirling-pdf/shared-operations/src/dynamic-ui/OperatorConstraints";
|
||||||
|
|
||||||
interface DynamicParameterFieldsProps {
|
interface DynamicParameterFieldsProps {
|
||||||
constraints: RecordConstraint;
|
constraints: RecordConstraint;
|
||||||
@ -13,15 +13,15 @@ const DynamicParameterFields: React.FC<DynamicParameterFieldsProps> = ({constrai
|
|||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
{Object.entries(constraints.record).map(([fieldName, value]) => {
|
{Object.entries(constraints.record).map(([fieldName, value]) => {
|
||||||
console.log(fieldName, value)
|
console.log(fieldName, value);
|
||||||
const globallyUniqueId = joinKeyPath([...parentKeyPath, fieldName]);
|
const globallyUniqueId = joinKeyPath([...parentKeyPath, fieldName]);
|
||||||
return <div className='mb-3' key={fieldName} >
|
return <div className='mb-3' key={fieldName} >
|
||||||
<label htmlFor={globallyUniqueId}>{t(value.displayNameKey)}</label>
|
<label htmlFor={globallyUniqueId}>{t(value.displayNameKey)}</label>
|
||||||
{fieldConstraintToElement(fieldName, parentKeyPath, globallyUniqueId, value)}
|
{fieldConstraintToElement(fieldName, parentKeyPath, globallyUniqueId, value)}
|
||||||
</div>
|
</div>;
|
||||||
})}
|
})}
|
||||||
</>);
|
</>);
|
||||||
}
|
};
|
||||||
|
|
||||||
function joinKeyPath(keyPath: string[]) {
|
function joinKeyPath(keyPath: string[]) {
|
||||||
return keyPath.join(".");
|
return keyPath.join(".");
|
||||||
@ -29,7 +29,7 @@ function joinKeyPath(keyPath: string[]) {
|
|||||||
|
|
||||||
function fieldConstraintToElement(fieldName: string, parentKeyPath: string[], globallyUniqueId: string, fieldConstraint: FieldConstraint) {
|
function fieldConstraintToElement(fieldName: string, parentKeyPath: string[], globallyUniqueId: string, fieldConstraint: FieldConstraint) {
|
||||||
if (Array.isArray(fieldConstraint.type)) {
|
if (Array.isArray(fieldConstraint.type)) {
|
||||||
if (fieldConstraint.type.every(e => typeof e == 'string' || typeof e == 'number')) {
|
if (fieldConstraint.type.every(e => typeof e == "string" || typeof e == "number")) {
|
||||||
return (
|
return (
|
||||||
<Form.Select id={globallyUniqueId} name={fieldName}>
|
<Form.Select id={globallyUniqueId} name={fieldName}>
|
||||||
<option value="" disabled>Select an option</option>
|
<option value="" disabled>Select an option</option>
|
||||||
@ -37,9 +37,9 @@ function fieldConstraintToElement(fieldName: string, parentKeyPath: string[], gl
|
|||||||
</Form.Select>
|
</Form.Select>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <div key={fieldName}>Error: Field type '{fieldConstraint.type}' not supported</div>
|
return <div key={fieldName}>Error: Field type '{fieldConstraint.type}' not supported</div>;
|
||||||
}
|
}
|
||||||
} else if (typeof fieldConstraint.type == 'string') {
|
} else if (typeof fieldConstraint.type == "string") {
|
||||||
switch (fieldConstraint.type) {
|
switch (fieldConstraint.type) {
|
||||||
case "file.pdf":
|
case "file.pdf":
|
||||||
return <input id={globallyUniqueId} type="file" name={fieldName} required={fieldConstraint.required} className="form-control required" accept="application/pdf" multiple={false}/>;
|
return <input id={globallyUniqueId} type="file" name={fieldName} required={fieldConstraint.required} className="form-control required" accept="application/pdf" multiple={false}/>;
|
||||||
@ -50,14 +50,14 @@ function fieldConstraintToElement(fieldName: string, parentKeyPath: string[], gl
|
|||||||
case "number":
|
case "number":
|
||||||
return <input id={globallyUniqueId} type="number" name={fieldName} required={fieldConstraint.required} />;
|
return <input id={globallyUniqueId} type="number" name={fieldName} required={fieldConstraint.required} />;
|
||||||
default:
|
default:
|
||||||
return <div key={fieldName}>Error: Field type '{fieldConstraint.type}' not supported</div>
|
return <div key={fieldName}>Error: Field type '{fieldConstraint.type}' not supported</div>;
|
||||||
}
|
}
|
||||||
} else if (fieldConstraint.type instanceof RecordConstraint) {
|
} else if (fieldConstraint.type instanceof RecordConstraint) {
|
||||||
//return <DynamicParameterFields constraints={fieldConstraint.type} parentKeyPath={[...parentKeyPath, fieldName]}/>
|
//return <DynamicParameterFields constraints={fieldConstraint.type} parentKeyPath={[...parentKeyPath, fieldName]}/>
|
||||||
return <div key={fieldName}>Error: Field type 'RecordConstraint' not supported yet!</div>
|
return <div key={fieldName}>Error: Field type 'RecordConstraint' not supported yet!</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div key={fieldName}>Error: Field type '{fieldConstraint.type}' not supported</div>
|
return <div key={fieldName}>Error: Field type '{fieldConstraint.type}' not supported</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DynamicParameterFields;
|
export default DynamicParameterFields;
|
@ -11,16 +11,16 @@ import {
|
|||||||
import { MdOutlineScanner, MdOutlineBalance } from "react-icons/md";
|
import { MdOutlineScanner, MdOutlineBalance } from "react-icons/md";
|
||||||
import { IconType } from "react-icons";
|
import { IconType } from "react-icons";
|
||||||
|
|
||||||
import Container from 'react-bootstrap/Container';
|
import Container from "react-bootstrap/Container";
|
||||||
import Nav from 'react-bootstrap/Nav';
|
import Nav from "react-bootstrap/Nav";
|
||||||
import Navbar from 'react-bootstrap/Navbar';
|
import Navbar from "react-bootstrap/Navbar";
|
||||||
import NavDropdown from 'react-bootstrap/NavDropdown';
|
import NavDropdown from "react-bootstrap/NavDropdown";
|
||||||
import { LinkContainer } from 'react-router-bootstrap';
|
import { LinkContainer } from "react-router-bootstrap";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import LanguagePicker from "./toolbar/LanguagePicker";
|
import LanguagePicker from "./toolbar/LanguagePicker";
|
||||||
import Logo from '../../public/stirling-pdf-logo.svg'
|
import Logo from "../../public/stirling-pdf-logo.svg";
|
||||||
import './NavBar.css';
|
import "./NavBar.css";
|
||||||
|
|
||||||
interface NavInfoItem {
|
interface NavInfoItem {
|
||||||
displayText: string;
|
displayText: string;
|
||||||
@ -31,7 +31,7 @@ interface NavInfoItem {
|
|||||||
interface NavInfoSublist {
|
interface NavInfoSublist {
|
||||||
displayText: string;
|
displayText: string;
|
||||||
icon: IconType;
|
icon: IconType;
|
||||||
sublist: Array<NavInfoItem>;
|
sublist: NavInfoItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToNavLink(item: NavInfoItem, index: number) {
|
function convertToNavLink(item: NavInfoItem, index: number) {
|
||||||
@ -51,7 +51,7 @@ function convertToNavDropdownItem(item: NavInfoItem | null, index: number) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
function convertToNavDropdown(sublist: NavInfoSublist, index: number) {
|
function convertToNavDropdown(sublist: NavInfoSublist, index: number) {
|
||||||
var myTitle = <>
|
const myTitle = <>
|
||||||
<span className="nav-icon">
|
<span className="nav-icon">
|
||||||
<sublist.icon/>
|
<sublist.icon/>
|
||||||
<span>{sublist.displayText}</span>
|
<span>{sublist.displayText}</span>
|
||||||
@ -69,63 +69,63 @@ function NavBar() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const navInfo = [
|
const navInfo = [
|
||||||
{displayText: t('multiTool.title'), icon: BsTools, dest: "/home", tooltip: t('home.multiTool.desc')},
|
{displayText: t("multiTool.title"), icon: BsTools, dest: "/home", tooltip: t("home.multiTool.desc")},
|
||||||
{displayText: t('navbar.pageOps'), icon: BsFileEarmarkPdf, sublist: [
|
{displayText: t("navbar.pageOps"), icon: BsFileEarmarkPdf, sublist: [
|
||||||
{ displayText: t('home.merge.title'), icon: AiOutlineMergeCells, dest: "/dashboard", tooltip: t('home.merge.desc') },
|
{ displayText: t("home.merge.title"), icon: AiOutlineMergeCells, dest: "/dashboard", tooltip: t("home.merge.desc") },
|
||||||
{ displayText: t('home.split.title'), icon: AiOutlineSplitCells, dest: "/about", tooltip: t('home.split.desc') },
|
{ displayText: t("home.split.title"), icon: AiOutlineSplitCells, dest: "/about", tooltip: t("home.split.desc") },
|
||||||
{ displayText: t('home.pdfOrganiser.title'), icon: BsSortNumericDown, dest: "/nothing-here", tooltip: t('home.pdfOrganiser.desc') },
|
{ displayText: t("home.pdfOrganiser.title"), icon: BsSortNumericDown, dest: "/nothing-here", tooltip: t("home.pdfOrganiser.desc") },
|
||||||
{ displayText: t('home.rotate.title'), icon: BsArrowClockwise, dest: "/nothing-here", tooltip: t('home.rotate.desc') },
|
{ displayText: t("home.rotate.title"), icon: BsArrowClockwise, dest: "/nothing-here", tooltip: t("home.rotate.desc") },
|
||||||
{ displayText: t('home.removePages.title'), icon: BsFileEarmarkX, dest: "/nothing-here", tooltip: t('home.removePages.desc') },
|
{ displayText: t("home.removePages.title"), icon: BsFileEarmarkX, dest: "/nothing-here", tooltip: t("home.removePages.desc") },
|
||||||
{ displayText: t('home.pageLayout.title'), icon: BsGrid, dest: "/page-operations/impose", tooltip: t('home.pageLayout.desc') },
|
{ displayText: t("home.pageLayout.title"), icon: BsGrid, dest: "/page-operations/impose", tooltip: t("home.pageLayout.desc") },
|
||||||
{ displayText: t('home.scalePages.title'), icon: BsArrowsFullscreen, dest: "/nothing-here", tooltip: t('home.scalePages.desc') },
|
{ displayText: t("home.scalePages.title"), icon: BsArrowsFullscreen, dest: "/nothing-here", tooltip: t("home.scalePages.desc") },
|
||||||
{ displayText: t('home.autoSplitPDF.title'), icon: BsLayoutSplit, dest: "/nothing-here", tooltip: t('home.autoSplitPDF.desc') },
|
{ displayText: t("home.autoSplitPDF.title"), icon: BsLayoutSplit, dest: "/nothing-here", tooltip: t("home.autoSplitPDF.desc") },
|
||||||
{ displayText: t('home.adjust-contrast.title'), icon: BsPalette, dest: "/nothing-here", tooltip: t('home.adjust-contrast.desc') },
|
{ displayText: t("home.adjust-contrast.title"), icon: BsPalette, dest: "/nothing-here", tooltip: t("home.adjust-contrast.desc") },
|
||||||
{ displayText: t('home.crop.title'), icon: BiCrop, dest: "/nothing-here", tooltip: t('home.crop.desc') },
|
{ displayText: t("home.crop.title"), icon: BiCrop, dest: "/nothing-here", tooltip: t("home.crop.desc") },
|
||||||
{ displayText: t('home.extractPage.title'), icon: BsArrowUpSquare, dest: "/nothing-here", tooltip: t('home.extractPage.desc') },
|
{ displayText: t("home.extractPage.title"), icon: BsArrowUpSquare, dest: "/nothing-here", tooltip: t("home.extractPage.desc") },
|
||||||
{ displayText: t('home.PdfToSinglePage.title'), icon: Bs1Square, dest: "/nothing-here", tooltip: t('home.PdfToSinglePage.desc') },
|
{ displayText: t("home.PdfToSinglePage.title"), icon: Bs1Square, dest: "/nothing-here", tooltip: t("home.PdfToSinglePage.desc") },
|
||||||
]},
|
]},
|
||||||
{displayText: t('navbar.convert'), icon: BsArrowLeftRight, sublist: [
|
{displayText: t("navbar.convert"), icon: BsArrowLeftRight, sublist: [
|
||||||
{ displayText: t('home.imageToPdf.title'), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t('home.imageToPdf.desc') },
|
{ displayText: t("home.imageToPdf.title"), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t("home.imageToPdf.desc") },
|
||||||
{ displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/convert/file-to-pdf", tooltip: t('home.fileToPDF.desc') },
|
{ displayText: t("home.fileToPDF.title"), icon: BsFileEarmark, dest: "/convert/file-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.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.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') },
|
{ displayText: t("home.MarkdownToPDF.title"), icon: BsFiletypeMd, dest: "/nothing-here", tooltip: t("home.MarkdownToPDF.desc") },
|
||||||
null,
|
null,
|
||||||
{ displayText: t('home.pdfToImage.title'), icon: BsFileEarmarkImage, dest: "/nothing-here", tooltip: t('home.pdfToImage.desc') },
|
{ displayText: t("home.pdfToImage.title"), icon: BsFileEarmarkImage, dest: "/nothing-here", tooltip: t("home.pdfToImage.desc") },
|
||||||
{ displayText: t('home.PDFToWord.title'), icon: BsFileEarmarkWord, dest: "/nothing-here", tooltip: t('home.PDFToWord.desc') },
|
{ displayText: t("home.PDFToWord.title"), icon: BsFileEarmarkWord, dest: "/nothing-here", tooltip: t("home.PDFToWord.desc") },
|
||||||
{ displayText: t('home.PDFToPresentation.title'), icon: BsFiletypePpt, dest: "/nothing-here", tooltip: t('home.PDFToPresentation.desc') },
|
{ displayText: t("home.PDFToPresentation.title"), icon: BsFiletypePpt, dest: "/nothing-here", tooltip: t("home.PDFToPresentation.desc") },
|
||||||
{ displayText: t('home.PDFToText.title'), icon: BsFiletypeTxt, dest: "/nothing-here", tooltip: t('home.PDFToText.desc') },
|
{ displayText: t("home.PDFToText.title"), icon: BsFiletypeTxt, dest: "/nothing-here", tooltip: t("home.PDFToText.desc") },
|
||||||
{ displayText: t('home.PDFToHTML.title'), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t('home.PDFToHTML.desc') },
|
{ displayText: t("home.PDFToHTML.title"), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t("home.PDFToHTML.desc") },
|
||||||
{ displayText: t('home.PDFToXML.title'), icon: BsFiletypeXml, dest: "/nothing-here", tooltip: t('home.PDFToXML.desc') },
|
{ displayText: t("home.PDFToXML.title"), icon: BsFiletypeXml, dest: "/nothing-here", tooltip: t("home.PDFToXML.desc") },
|
||||||
{ displayText: t('home.pdfToPDFA.title'), icon: BsFileEarmarkPdf, dest: "/nothing-here", tooltip: t('home.pdfToPDFA.desc') },
|
{ displayText: t("home.pdfToPDFA.title"), icon: BsFileEarmarkPdf, dest: "/nothing-here", tooltip: t("home.pdfToPDFA.desc") },
|
||||||
]},
|
]},
|
||||||
{displayText: t('navbar.security'), icon: BsShieldCheck, sublist: [
|
{displayText: t("navbar.security"), icon: BsShieldCheck, sublist: [
|
||||||
{ displayText: t('home.addPassword.title'), icon: BsLock, dest: "/dashboard", tooltip: t('home.addPassword.desc') },
|
{ displayText: t("home.addPassword.title"), icon: BsLock, dest: "/dashboard", tooltip: t("home.addPassword.desc") },
|
||||||
{ displayText: t('home.removePassword.title'), icon: BsUnlock, dest: "/nothing-here", tooltip: t('home.removePassword.desc') },
|
{ displayText: t("home.removePassword.title"), icon: BsUnlock, dest: "/nothing-here", tooltip: t("home.removePassword.desc") },
|
||||||
{ displayText: t('home.permissions.title'), icon: BsShieldLock, dest: "/nothing-here", tooltip: t('home.permissions.desc') },
|
{ displayText: t("home.permissions.title"), icon: BsShieldLock, dest: "/nothing-here", tooltip: t("home.permissions.desc") },
|
||||||
{ displayText: t('home.watermark.title'), icon: BsDroplet, dest: "/nothing-here", tooltip: t('home.watermark.desc') },
|
{ displayText: t("home.watermark.title"), icon: BsDroplet, dest: "/nothing-here", tooltip: t("home.watermark.desc") },
|
||||||
{ displayText: t('home.certSign.title'), icon: BsAward, dest: "/nothing-here", tooltip: t('home.certSign.desc') },
|
{ displayText: t("home.certSign.title"), icon: BsAward, dest: "/nothing-here", tooltip: t("home.certSign.desc") },
|
||||||
{ displayText: t('home.sanitizePdf.title'), icon: BiSprayCan, dest: "/nothing-here", tooltip: t('home.sanitizePdf.desc') },
|
{ displayText: t("home.sanitizePdf.title"), icon: BiSprayCan, dest: "/nothing-here", tooltip: t("home.sanitizePdf.desc") },
|
||||||
{ displayText: t('home.autoRedact.title'), icon: BsEraserFill, dest: "/nothing-here", tooltip: t('home.autoRedact.desc') },
|
{ displayText: t("home.autoRedact.title"), icon: BsEraserFill, dest: "/nothing-here", tooltip: t("home.autoRedact.desc") },
|
||||||
]},
|
]},
|
||||||
{displayText: t('navbar.other'), icon: BsCardList, sublist: [
|
{displayText: t("navbar.other"), icon: BsCardList, sublist: [
|
||||||
{ displayText: t('home.ocr.title'), icon: BsSearch, dest: "/dashboard", tooltip: t('home.ocr.desc') },
|
{ displayText: t("home.ocr.title"), icon: BsSearch, dest: "/dashboard", tooltip: t("home.ocr.desc") },
|
||||||
{ displayText: t('home.addImage.title'), icon: BsFileEarmarkRichtext, dest: "/nothing-here", tooltip: t('home.addImage.desc') },
|
{ displayText: t("home.addImage.title"), icon: BsFileEarmarkRichtext, dest: "/nothing-here", tooltip: t("home.addImage.desc") },
|
||||||
{ displayText: t('home.compressPdfs.title'), icon: BsFileZip, dest: "/nothing-here", tooltip: t('home.compressPdfs.desc') },
|
{ displayText: t("home.compressPdfs.title"), icon: BsFileZip, dest: "/nothing-here", tooltip: t("home.compressPdfs.desc") },
|
||||||
{ displayText: t('home.extractImages.title'), icon: BsImages, dest: "/nothing-here", tooltip: t('home.extractImages.desc') },
|
{ displayText: t("home.extractImages.title"), icon: BsImages, dest: "/nothing-here", tooltip: t("home.extractImages.desc") },
|
||||||
{ displayText: t('home.changeMetadata.title'), icon: BsClipboardData, dest: "/nothing-here", tooltip: t('home.changeMetadata.desc') },
|
{ displayText: t("home.changeMetadata.title"), icon: BsClipboardData, dest: "/nothing-here", tooltip: t("home.changeMetadata.desc") },
|
||||||
{ displayText: t('home.ScannerImageSplit.title'), icon: MdOutlineScanner, dest: "/nothing-here", tooltip: t('home.ScannerImageSplit.desc') },
|
{ displayText: t("home.ScannerImageSplit.title"), icon: MdOutlineScanner, dest: "/nothing-here", tooltip: t("home.ScannerImageSplit.desc") },
|
||||||
{ displayText: t('home.sign.title'), icon: BsVectorPen, dest: "/nothing-here", tooltip: t('home.sign.desc') },
|
{ displayText: t("home.sign.title"), icon: BsVectorPen, dest: "/nothing-here", tooltip: t("home.sign.desc") },
|
||||||
{ displayText: t('home.flatten.title'), icon: BsArrowsCollapse, dest: "/nothing-here", tooltip: t('home.flatten.desc') },
|
{ displayText: t("home.flatten.title"), icon: BsArrowsCollapse, dest: "/nothing-here", tooltip: t("home.flatten.desc") },
|
||||||
{ displayText: t('home.repair.title'), icon: BsWrench, dest: "/nothing-here", tooltip: t('home.repair.desc') },
|
{ displayText: t("home.repair.title"), icon: BsWrench, dest: "/nothing-here", tooltip: t("home.repair.desc") },
|
||||||
{ displayText: t('home.removeBlanks.title'), icon: BsFile, dest: "/nothing-here", tooltip: t('home.removeBlanks.desc') },
|
{ displayText: t("home.removeBlanks.title"), icon: BsFile, dest: "/nothing-here", tooltip: t("home.removeBlanks.desc") },
|
||||||
{ displayText: t('home.compare.title'), icon: MdOutlineBalance, dest: "/nothing-here", tooltip: t('home.compare.desc') },
|
{ displayText: t("home.compare.title"), icon: MdOutlineBalance, dest: "/nothing-here", tooltip: t("home.compare.desc") },
|
||||||
{ displayText: t('home.add-page-numbers.title'), icon: Bs123, dest: "/nothing-here", tooltip: t('home.add-page-numbers.desc') },
|
{ displayText: t("home.add-page-numbers.title"), icon: Bs123, dest: "/nothing-here", tooltip: t("home.add-page-numbers.desc") },
|
||||||
{ displayText: t('home.auto-rename.title'), icon: BsFonts, dest: "/nothing-here", tooltip: t('home.auto-rename.desc') },
|
{ displayText: t("home.auto-rename.title"), icon: BsFonts, dest: "/nothing-here", tooltip: t("home.auto-rename.desc") },
|
||||||
{ displayText: t('home.getPdfInfo.title'), icon: BsInfoCircle, dest: "/nothing-here", tooltip: t('home.getPdfInfo.desc') },
|
{ displayText: t("home.getPdfInfo.title"), icon: BsInfoCircle, dest: "/nothing-here", tooltip: t("home.getPdfInfo.desc") },
|
||||||
{ displayText: t('home.showJS.title'), icon: BsFiletypeJs, dest: "/nothing-here", tooltip: t('home.showJS.desc') },
|
{ displayText: t("home.showJS.title"), icon: BsFiletypeJs, dest: "/nothing-here", tooltip: t("home.showJS.desc") },
|
||||||
]},
|
]},
|
||||||
] as Array<NavInfoItem | NavInfoSublist>;
|
] as (NavInfoItem | NavInfoSublist)[];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -143,8 +143,8 @@ function NavBar() {
|
|||||||
|
|
||||||
<Nav>
|
<Nav>
|
||||||
{navInfo.map((ni, idx) => {
|
{navInfo.map((ni, idx) => {
|
||||||
var element;
|
let element;
|
||||||
if ('dest' in ni) {
|
if ("dest" in ni) {
|
||||||
element = convertToNavLink(ni, idx);
|
element = convertToNavLink(ni, idx);
|
||||||
} else {
|
} else {
|
||||||
element = convertToNavDropdown(ni, idx);
|
element = convertToNavDropdown(ni, idx);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
import NavDropdown from 'react-bootstrap/NavDropdown';
|
import NavDropdown from "react-bootstrap/NavDropdown";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsGlobe2 } from 'react-icons/bs';
|
import { BsGlobe2 } from "react-icons/bs";
|
||||||
|
|
||||||
function generateSublist() {
|
function generateSublist() {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const out: JSX.Element[] = [];
|
const out: JSX.Element[] = [];
|
||||||
const languages = i18n.options.resources;
|
const languages = i18n.options.resources;
|
||||||
for (var key in languages) {
|
for (const key in languages) {
|
||||||
const lang: any = languages[key].translation;
|
const lang: any = languages[key].translation;
|
||||||
const staticKey = key;
|
const staticKey = key;
|
||||||
out.push((
|
out.push((
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
|
|
||||||
declare module '@stirling-pdf/shared-operations/wasm/pdfcpu/pdfcpu-wrapper-browser.js' {
|
declare module "@stirling-pdf/shared-operations/wasm/pdfcpu/pdfcpu-wrapper-browser.js" {
|
||||||
export async function oneToOne(wasmArray: any, snapshot: any): Promise<Uint8Array>;
|
export async function oneToOne(wasmArray: any, snapshot: any): Promise<Uint8Array>;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { isLibreOfficeInstalled } from "../../utils/libre-office-utils";
|
import { isLibreOfficeInstalled } from "../../utils/libre-office-utils";
|
||||||
|
|
||||||
const hasLibreOffice = await isLibreOfficeInstalled();
|
const hasLibreOffice = await isLibreOfficeInstalled();
|
||||||
console.log(hasLibreOffice)
|
console.log(hasLibreOffice);
|
||||||
|
|
||||||
function About() {
|
function About() {
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import DynamicParameterFields from "../../components/DynamicParameterFields";
|
import DynamicParameterFields from "../../components/DynamicParameterFields";
|
||||||
import { ImposeParamConstraints } from "@stirling-pdf/shared-operations/src/functions/impose";
|
import { ImposeParamConstraints } from "@stirling-pdf/shared-operations/src/functions/impose";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
function Impose() {
|
function Impose() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
export function appendToFilename(inputPath: string, toAppend: string) {
|
export function appendToFilename(inputPath: string, toAppend: string) {
|
||||||
const parts = inputPath.split('.');
|
const parts = inputPath.split(".");
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
parts[parts.length-2] = parts[parts.length-2] + toAppend;
|
parts[parts.length-2] = parts[parts.length-2] + toAppend;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { readBinaryFile, writeBinaryFile, removeDir, BaseDirectory } from '@tauri-apps/api/fs';
|
import { readBinaryFile, writeBinaryFile, removeDir, BaseDirectory } from "@tauri-apps/api/fs";
|
||||||
import { PdfFile,RepresentationType } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile'
|
import { PdfFile,RepresentationType } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||||
import { runShell, isTauriAvailable } from './tauri-wrapper';
|
import { runShell, isTauriAvailable } from "./tauri-wrapper";
|
||||||
|
|
||||||
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
|
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
|
||||||
const randUuid = crypto.randomUUID();
|
const randUuid = crypto.randomUUID();
|
||||||
@ -18,9 +18,9 @@ export async function fileToPdf(byteArray: Uint8Array, filename: string): Promis
|
|||||||
}
|
}
|
||||||
console.debug(`${stream}, ${randUuid}: ${message}`);
|
console.debug(`${stream}, ${randUuid}: ${message}`);
|
||||||
});
|
});
|
||||||
const lastMessage = messageList[messageList.length-1]
|
const lastMessage = messageList[messageList.length-1];
|
||||||
const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
|
const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
|
||||||
const outputFilePathSplit = outputFilePath.toString().split("[\\/]")
|
const outputFilePathSplit = outputFilePath.toString().split("[\\/]");
|
||||||
const outputFileName = outputFilePathSplit[outputFilePathSplit.length-1];
|
const outputFileName = outputFilePathSplit[outputFilePathSplit.length-1];
|
||||||
const outputBytes = await readBinaryFile(outputFilePath);
|
const outputBytes = await readBinaryFile(outputFilePath);
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export async function isLibreOfficeInstalled() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
console.log("messageList", messageList)
|
console.log("messageList", messageList);
|
||||||
const result = messageList[0].match("LibreOffice ([0-9]+\.){4}.*");
|
const result = messageList[0].match("LibreOffice ([0-9]+\.){4}.*");
|
||||||
return result ? true : false;
|
return result ? true : false;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import SharedOperations, { OperationsType } from '@stirling-pdf/shared-operations/src'
|
import SharedOperations, { OperationsType } from "@stirling-pdf/shared-operations/src";
|
||||||
import { ImposeParamsType } from '@stirling-pdf/shared-operations/src/functions/impose'
|
import { ImposeParamsType } from "@stirling-pdf/shared-operations/src/functions/impose";
|
||||||
import { PdfFile } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile"
|
import { PdfFile } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||||
|
|
||||||
// Import injected libraries here!
|
// Import injected libraries here!
|
||||||
import * as pdfcpuWrapper from "@stirling-pdf/shared-operations/wasm/pdfcpu/pdfcpu-wrapper-browser.js";
|
import * as pdfcpuWrapper from "@stirling-pdf/shared-operations/wasm/pdfcpu/pdfcpu-wrapper-browser.js";
|
||||||
@ -13,5 +13,5 @@ async function impose(params: ImposeParamsType): Promise<PdfFile> {
|
|||||||
const toExport: OperationsType = {
|
const toExport: OperationsType = {
|
||||||
...SharedOperations,
|
...SharedOperations,
|
||||||
impose,
|
impose,
|
||||||
}
|
};
|
||||||
export default toExport;
|
export default toExport;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import { open, save } from '@tauri-apps/api/dialog';
|
import { open, save } from "@tauri-apps/api/dialog";
|
||||||
import { readBinaryFile, writeBinaryFile } from '@tauri-apps/api/fs';
|
import { readBinaryFile, writeBinaryFile } from "@tauri-apps/api/fs";
|
||||||
import { Command } from '@tauri-apps/api/shell'
|
import { Command } from "@tauri-apps/api/shell";
|
||||||
|
|
||||||
export type TauriBrowserFile = {
|
export interface TauriBrowserFile {
|
||||||
name: string,
|
name: string,
|
||||||
relativePath?: string,
|
relativePath?: string,
|
||||||
data: Uint8Array,
|
data: Uint8Array,
|
||||||
@ -29,13 +29,13 @@ export function isTauriAvailable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [*] = Not available in browser
|
// [*] = Not available in browser
|
||||||
type SelectFilesDialogOptions = {
|
interface SelectFilesDialogOptions {
|
||||||
defaultPath?: string, // [*] the default path to open the dialog on
|
defaultPath?: string, // [*] the default path to open the dialog on
|
||||||
directory?: boolean, // should the dialog be a directory dialog
|
directory?: boolean, // should the dialog be a directory dialog
|
||||||
filters?: Array<{ // list of file type filters
|
filters?: { // list of file type filters
|
||||||
name: string, // category name eg. 'Images'
|
name: string, // category name eg. 'Images'
|
||||||
extensions: string[] // list of extensions eg ['png', 'jpeg', 'jpg']
|
extensions: string[] // list of extensions eg ['png', 'jpeg', 'jpg']
|
||||||
}>,
|
}[],
|
||||||
multiple?: boolean, // allow multiple selections
|
multiple?: boolean, // allow multiple selections
|
||||||
recursive?: boolean, // [*] If directory is true, indicates that it will be read recursively later. Defines whether subdirectories will be allowed on the scope or not.
|
recursive?: boolean, // [*] If directory is true, indicates that it will be read recursively later. Defines whether subdirectories will be allowed on the scope or not.
|
||||||
title?: string // [*] the title of the dialog
|
title?: string // [*] the title of the dialog
|
||||||
@ -43,7 +43,7 @@ type SelectFilesDialogOptions = {
|
|||||||
export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrowserFile[] | null> {
|
export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrowserFile[] | null> {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
if (isTauriAvailable()) {
|
if (isTauriAvailable()) {
|
||||||
var selected = await open(options);
|
let selected = await open(options);
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
@ -65,15 +65,15 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows
|
|||||||
resolve(files);
|
resolve(files);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
var input = document.createElement('input');
|
const input = document.createElement("input");
|
||||||
input.type = 'file';
|
input.type = "file";
|
||||||
if (options.directory) input.setAttribute("webkitdirectory", "");
|
if (options.directory) input.setAttribute("webkitdirectory", "");
|
||||||
if (options.filters) input.setAttribute("accept", options.filters.flatMap(f => f.extensions).map(ext => "."+ext).join(", "));
|
if (options.filters) input.setAttribute("accept", options.filters.flatMap(f => f.extensions).map(ext => "."+ext).join(", "));
|
||||||
if (options.multiple) input.setAttribute("multiple", "");
|
if (options.multiple) input.setAttribute("multiple", "");
|
||||||
|
|
||||||
input.onchange = async () => {
|
input.onchange = async () => {
|
||||||
if (input.files && input.files.length) {
|
if (input.files && input.files.length) {
|
||||||
console.log("input.files", input.files)
|
console.log("input.files", input.files);
|
||||||
const files: TauriBrowserFile[] = [];
|
const files: TauriBrowserFile[] = [];
|
||||||
for (const f of input.files) {
|
for (const f of input.files) {
|
||||||
const contents = new Uint8Array(await f.arrayBuffer());
|
const contents = new Uint8Array(await f.arrayBuffer());
|
||||||
@ -91,8 +91,8 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows
|
|||||||
|
|
||||||
// detect the user clicking cancel
|
// detect the user clicking cancel
|
||||||
document.body.onfocus = () => {
|
document.body.onfocus = () => {
|
||||||
setTimeout(()=>resolve(null), 200); // the timeout is needed because 'document.body.onfocus' is called before 'input.onchange'
|
setTimeout(()=>{ resolve(null) }, 200); // the timeout is needed because 'document.body.onfocus' is called before 'input.onchange'
|
||||||
}
|
};
|
||||||
|
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
@ -100,40 +100,40 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [*] = Not available in browser
|
// [*] = Not available in browser
|
||||||
type DownloadFilesDialogOptions = {
|
interface DownloadFilesDialogOptions {
|
||||||
defaultPath?: string, // the default path to open the dialog on
|
defaultPath?: string, // the default path to open the dialog on
|
||||||
filters?: Array<{ // [*] list of file type filters
|
filters?: { // [*] list of file type filters
|
||||||
name: string, // category name eg. 'Images'
|
name: string, // category name eg. 'Images'
|
||||||
extensions: string[] // list of extensions eg ['png', 'jpeg', 'jpg']
|
extensions: string[] // list of extensions eg ['png', 'jpeg', 'jpg']
|
||||||
}>,
|
}[],
|
||||||
title?: string // [*] the title of the dialog
|
title?: string // [*] the title of the dialog
|
||||||
}
|
}
|
||||||
export async function downloadFile(fileData: Uint8Array, options: DownloadFilesDialogOptions): Promise<undefined> {
|
export async function downloadFile(fileData: Uint8Array, options: DownloadFilesDialogOptions): Promise<undefined> {
|
||||||
if (isTauriAvailable()) {
|
if (isTauriAvailable()) {
|
||||||
const pathToSave = await save(options);
|
const pathToSave = await save(options);
|
||||||
console.log("pathToSave", pathToSave)
|
console.log("pathToSave", pathToSave);
|
||||||
if (pathToSave) {
|
if (pathToSave) {
|
||||||
await writeBinaryFile(pathToSave, fileData);
|
await writeBinaryFile(pathToSave, fileData);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const pdfBlob = new Blob([fileData], { type: 'application/pdf' });
|
const pdfBlob = new Blob([fileData], { type: "application/pdf" });
|
||||||
const url = URL.createObjectURL(pdfBlob);
|
const url = URL.createObjectURL(pdfBlob);
|
||||||
const downloadOption = localStorage.getItem('downloadOption');
|
const downloadOption = localStorage.getItem("downloadOption");
|
||||||
|
|
||||||
// ensure filename is not a path
|
// ensure filename is not a path
|
||||||
const separator = options.defaultPath?.includes("\\") ? "\\" : "/";
|
const separator = options.defaultPath?.includes("\\") ? "\\" : "/";
|
||||||
const filename = options.defaultPath?.split(separator).pop();
|
const filename = options.defaultPath?.split(separator).pop();
|
||||||
const filenameToUse = filename ? filename : 'edited.pdf';
|
const filenameToUse = filename ? filename : "edited.pdf";
|
||||||
|
|
||||||
if (downloadOption === 'sameWindow') {
|
if (downloadOption === "sameWindow") {
|
||||||
// Open the file in the same window
|
// Open the file in the same window
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
} else if (downloadOption === 'newWindow') {
|
} else if (downloadOption === "newWindow") {
|
||||||
// Open the file in a new window
|
// Open the file in a new window
|
||||||
window.open(url, '_blank');
|
window.open(url, "_blank");
|
||||||
} else {
|
} else {
|
||||||
// Download the file
|
// Download the file
|
||||||
const downloadLink = document.createElement('a');
|
const downloadLink = document.createElement("a");
|
||||||
downloadLink.href = url;
|
downloadLink.href = url;
|
||||||
downloadLink.download = filenameToUse;
|
downloadLink.download = filenameToUse;
|
||||||
downloadLink.click();
|
downloadLink.click();
|
||||||
@ -152,18 +152,18 @@ export function runShell(commandName: string, args: string[], callback: (message
|
|||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
|
||||||
const comm = new Command(commandName, args);
|
const comm = new Command(commandName, args);
|
||||||
comm.on('close', data => {
|
comm.on("close", data => {
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
reject(new Error(`Command failed with exit code ${data.code} and signal ${data.signal}`));
|
reject(new Error(`Command failed with exit code ${data.code} and signal ${data.signal}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
comm.on('error', error => callback(error, "error"));
|
comm.on("error", error => { callback(error, "error") });
|
||||||
comm.stdout.on('data', line => callback(line, "stdout"));
|
comm.stdout.on("data", line => { callback(line, "stdout") });
|
||||||
comm.stderr.on('data', line => callback(line, "stderr"));
|
comm.stderr.on("data", line => { callback(line, "stderr") });
|
||||||
|
|
||||||
const child = await comm.spawn();
|
const child = await comm.spawn();
|
||||||
console.debug(`Started child process with pid: ${child.pid}`)
|
console.debug(`Started child process with pid: ${child.pid}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import "@stirling-pdf/shared-operations/src/locales/i18next.config";
|
import "@stirling-pdf/shared-operations/src/locales/i18next.config";
|
||||||
|
|
||||||
import express from 'express';
|
import express from "express";
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = 8000;
|
const PORT = 8000;
|
||||||
|
|
||||||
// server-node: backend api
|
// server-node: backend api
|
||||||
import api from './routes/api/api-controller';
|
import api from "./routes/api/api-controller";
|
||||||
app.use("/api", api);
|
app.use("/api", api);
|
||||||
|
|
||||||
// serve
|
// serve
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`http://localhost:${PORT}`);
|
console.log(`http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||||
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import express, { Request, Response } from 'express';
|
import express, { Request, Response } from "express";
|
||||||
|
|
||||||
import workflow from './workflow-controller';
|
import workflow from "./workflow-controller";
|
||||||
import dynamicOperations from './dynamic-operations-controller';
|
import dynamicOperations from "./dynamic-operations-controller";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import express, { Request, Response } from 'express';
|
import express, { Request, Response } from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import multer from 'multer';
|
import multer from "multer";
|
||||||
const upload = multer();
|
const upload = multer();
|
||||||
import { getOperatorByName } from '@stirling-pdf/shared-operations/src/workflow/getOperatorByName';
|
import { getOperatorByName } from "@stirling-pdf/shared-operations/src/workflow/getOperatorByName";
|
||||||
import { Operator } from '@stirling-pdf/shared-operations/src/functions';
|
import { Operator } from "@stirling-pdf/shared-operations/src/functions";
|
||||||
|
|
||||||
import { PdfFile } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile';
|
import { PdfFile } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||||
import { respondWithPdfFiles } from '../../utils/endpoint-utils';
|
import { respondWithPdfFiles } from "../../utils/endpoint-utils";
|
||||||
import { Action } from '@stirling-pdf/shared-operations/declarations/Action';
|
import { Action } from "@stirling-pdf/shared-operations/declarations/Action";
|
||||||
import { JoiPDFFileSchema } from '@stirling-pdf/shared-operations/src/wrappers/PdfFileJoi';
|
import { JoiPDFFileSchema } from "@stirling-pdf/shared-operations/src/wrappers/PdfFileJoi";
|
||||||
|
|
||||||
router.post('/:func', upload.array("file"), async function(req: Request, res: Response) {
|
router.post("/:func", upload.array("file"), async function(req: Request, res: Response) {
|
||||||
handleEndpoint(req, res);
|
handleEndpoint(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/:dir/:func', upload.array("file"), async function(req: Request, res: Response) {
|
router.post("/:dir/:func", upload.array("file"), async function(req: Request, res: Response) {
|
||||||
handleEndpoint(req, res);
|
handleEndpoint(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleEndpoint(req: Request, res: Response) {
|
function handleEndpoint(req: Request, res: Response) {
|
||||||
if(!req.files || req.files.length == 0) {
|
if(!req.files || req.files.length == 0) {
|
||||||
res.status(400).json({error: "no input file(s) were provided"})
|
res.status(400).json({error: "no input file(s) were provided"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,11 +46,11 @@ function handleEndpoint(req: Request, res: Response) {
|
|||||||
|
|
||||||
operation.run(validationResults.value.input, (progress) => {}).then(pdfFiles => {
|
operation.run(validationResults.value.input, (progress) => {}).then(pdfFiles => {
|
||||||
respondWithPdfFiles(res, pdfFiles, req.params.func + "_result");
|
respondWithPdfFiles(res, pdfFiles, req.params.func + "_result");
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(400).json({error: `the operator of type ${req.params.func} does not exist`})
|
res.status(400).json({error: `the operator of type ${req.params.func} does not exist`});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import express, { Request, Response } from 'express';
|
import express, { Request, Response } from "express";
|
||||||
import crypto from 'crypto';
|
import crypto from "crypto";
|
||||||
import multer from 'multer'
|
import multer from "multer";
|
||||||
const upload = multer();
|
const upload = multer();
|
||||||
|
|
||||||
import { traverseOperations } from "@stirling-pdf/shared-operations/src/workflow/traverseOperations";
|
import { traverseOperations } from "@stirling-pdf/shared-operations/src/workflow/traverseOperations";
|
||||||
import { PdfFile, RepresentationType } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile';
|
import { PdfFile, RepresentationType } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||||
import { respondWithPdfFiles } from '../../utils/endpoint-utils';
|
import { respondWithPdfFiles } from "../../utils/endpoint-utils";
|
||||||
import { JoiPDFFileSchema } from '@stirling-pdf/shared-operations/src/wrappers/PdfFileJoi';
|
import { JoiPDFFileSchema } from "@stirling-pdf/shared-operations/src/wrappers/PdfFileJoi";
|
||||||
|
|
||||||
interface Workflow {
|
interface Workflow {
|
||||||
eventStream?: express.Response<any, Record<string, any>>,
|
eventStream?: express.Response,
|
||||||
result?: PdfFile[],
|
result?: PdfFile[],
|
||||||
finished: boolean,
|
finished: boolean,
|
||||||
createdAt: EpochTimeStamp,
|
createdAt: EpochTimeStamp,
|
||||||
@ -73,19 +73,19 @@ router.post("/:workflowUuid?", [
|
|||||||
} else {
|
} else {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Start Aync Workflow");
|
console.log("Start Aync Workflow");
|
||||||
// TODO: UUID collision checks
|
// TODO: UUID collision checks
|
||||||
let workflowID = req.params.workflowUuid
|
let workflowID = req.params.workflowUuid;
|
||||||
if(!workflowID)
|
if(!workflowID)
|
||||||
workflowID = generateWorkflowID();
|
workflowID = generateWorkflowID();
|
||||||
|
|
||||||
activeWorkflows[workflowID] = {
|
activeWorkflows[workflowID] = {
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
finished: false
|
finished: false
|
||||||
}
|
};
|
||||||
const activeWorkflow = activeWorkflows[workflowID];
|
const activeWorkflow = activeWorkflows[workflowID];
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
@ -104,7 +104,7 @@ router.post("/:workflowUuid?", [
|
|||||||
activeWorkflow.eventStream.write(`data: ${state}\n\n`);
|
activeWorkflow.eventStream.write(`data: ${state}\n\n`);
|
||||||
}).then(async (pdfResults) => {
|
}).then(async (pdfResults) => {
|
||||||
if(activeWorkflow.eventStream) {
|
if(activeWorkflow.eventStream) {
|
||||||
activeWorkflow.eventStream.write(`data: processing done\n\n`);
|
activeWorkflow.eventStream.write("data: processing done\n\n");
|
||||||
activeWorkflow.eventStream.end();
|
activeWorkflow.eventStream.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,16 +170,16 @@ router.get("/progress-stream/:workflowUuid", (req: Request, res: Response) => {
|
|||||||
// TODO: Check if already done
|
// TODO: Check if already done
|
||||||
|
|
||||||
// Send realtime updates
|
// Send realtime updates
|
||||||
res.setHeader('Cache-Control', 'no-cache');
|
res.setHeader("Cache-Control", "no-cache");
|
||||||
res.setHeader('Content-Type', 'text/event-stream');
|
res.setHeader("Content-Type", "text/event-stream");
|
||||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
res.setHeader('Connection', 'keep-alive');
|
res.setHeader("Connection", "keep-alive");
|
||||||
res.flushHeaders(); // flush the headers to establish SSE with client
|
res.flushHeaders(); // flush the headers to establish SSE with client
|
||||||
|
|
||||||
const workflow = activeWorkflows[req.params.workflowUuid];
|
const workflow = activeWorkflows[req.params.workflowUuid];
|
||||||
workflow.eventStream = res;
|
workflow.eventStream = res;
|
||||||
|
|
||||||
res.on('close', () => {
|
res.on("close", () => {
|
||||||
res.end();
|
res.end();
|
||||||
// TODO: Abort if not already done?
|
// TODO: Abort if not already done?
|
||||||
});
|
});
|
||||||
@ -203,7 +203,7 @@ router.get("/result/:workflowUuid", async (req: Request, res: Response) => {
|
|||||||
const workflow = activeWorkflows[req.params.workflowUuid];
|
const workflow = activeWorkflows[req.params.workflowUuid];
|
||||||
if(!workflow.finished) {
|
if(!workflow.finished) {
|
||||||
res.status(202).json({ message: "Workflow hasn't finished yet. Check progress or connect to progress-steam to get notified when its done." });
|
res.status(202).json({ message: "Workflow hasn't finished yet. Check progress or connect to progress-steam to get notified when its done." });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await respondWithPdfFiles(res, workflow.result, "workflow-results");
|
await respondWithPdfFiles(res, workflow.result, "workflow-results");
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
import { Response } from 'express';
|
import { Response } from "express";
|
||||||
import { PdfFile } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile'
|
import { PdfFile } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||||
import Archiver from 'archiver';
|
import Archiver from "archiver";
|
||||||
|
|
||||||
export async function respondWithFile(res: Response, uint8Array: Uint8Array, filename: string, mimeType: string): Promise<void> {
|
export async function respondWithFile(res: Response, uint8Array: Uint8Array, filename: string, mimeType: string): Promise<void> {
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': mimeType,
|
"Content-Type": mimeType,
|
||||||
'Content-disposition': `attachment; filename="${filename}"`,
|
"Content-disposition": `attachment; filename="${filename}"`,
|
||||||
'Content-Length': uint8Array.length
|
"Content-Length": uint8Array.length
|
||||||
});
|
});
|
||||||
res.end(uint8Array);
|
res.end(uint8Array);
|
||||||
}
|
}
|
||||||
@ -23,14 +23,14 @@ export async function respondWithZip(res: Response, filename: string, files: {ui
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(filename)
|
console.log(filename);
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'application/zip',
|
"Content-Type": "application/zip",
|
||||||
'Content-disposition': `attachment; filename="${filename}.zip"`,
|
"Content-disposition": `attachment; filename="${filename}.zip"`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Also allow changing the compression level
|
// TODO: Also allow changing the compression level
|
||||||
var zip = Archiver('zip');
|
const zip = Archiver("zip");
|
||||||
|
|
||||||
// Stream the file to the user.
|
// Stream the file to the user.
|
||||||
zip.pipe(res);
|
zip.pipe(res);
|
||||||
@ -50,10 +50,10 @@ export async function respondWithPdfFiles(res: Response, pdfFiles: PdfFile[] | u
|
|||||||
res.status(500).json({"warning": "The workflow had no outputs."});
|
res.status(500).json({"warning": "The workflow had no outputs."});
|
||||||
}
|
}
|
||||||
else if (pdfFiles.length == 1) {
|
else if (pdfFiles.length == 1) {
|
||||||
respondWithPdfFile(res, pdfFiles[0])
|
respondWithPdfFile(res, pdfFiles[0]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const promises = pdfFiles.map(async (pdf) => {return{uint8Array: await pdf.uint8Array, filename: pdf.filename + ".pdf"}})
|
const promises = pdfFiles.map(async (pdf) => {return{uint8Array: await pdf.uint8Array, filename: pdf.filename + ".pdf"}});
|
||||||
const files = await Promise.all(promises);
|
const files = await Promise.all(promises);
|
||||||
respondWithZip(res, filename, files);
|
respondWithZip(res, filename, files);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import os from 'os';
|
import os from "os";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import { exec, spawn } from 'child_process'
|
import { exec, spawn } from "child_process";
|
||||||
import { PdfFile, RepresentationType } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile'
|
import { PdfFile, RepresentationType } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||||
|
|
||||||
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
|
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
|
||||||
const parentDir = path.join(os.tmpdir(), "StirlingPDF");
|
const parentDir = path.join(os.tmpdir(), "StirlingPDF");
|
||||||
@ -46,14 +46,14 @@ export function isLibreOfficeInstalled() {
|
|||||||
const result = stdout.match("LibreOffice ([0-9]+\.){4}.*");
|
const result = stdout.match("LibreOffice ([0-9]+\.){4}.*");
|
||||||
resolve(result ? true : false);
|
resolve(result ? true : false);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeBytesToFile(filePath: string, bytes: Uint8Array): Promise<void> {
|
function writeBytesToFile(filePath: string, bytes: Uint8Array): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(filePath, bytes, function(err) {
|
fs.writeFile(filePath, bytes, function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
reject(err)
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
@ -80,17 +80,17 @@ function runLibreOfficeCommand(idKey: string, args: string[]): Promise<string[]>
|
|||||||
|
|
||||||
const process = spawn("libreoffice", args);
|
const process = spawn("libreoffice", args);
|
||||||
|
|
||||||
process.stdout.on('data', (data) => {
|
process.stdout.on("data", (data) => {
|
||||||
const dataStr = data.toString();
|
const dataStr = data.toString();
|
||||||
console.log(`Progress ${idKey}:`, dataStr);
|
console.log(`Progress ${idKey}:`, dataStr);
|
||||||
messageList.push(dataStr);
|
messageList.push(dataStr);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.stderr.on('data', (data) => {
|
process.stderr.on("data", (data) => {
|
||||||
console.error(`stderr ${idKey}:`, data.toString());
|
console.error(`stderr ${idKey}:`, data.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('exit', (code) => {
|
process.on("exit", (code) => {
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve(messageList);
|
resolve(messageList);
|
||||||
} else {
|
} else {
|
||||||
@ -98,7 +98,7 @@ function runLibreOfficeCommand(idKey: string, args: string[]): Promise<string[]>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('error', (err) => {
|
process.on("error", (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
2
shared-operations/declarations/pdfcpu.d.ts
vendored
2
shared-operations/declarations/pdfcpu.d.ts
vendored
@ -1,3 +1,3 @@
|
|||||||
declare module '#pdfcpu' {
|
declare module "#pdfcpu" {
|
||||||
export function oneToOne(wasmArray: string[], snapshot: Uint8Array): Promise<Uint8Array>;
|
export function oneToOne(wasmArray: string[], snapshot: Uint8Array): Promise<Uint8Array>;
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
import { PdfFile } from "../wrappers/PdfFile.js";
|
||||||
import { Sorts } from './common/pageIndexesSorting.js';
|
import { Sorts } from "./common/pageIndexesSorting.js";
|
||||||
import { getPages } from './common/getPagesByIndex.js';
|
import { getPages } from "./common/getPagesByIndex.js";
|
||||||
import { parsePageIndexSpecification } from './common/pageIndexesUtils.js';
|
import { parsePageIndexSpecification } from "./common/pageIndexesUtils.js";
|
||||||
|
|
||||||
export type ArrangePagesParamsType = {
|
export interface ArrangePagesParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
arrangementConfig: string; // a member of Sorts, or a page index specification
|
arrangementConfig: string; // a member of Sorts, or a page index specification
|
||||||
}
|
}
|
||||||
@ -22,6 +22,6 @@ export async function arrangePages(params: ArrangePagesParamsType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newFile = await getPages(file, sortIndexes);
|
const newFile = await getPages(file, sortIndexes);
|
||||||
newFile.filename += "arrangedPages"
|
newFile.filename += "arrangedPages";
|
||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../../wrappers/PdfFile';
|
import { PdfFile } from "../../wrappers/PdfFile";
|
||||||
import { PDFPageProxy } from "pdfjs-dist/types/src/display/api.js";
|
import { PDFPageProxy } from "pdfjs-dist/types/src/display/api.js";
|
||||||
import { Image, ImageKind } from 'image-js';
|
import { Image, ImageKind } from "image-js";
|
||||||
|
|
||||||
import { getImagesOnPage, PDFJSImage } from "./getImagesOnPage.js";
|
import { getImagesOnPage, PDFJSImage } from "./getImagesOnPage.js";
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ async function areImagesBlank(page: PDFPageProxy, threshold: number): Promise<bo
|
|||||||
|
|
||||||
// TODO: Fix this function
|
// TODO: Fix this function
|
||||||
async function isImageBlank(image: PDFJSImage, threshold: number): Promise<boolean> {
|
async function isImageBlank(image: PDFJSImage, threshold: number): Promise<boolean> {
|
||||||
var img = new Image(image.width, image.height, image.data, { kind: "RGB" as ImageKind }); // TODO: Maybe respect image.kind and convert accordingly, needs to be tested with a pdf with alpha-image
|
const img = new Image(image.width, image.height, image.data, { kind: "RGB" as ImageKind }); // TODO: Maybe respect image.kind and convert accordingly, needs to be tested with a pdf with alpha-image
|
||||||
var grey = img.grey();
|
const grey = img.grey();
|
||||||
var mean = grey.getMean();
|
const mean = grey.getMean();
|
||||||
return mean[0] <= threshold;
|
return mean[0] <= threshold;
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import jsQR from "jsqr";
|
import jsQR from "jsqr";
|
||||||
|
|
||||||
import { PdfFile } from '../../wrappers/PdfFile.js';
|
import { PdfFile } from "../../wrappers/PdfFile.js";
|
||||||
import { getImagesOnPage, PDFJSImage } from "./getImagesOnPage.js";
|
import { getImagesOnPage, PDFJSImage } from "./getImagesOnPage.js";
|
||||||
|
|
||||||
export async function detectQRCodePages(file: PdfFile) {
|
export async function detectQRCodePages(file: PdfFile) {
|
||||||
@ -24,7 +24,7 @@ export async function detectQRCodePages(file: PdfFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(pagesWithQR.length == 0) {
|
if(pagesWithQR.length == 0) {
|
||||||
console.warn("Could not find any QR Codes in the provided PDF.")
|
console.warn("Could not find any QR Codes in the provided PDF.");
|
||||||
}
|
}
|
||||||
return pagesWithQR;
|
return pagesWithQR;
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
|
|
||||||
import { PDFPageProxy } from "pdfjs-dist/types/src/display/api.js";
|
import { PDFPageProxy } from "pdfjs-dist/types/src/display/api.js";
|
||||||
|
|
||||||
import * as PDFJS from 'pdfjs-dist';
|
import * as PDFJS from "pdfjs-dist";
|
||||||
|
|
||||||
export type PDFJSImage = {
|
export interface PDFJSImage {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
interpolate?: any;
|
interpolate?: any;
|
||||||
kind: number; // TODO: Document what this is, maybe hasAlpha?
|
kind: number; // TODO: Document what this is, maybe hasAlpha?
|
||||||
data: Uint8ClampedArray;
|
data: Uint8ClampedArray;
|
||||||
};
|
}
|
||||||
|
|
||||||
export async function getImagesOnPage(page: PDFPageProxy): Promise<PDFJSImage[]> {
|
export async function getImagesOnPage(page: PDFPageProxy): Promise<PDFJSImage[]> {
|
||||||
const ops = await page.getOperatorList();
|
const ops = await page.getOperatorList();
|
||||||
const images: PDFJSImage[] = [];
|
const images: PDFJSImage[] = [];
|
||||||
for (var j=0; j < ops.fnArray.length; j++) {
|
for (let j=0; j < ops.fnArray.length; j++) {
|
||||||
if (ops.fnArray[j] == PDFJS.OPS.paintImageXObject) {
|
if (ops.fnArray[j] == PDFJS.OPS.paintImageXObject) {
|
||||||
const image = page.objs.get(ops.argsArray[j][0]) as PDFJSImage;
|
const image = page.objs.get(ops.argsArray[j][0]) as PDFJSImage;
|
||||||
images.push(image);
|
images.push(image);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import { PdfFile, RepresentationType } from '../../wrappers/PdfFile.js';
|
import { PdfFile, RepresentationType } from "../../wrappers/PdfFile.js";
|
||||||
import { PDFDocument } from 'pdf-lib';
|
import { PDFDocument } from "pdf-lib";
|
||||||
|
|
||||||
export async function getPages(file: PdfFile, pageIndexes: number[]): Promise<PdfFile> {
|
export async function getPages(file: PdfFile, pageIndexes: number[]): Promise<PdfFile> {
|
||||||
const pdfLibDocument = await file.pdfLibDocument;
|
const pdfLibDocument = await file.pdfLibDocument;
|
||||||
|
@ -108,9 +108,7 @@ function removeFirstAndLast(totalPages: number): number[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type SortFunction = (totalPages: number) => number[];
|
export type SortFunction = (totalPages: number) => number[];
|
||||||
type Sorts = {
|
type Sorts = Record<string, SortFunction>;
|
||||||
[key: string]: SortFunction;
|
|
||||||
};
|
|
||||||
export const Sorts: Sorts = Object.freeze({
|
export const Sorts: Sorts = Object.freeze({
|
||||||
"REVERSE_ORDER": reverseSort,
|
"REVERSE_ORDER": reverseSort,
|
||||||
"DUPLEX_SORT": duplexSort,
|
"DUPLEX_SORT": duplexSort,
|
||||||
|
@ -18,7 +18,7 @@ export function invertSelection(selection: number[], pages: number|number[]): nu
|
|||||||
*/
|
*/
|
||||||
export function parsePageIndexSpecification(specification: string, totalPages: number): number[] {
|
export function parsePageIndexSpecification(specification: string, totalPages: number): number[] {
|
||||||
// Translated to JS from the original Java function
|
// Translated to JS from the original Java function
|
||||||
const pageOrderArr = specification.split(",")
|
const pageOrderArr = specification.split(",");
|
||||||
const newPageOrder: number[] = [];
|
const newPageOrder: number[] = [];
|
||||||
|
|
||||||
// loop through the page order array
|
// loop through the page order array
|
||||||
@ -32,13 +32,13 @@ export function parsePageIndexSpecification(specification: string, totalPages: n
|
|||||||
}
|
}
|
||||||
else if (element.match("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
|
else if (element.match("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
|
||||||
// Handle page order as a function
|
// Handle page order as a function
|
||||||
var coefficient = 0;
|
let coefficient = 0;
|
||||||
var constant = 0;
|
let constant = 0;
|
||||||
var coefficientExists = false;
|
let coefficientExists = false;
|
||||||
var constantExists = false;
|
let constantExists = false;
|
||||||
|
|
||||||
if (element.includes("n")) {
|
if (element.includes("n")) {
|
||||||
var parts = element.split("n");
|
const parts = element.split("n");
|
||||||
if (!parts[0]) {
|
if (!parts[0]) {
|
||||||
coefficient = parseInt(parts[0]);
|
coefficient = parseInt(parts[0]);
|
||||||
coefficientExists = true;
|
coefficientExists = true;
|
||||||
@ -53,7 +53,7 @@ export function parsePageIndexSpecification(specification: string, totalPages: n
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 1; i <= totalPages; i++) {
|
for (var i = 1; i <= totalPages; i++) {
|
||||||
var pageNum = coefficientExists ? coefficient * i : i;
|
let pageNum = coefficientExists ? coefficient * i : i;
|
||||||
pageNum += constantExists ? constant : 0;
|
pageNum += constantExists ? constant : 0;
|
||||||
|
|
||||||
if (pageNum <= totalPages && pageNum > 0) {
|
if (pageNum <= totalPages && pageNum > 0) {
|
||||||
@ -64,13 +64,13 @@ export function parsePageIndexSpecification(specification: string, totalPages: n
|
|||||||
// split the range into start and end page
|
// split the range into start and end page
|
||||||
const range = element.split("-");
|
const range = element.split("-");
|
||||||
const start = parseInt(range[0]);
|
const start = parseInt(range[0]);
|
||||||
var end = parseInt(range[1]);
|
let end = parseInt(range[1]);
|
||||||
// check if the end page is greater than total pages
|
// check if the end page is greater than total pages
|
||||||
if (end > totalPages) {
|
if (end > totalPages) {
|
||||||
end = totalPages;
|
end = totalPages;
|
||||||
}
|
}
|
||||||
// loop through the range of pages
|
// loop through the range of pages
|
||||||
for (var j = start; j <= end; j++) {
|
for (let j = start; j <= end; j++) {
|
||||||
// print the current index
|
// print the current index
|
||||||
newPageOrder.push(j - 1);
|
newPageOrder.push(j - 1);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../../wrappers/PdfFile';
|
import { PdfFile } from "../../wrappers/PdfFile";
|
||||||
|
|
||||||
export async function sortPdfArray(
|
export async function sortPdfArray(
|
||||||
files: PdfFile[],
|
files: PdfFile[],
|
||||||
sortType: "orderProvided"|"byFileName"|"byDateModified"|"byDateCreated"|"byPDFTitle" = "orderProvided"
|
sortType: "orderProvided"|"byFileName"|"byDateModified"|"byDateCreated"|"byPDFTitle" = "orderProvided"
|
||||||
): Promise<PdfFile[]> {
|
): Promise<PdfFile[]> {
|
||||||
|
|
||||||
const docCache = await PdfFile.cacheAsPdfLibDocuments(files);
|
const docCache = await PdfFile.cacheAsPdfLibDocuments(files);
|
||||||
|
|
||||||
@ -19,24 +19,24 @@ export async function sortPdfArray(
|
|||||||
break;
|
break;
|
||||||
case "byDateModified":
|
case "byDateModified":
|
||||||
files.sort((a, b) => {
|
files.sort((a, b) => {
|
||||||
const ad = docCache.get(a)?.getModificationDate()?.getTime();
|
const ad = docCache.get(a).getModificationDate().getTime();
|
||||||
const bd = docCache.get(b)?.getModificationDate()?.getTime();
|
const bd = docCache.get(b).getModificationDate().getTime();
|
||||||
if (!ad || !bd) return 0;
|
if (!ad || !bd) return 0;
|
||||||
return ad > bd ? 1 : -1
|
return ad > bd ? 1 : -1;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "byDateCreated":
|
case "byDateCreated":
|
||||||
files.sort((a, b) => {
|
files.sort((a, b) => {
|
||||||
const ad = docCache.get(a)?.getCreationDate()?.getTime();
|
const ad = docCache.get(a).getCreationDate().getTime();
|
||||||
const bd = docCache.get(b)?.getCreationDate()?.getTime();
|
const bd = docCache.get(b).getCreationDate().getTime();
|
||||||
if (!ad || !bd) return 0;
|
if (!ad || !bd) return 0;
|
||||||
return ad > bd ? 1 : -1
|
return ad > bd ? 1 : -1;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "byPDFTitle":
|
case "byPDFTitle":
|
||||||
files.sort((a, b) => {
|
files.sort((a, b) => {
|
||||||
const ad = docCache.get(a)?.getTitle();
|
const ad = docCache.get(a).getTitle();
|
||||||
const bd = docCache.get(b)?.getTitle();
|
const bd = docCache.get(b).getTitle();
|
||||||
if (!ad || !bd) return 0;
|
if (!ad || !bd) return 0;
|
||||||
return ad.localeCompare(bd);
|
return ad.localeCompare(bd);
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../../wrappers/PdfFile.js';
|
import { PdfFile } from "../../wrappers/PdfFile.js";
|
||||||
import { getPages } from "./getPagesByIndex";
|
import { getPages } from "./getPagesByIndex";
|
||||||
|
|
||||||
export async function splitPagesByIndex(file: PdfFile, splitAfterPageIndexes: number[]): Promise<PdfFile[]> {
|
export async function splitPagesByIndex(file: PdfFile, splitAfterPageIndexes: number[]): Promise<PdfFile[]> {
|
||||||
@ -22,4 +22,4 @@ export async function splitPagesByIndex(file: PdfFile, splitAfterPageIndexes: nu
|
|||||||
pagesArray = [];
|
pagesArray = [];
|
||||||
|
|
||||||
return subDocuments;
|
return subDocuments;
|
||||||
};
|
}
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
import { PdfFile } from "../wrappers/PdfFile.js";
|
||||||
import { getPages } from './common/getPagesByIndex.js';
|
import { getPages } from "./common/getPagesByIndex.js";
|
||||||
import { parsePageIndexSpecification } from './common/pageIndexesUtils'
|
import { parsePageIndexSpecification } from "./common/pageIndexesUtils";
|
||||||
|
|
||||||
export type ExtractPagesParamsType = {
|
export interface ExtractPagesParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
pageIndexes: string | number[];
|
pageIndexes: string | number[];
|
||||||
}
|
}
|
||||||
@ -11,13 +11,13 @@ export async function extractPages(params: ExtractPagesParamsType): Promise<PdfF
|
|||||||
const { file, pageIndexes } = params;
|
const { file, pageIndexes } = params;
|
||||||
const pdfLibDocument = await file.pdfLibDocument;
|
const pdfLibDocument = await file.pdfLibDocument;
|
||||||
|
|
||||||
var indexes = pageIndexes;
|
let indexes = pageIndexes;
|
||||||
|
|
||||||
if (!Array.isArray(indexes)) {
|
if (!Array.isArray(indexes)) {
|
||||||
indexes = parsePageIndexSpecification(indexes, pdfLibDocument.getPageCount());
|
indexes = parsePageIndexSpecification(indexes, pdfLibDocument.getPageCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
const newFile = await getPages(file, indexes);
|
const newFile = await getPages(file, indexes);
|
||||||
newFile.filename += "_extractedPages"
|
newFile.filename += "_extractedPages";
|
||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,20 @@ import * as pdfcpuWrapper from "#pdfcpu"; // This is updated by tsconfig.json/pa
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { JoiPDFFileSchema } from "../wrappers/PdfFileJoi";
|
import { JoiPDFFileSchema } from "../wrappers/PdfFileJoi";
|
||||||
|
|
||||||
import i18next from 'i18next';
|
import i18next from "i18next";
|
||||||
i18next.loadNamespaces('impose', (err, t) => { if (err) throw err; });
|
i18next.loadNamespaces("impose", (err, t) => { if (err) throw err; });
|
||||||
|
|
||||||
export class Impose extends Operator {
|
export class Impose extends Operator {
|
||||||
static type: string = "impose";
|
static type = "impose";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validation & Localisation
|
* Validation & Localisation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected static inputSchema = JoiPDFFileSchema.label(i18next.t('inputs.pdffile.name')).description(i18next.t('inputs.pdffile.description'));
|
protected static inputSchema = JoiPDFFileSchema.label(i18next.t("inputs.pdffile.name")).description(i18next.t("inputs.pdffile.description"));
|
||||||
protected static valueSchema = Joi.object({
|
protected static valueSchema = Joi.object({
|
||||||
nup: Joi.number().integer().valid(2, 3, 4, 8, 9, 12, 16).required()
|
nup: Joi.number().integer().valid(2, 3, 4, 8, 9, 12, 16).required()
|
||||||
.label(i18next.t('values.nup.friendlyName', { ns: 'impose' })).description(i18next.t('values.nup.description', { ns: 'impose' }))
|
.label(i18next.t("values.nup.friendlyName", { ns: "impose" })).description(i18next.t("values.nup.description", { ns: "impose" }))
|
||||||
.example("3").example("4"),
|
.example("3").example("4"),
|
||||||
format: Joi.string().valid(...[
|
format: Joi.string().valid(...[
|
||||||
// ISO 216:1975 A
|
// ISO 216:1975 A
|
||||||
@ -62,16 +62,16 @@ export class Impose extends Operator {
|
|||||||
"JIS-B7", "JIS-B8", "JIS-B9", "JIS-B10", "JIS-B11", "JIS-B12",
|
"JIS-B7", "JIS-B8", "JIS-B9", "JIS-B10", "JIS-B11", "JIS-B12",
|
||||||
"Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen"
|
"Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen"
|
||||||
].flatMap(size => [size, size + "P", size + "L"])).required()
|
].flatMap(size => [size, size + "P", size + "L"])).required()
|
||||||
.label(i18next.t('values.format.friendlyName', { ns: 'impose' })).description(i18next.t('values.format.description', { ns: 'impose' }))
|
.label(i18next.t("values.format.friendlyName", { ns: "impose" })).description(i18next.t("values.format.description", { ns: "impose" }))
|
||||||
.example("A4").example("A3L")
|
.example("A4").example("A3L")
|
||||||
});
|
});
|
||||||
protected static outputSchema = JoiPDFFileSchema.label(i18next.t('outputs.pdffile.name')).description(i18next.t('outputs.pdffile.description'));
|
protected static outputSchema = JoiPDFFileSchema.label(i18next.t("outputs.pdffile.name")).description(i18next.t("outputs.pdffile.description"));
|
||||||
|
|
||||||
static schema = Joi.object({
|
static schema = Joi.object({
|
||||||
input: Impose.inputSchema,
|
input: Impose.inputSchema,
|
||||||
values: Impose.valueSchema.required(),
|
values: Impose.valueSchema.required(),
|
||||||
output: Impose.outputSchema
|
output: Impose.outputSchema
|
||||||
}).label(i18next.t('friendlyName', { ns: 'impose' })).description(i18next.t('description', { ns: 'impose' }));
|
}).label(i18next.t("friendlyName", { ns: "impose" })).description(i18next.t("description", { ns: "impose" }));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +89,7 @@ export class Impose extends Operator {
|
|||||||
"nup",
|
"nup",
|
||||||
"-c",
|
"-c",
|
||||||
"disable",
|
"disable",
|
||||||
'f:' + this.actionValues.format,
|
"f:" + this.actionValues.format,
|
||||||
"/output.pdf",
|
"/output.pdf",
|
||||||
String(this.actionValues.nup),
|
String(this.actionValues.nup),
|
||||||
"input.pdf",
|
"input.pdf",
|
||||||
@ -104,10 +104,10 @@ export class Impose extends Operator {
|
|||||||
input.filename + "_imposed"
|
input.filename + "_imposed"
|
||||||
);
|
);
|
||||||
|
|
||||||
progressCallback({ curFileProgress: 1, operationProgress: index/max })
|
progressCallback({ curFileProgress: 1, operationProgress: index/max });
|
||||||
|
|
||||||
console.log("ImposeResult: ", result);
|
console.log("ImposeResult: ", result);
|
||||||
return result;
|
return result;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ export async function nToOne <I, O>(inputs: I[], callback: (input: I[]) => Promi
|
|||||||
|
|
||||||
/** This function should be used if the Operation takes one file as input and may output multiple files */
|
/** This function should be used if the Operation takes one file as input and may output multiple files */
|
||||||
export async function oneToN <I, O>(inputs: I[], callback: (input: I, index: number, max: number) => Promise<O[]>): Promise<O[]> {
|
export async function oneToN <I, O>(inputs: I[], callback: (input: I, index: number, max: number) => Promise<O[]>): Promise<O[]> {
|
||||||
let output: O[] = []
|
let output: O[] = [];
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
output = output.concat(await callback(inputs[i], i, inputs.length));
|
output = output.concat(await callback(inputs[i], i, inputs.length));
|
||||||
}
|
}
|
||||||
@ -55,6 +55,6 @@ export async function oneToN <I, O>(inputs: I[], callback: (input: I, index: num
|
|||||||
/** This function should be used if the Operation takes one file as input and outputs only one file */
|
/** This function should be used if the Operation takes one file as input and outputs only one file */
|
||||||
export async function oneToOne <I, O>(inputs: I[], callback: (input: I, index: number, max: number) => Promise<O>): Promise<O[]> {
|
export async function oneToOne <I, O>(inputs: I[], callback: (input: I, index: number, max: number) => Promise<O>): Promise<O[]> {
|
||||||
return oneToN(inputs, async (input, index, max) => {
|
return oneToN(inputs, async (input, index, max) => {
|
||||||
return [await callback(input, index, max)]
|
return [await callback(input, index, max)];
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import { PDFDocument } from 'pdf-lib';
|
import { PDFDocument } from "pdf-lib";
|
||||||
import { PdfFile, RepresentationType } from '../wrappers/PdfFile';
|
import { PdfFile, RepresentationType } from "../wrappers/PdfFile";
|
||||||
|
|
||||||
export type MergeParamsType = {
|
export interface MergeParamsType {
|
||||||
files: PdfFile[];
|
files: PdfFile[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,6 +15,6 @@ export async function mergePDFs(params: MergeParamsType): Promise<PdfFile> {
|
|||||||
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
const newName = "("+params.files.map(input => input.filename).join("_and_") + ")_merged"
|
const newName = "("+params.files.map(input => input.filename).join("_and_") + ")_merged";
|
||||||
return new PdfFile("mergedPDF", mergedPdf, RepresentationType.PDFLibDocument, newName);
|
return new PdfFile("mergedPDF", mergedPdf, RepresentationType.PDFLibDocument, newName);
|
||||||
};
|
}
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
import { PdfFile } from "../wrappers/PdfFile.js";
|
||||||
import { detectEmptyPages } from './common/detectEmptyPages.js';
|
import { detectEmptyPages } from "./common/detectEmptyPages.js";
|
||||||
import { getPages } from './common/getPagesByIndex.js';
|
import { getPages } from "./common/getPagesByIndex.js";
|
||||||
import { invertSelection } from './common/pageIndexesUtils.js';
|
import { invertSelection } from "./common/pageIndexesUtils.js";
|
||||||
|
|
||||||
export type RemoveBlankPagesParamsType = {
|
export interface RemoveBlankPagesParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
whiteThreashold: number;
|
whiteThreashold: number;
|
||||||
}
|
}
|
||||||
@ -15,9 +15,9 @@ export async function removeBlankPages(params: RemoveBlankPagesParamsType) {
|
|||||||
|
|
||||||
const emptyPages = await detectEmptyPages(file, whiteThreashold);
|
const emptyPages = await detectEmptyPages(file, whiteThreashold);
|
||||||
console.debug("Empty Pages: ", emptyPages);
|
console.debug("Empty Pages: ", emptyPages);
|
||||||
const pagesToKeep = invertSelection(emptyPages, pageCount)
|
const pagesToKeep = invertSelection(emptyPages, pageCount);
|
||||||
|
|
||||||
const newFile = await getPages(file, pagesToKeep);
|
const newFile = await getPages(file, pagesToKeep);
|
||||||
newFile.filename += "_removedBlanks"
|
newFile.filename += "_removedBlanks";
|
||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
import { PdfFile } from "../wrappers/PdfFile.js";
|
||||||
import { getPages } from './common/getPagesByIndex.js';
|
import { getPages } from "./common/getPagesByIndex.js";
|
||||||
import { invertSelection, parsePageIndexSpecification } from './common/pageIndexesUtils.js';
|
import { invertSelection, parsePageIndexSpecification } from "./common/pageIndexesUtils.js";
|
||||||
|
|
||||||
export type RemovePagesParamsType = {
|
export interface RemovePagesParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
pageSelector: string;
|
pageSelector: string;
|
||||||
}
|
}
|
||||||
@ -16,6 +16,6 @@ export async function removePages(params: RemovePagesParamsType) {
|
|||||||
const pagesToKeep = invertSelection(pageSelection, pageCount);
|
const pagesToKeep = invertSelection(pageSelection, pageCount);
|
||||||
|
|
||||||
const newFile = await getPages(file, pagesToKeep);
|
const newFile = await getPages(file, pagesToKeep);
|
||||||
newFile.filename += "_removedPages"
|
newFile.filename += "_removedPages";
|
||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import { degrees } from 'pdf-lib';
|
import { degrees } from "pdf-lib";
|
||||||
import { PdfFile, RepresentationType } from '../wrappers/PdfFile';
|
import { PdfFile, RepresentationType } from "../wrappers/PdfFile";
|
||||||
|
|
||||||
export type RotateParamsType = {
|
export interface RotateParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
rotation: number|number[];
|
rotation: number|number[];
|
||||||
}
|
}
|
||||||
@ -15,19 +15,19 @@ export async function rotatePages(params: RotateParamsType): Promise<PdfFile> {
|
|||||||
|
|
||||||
if (Array.isArray(rotation)) {
|
if (Array.isArray(rotation)) {
|
||||||
if (rotation.length != pages.length) {
|
if (rotation.length != pages.length) {
|
||||||
throw new Error(`Number of given rotations '${rotation.length}' is not the same as the number of pages '${pages.length}'`)
|
throw new Error(`Number of given rotations '${rotation.length}' is not the same as the number of pages '${pages.length}'`);
|
||||||
}
|
}
|
||||||
for (let i=0; i<rotation.length; i++) {
|
for (let i=0; i<rotation.length; i++) {
|
||||||
const oldRotation = pages[i].getRotation().angle
|
const oldRotation = pages[i].getRotation().angle;
|
||||||
pages[i].setRotation(degrees(oldRotation + rotation[i]))
|
pages[i].setRotation(degrees(oldRotation + rotation[i]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pages.forEach(page => {
|
pages.forEach(page => {
|
||||||
// Change page size
|
// Change page size
|
||||||
const oldRotation = page.getRotation().angle
|
const oldRotation = page.getRotation().angle;
|
||||||
page.setRotation(degrees(oldRotation + rotation))
|
page.setRotation(degrees(oldRotation + rotation));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PdfFile(file.originalFilename, pdfDoc, RepresentationType.PDFLibDocument, file.filename+"_rotated");
|
return new PdfFile(file.originalFilename, pdfDoc, RepresentationType.PDFLibDocument, file.filename+"_rotated");
|
||||||
};
|
}
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import { PDFPage } from 'pdf-lib';
|
import { PDFPage } from "pdf-lib";
|
||||||
import { PdfFile, RepresentationType } from '../wrappers/PdfFile';
|
import { PdfFile, RepresentationType } from "../wrappers/PdfFile";
|
||||||
|
|
||||||
export type ScaleContentParamsType = {
|
export interface ScaleContentParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
scaleFactor: number|number[];
|
scaleFactor: number|number[];
|
||||||
}
|
}
|
||||||
@ -15,17 +15,17 @@ export async function scaleContent(params: ScaleContentParamsType): Promise<PdfF
|
|||||||
|
|
||||||
if (Array.isArray(scaleFactor)) {
|
if (Array.isArray(scaleFactor)) {
|
||||||
if (scaleFactor.length != pages.length) {
|
if (scaleFactor.length != pages.length) {
|
||||||
throw new Error(`Number of given scale factors '${scaleFactor.length}' is not the same as the number of pages '${pages.length}'`)
|
throw new Error(`Number of given scale factors '${scaleFactor.length}' is not the same as the number of pages '${pages.length}'`);
|
||||||
}
|
}
|
||||||
for (let i=0; i<scaleFactor.length; i++) {
|
for (let i=0; i<scaleFactor.length; i++) {
|
||||||
scalePage(pages[i], scaleFactor[i]);
|
scalePage(pages[i], scaleFactor[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pages.forEach(page => scalePage(page, scaleFactor));
|
pages.forEach(page => { scalePage(page, scaleFactor) });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PdfFile(file.originalFilename, pdfDoc, RepresentationType.PDFLibDocument, file.filename+"_scaledContent");
|
return new PdfFile(file.originalFilename, pdfDoc, RepresentationType.PDFLibDocument, file.filename+"_scaledContent");
|
||||||
};
|
}
|
||||||
|
|
||||||
function scalePage(page: PDFPage, scaleFactor: number) {
|
function scalePage(page: PDFPage, scaleFactor: number) {
|
||||||
const width = page.getWidth();
|
const width = page.getWidth();
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
|
|
||||||
import Joi from 'joi';
|
import Joi from "joi";
|
||||||
import { PDFPage } from 'pdf-lib';
|
import { PDFPage } from "pdf-lib";
|
||||||
import { PdfFile, RepresentationType, JoiPDFFileSchema } from '../wrappers/PdfFileJoi';
|
import { PdfFile, RepresentationType, JoiPDFFileSchema } from "../wrappers/PdfFileJoi";
|
||||||
|
|
||||||
const whSchema = Joi.string().custom((value, helpers) => {
|
const whSchema = Joi.string().custom((value, helpers) => {
|
||||||
console.log("value.pageSize", typeof value)
|
console.log("value.pageSize", typeof value);
|
||||||
try {
|
try {
|
||||||
const obj = JSON.parse(value);
|
const obj = JSON.parse(value);
|
||||||
if (!obj.width && !obj.height) {
|
if (!obj.width && !obj.height) {
|
||||||
return helpers.error('any.required', { message: 'At least one of width/height must be present' });
|
return helpers.error("any.required", { message: "At least one of width/height must be present" });
|
||||||
}
|
}
|
||||||
if (typeof obj.width != 'number' && typeof obj.width != 'undefined') {
|
if (typeof obj.width != "number" && typeof obj.width != "undefined") {
|
||||||
return helpers.error('any.invalid', { message: 'Width must be a number if present' });
|
return helpers.error("any.invalid", { message: "Width must be a number if present" });
|
||||||
}
|
}
|
||||||
if (typeof obj.height != 'number' && typeof obj.height != 'undefined') {
|
if (typeof obj.height != "number" && typeof obj.height != "undefined") {
|
||||||
return helpers.error('any.invalid', { message: 'Height must be a number if present' });
|
return helpers.error("any.invalid", { message: "Height must be a number if present" });
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return helpers.error('any.invalid', { message: 'Value must be a valid JSON' });
|
return helpers.error("any.invalid", { message: "Value must be a valid JSON" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ export const ScalePageSchema = Joi.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export type ScalePageParamsType = {
|
export interface ScalePageParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
pageSize: { width?:number,height?:number }|{ width?:number,height?:number }[];
|
pageSize: { width?:number,height?:number }|{ width?:number,height?:number }[];
|
||||||
}
|
}
|
||||||
@ -41,17 +41,17 @@ export async function scalePage(params: ScalePageParamsType): Promise<PdfFile> {
|
|||||||
|
|
||||||
if (Array.isArray(pageSize)) {
|
if (Array.isArray(pageSize)) {
|
||||||
if (pageSize.length != pages.length) {
|
if (pageSize.length != pages.length) {
|
||||||
throw new Error(`Number of given sizes '${pageSize.length}' is not the same as the number of pages '${pages.length}'`)
|
throw new Error(`Number of given sizes '${pageSize.length}' is not the same as the number of pages '${pages.length}'`);
|
||||||
}
|
}
|
||||||
for (let i=0; i<pageSize.length; i++) {
|
for (let i=0; i<pageSize.length; i++) {
|
||||||
resize(pages[i], pageSize[i]);
|
resize(pages[i], pageSize[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pages.forEach(page => resize(page, pageSize));
|
pages.forEach(page => { resize(page, pageSize) });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PdfFile(file.originalFilename, pdfDoc, RepresentationType.PDFLibDocument, file.filename+"_scaledPages");
|
return new PdfFile(file.originalFilename, pdfDoc, RepresentationType.PDFLibDocument, file.filename+"_scaledPages");
|
||||||
};
|
}
|
||||||
|
|
||||||
function resize(page: PDFPage, newSize: {width?:number,height?:number}) {
|
function resize(page: PDFPage, newSize: {width?:number,height?:number}) {
|
||||||
const calculatedSize = calculateSize(page, newSize);
|
const calculatedSize = calculateSize(page, newSize);
|
||||||
@ -74,7 +74,7 @@ function calculateSize(page: PDFPage, newSize: {width?:number,height?:number}):
|
|||||||
const ratio = oldSize.height / oldSize.width;
|
const ratio = oldSize.height / oldSize.width;
|
||||||
return { width: newSize.width, height: newSize.width * ratio };
|
return { width: newSize.width, height: newSize.width * ratio };
|
||||||
}
|
}
|
||||||
return { width: newSize.width!, height: newSize.height! };
|
return { width: newSize.width, height: newSize.height };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PageSize = Object.freeze({
|
export const PageSize = Object.freeze({
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
import { PdfFile } from "../wrappers/PdfFile.js";
|
||||||
import { splitPagesByIndex } from "./common/splitPagesByIndex.js";
|
import { splitPagesByIndex } from "./common/splitPagesByIndex.js";
|
||||||
import { detectEmptyPages } from "./common/detectEmptyPages.js";
|
import { detectEmptyPages } from "./common/detectEmptyPages.js";
|
||||||
import { detectQRCodePages } from "./common/detectQRCodePages.js";
|
import { detectQRCodePages } from "./common/detectQRCodePages.js";
|
||||||
|
|
||||||
export type SplitPageByPresetParamsType = {
|
export interface SplitPageByPresetParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE";
|
type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE";
|
||||||
whiteThreashold?: number;
|
whiteThreashold?: number;
|
||||||
@ -41,4 +41,4 @@ export async function splitPagesByPreset(params: SplitPageByPresetParamsType): P
|
|||||||
newFiles[i].filename += "_split-"+i;
|
newFiles[i].filename += "_split-"+i;
|
||||||
}
|
}
|
||||||
return newFiles;
|
return newFiles;
|
||||||
};
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
import { PdfFile } from "../wrappers/PdfFile.js";
|
||||||
import { parsePageIndexSpecification } from './common/pageIndexesUtils'
|
import { parsePageIndexSpecification } from "./common/pageIndexesUtils";
|
||||||
import { splitPagesByIndex } from './common/splitPagesByIndex.js';
|
import { splitPagesByIndex } from "./common/splitPagesByIndex.js";
|
||||||
|
|
||||||
export type SplitPdfByIndexParamsType = {
|
export interface SplitPdfByIndexParamsType {
|
||||||
file: PdfFile;
|
file: PdfFile;
|
||||||
pageIndexes: string | number[];
|
pageIndexes: string | number[];
|
||||||
}
|
}
|
||||||
@ -11,7 +11,7 @@ export async function splitPdfByIndex(params: SplitPdfByIndexParamsType): Promis
|
|||||||
const { file, pageIndexes } = params;
|
const { file, pageIndexes } = params;
|
||||||
const pdfLibDocument = await file.pdfLibDocument;
|
const pdfLibDocument = await file.pdfLibDocument;
|
||||||
|
|
||||||
var indexes = pageIndexes;
|
let indexes = pageIndexes;
|
||||||
|
|
||||||
if (!Array.isArray(indexes)) {
|
if (!Array.isArray(indexes)) {
|
||||||
indexes = parsePageIndexSpecification(indexes, pdfLibDocument.getPageCount());
|
indexes = parsePageIndexSpecification(indexes, pdfLibDocument.getPageCount());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { PdfFile } from '../wrappers/PdfFile';
|
import { PdfFile } from "../wrappers/PdfFile";
|
||||||
|
|
||||||
export type UpdateMetadataParams = {
|
export interface UpdateMetadataParams {
|
||||||
file: PdfFile,
|
file: PdfFile,
|
||||||
deleteAll?: boolean, // Delete all metadata if set to true
|
deleteAll?: boolean, // Delete all metadata if set to true
|
||||||
author?: string, // The author of the document
|
author?: string, // The author of the document
|
||||||
@ -21,34 +21,34 @@ export async function updateMetadata(params: UpdateMetadataParams): Promise<PdfF
|
|||||||
|
|
||||||
if (params.deleteAll) {
|
if (params.deleteAll) {
|
||||||
pdfDoc.setAuthor("");
|
pdfDoc.setAuthor("");
|
||||||
pdfDoc.setCreationDate(new Date(0))
|
pdfDoc.setCreationDate(new Date(0));
|
||||||
pdfDoc.setCreator("")
|
pdfDoc.setCreator("");
|
||||||
pdfDoc.setKeywords([])
|
pdfDoc.setKeywords([]);
|
||||||
pdfDoc.setModificationDate(new Date(0))
|
pdfDoc.setModificationDate(new Date(0));
|
||||||
pdfDoc.setProducer("")
|
pdfDoc.setProducer("");
|
||||||
pdfDoc.setSubject("")
|
pdfDoc.setSubject("");
|
||||||
pdfDoc.setTitle("")
|
pdfDoc.setTitle("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(params.author)
|
if(params.author)
|
||||||
pdfDoc.setAuthor(params.author);
|
pdfDoc.setAuthor(params.author);
|
||||||
if(params.creationDate)
|
if(params.creationDate)
|
||||||
pdfDoc.setCreationDate(params.creationDate)
|
pdfDoc.setCreationDate(params.creationDate);
|
||||||
if(params.creator)
|
if(params.creator)
|
||||||
pdfDoc.setCreator(params.creator)
|
pdfDoc.setCreator(params.creator);
|
||||||
if(params.keywords)
|
if(params.keywords)
|
||||||
pdfDoc.setKeywords(params.keywords.split(","))
|
pdfDoc.setKeywords(params.keywords.split(","));
|
||||||
if(params.modificationDate)
|
if(params.modificationDate)
|
||||||
pdfDoc.setModificationDate(params.modificationDate)
|
pdfDoc.setModificationDate(params.modificationDate);
|
||||||
if(params.producer)
|
if(params.producer)
|
||||||
pdfDoc.setProducer(params.producer)
|
pdfDoc.setProducer(params.producer);
|
||||||
if(params.subject)
|
if(params.subject)
|
||||||
pdfDoc.setSubject(params.subject)
|
pdfDoc.setSubject(params.subject);
|
||||||
if(params.title)
|
if(params.title)
|
||||||
pdfDoc.setTitle(params.title)
|
pdfDoc.setTitle(params.title);
|
||||||
|
|
||||||
// TODO add trapped and custom metadata. May need another library
|
// TODO add trapped and custom metadata. May need another library
|
||||||
|
|
||||||
params.file.filename += "_updatedMetadata";
|
params.file.filename += "_updatedMetadata";
|
||||||
return params.file;
|
return params.file;
|
||||||
};
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import i18next from "i18next"
|
import i18next from "i18next";
|
||||||
import resourcesToBackend from 'i18next-resources-to-backend';
|
import resourcesToBackend from "i18next-resources-to-backend";
|
||||||
|
|
||||||
i18next
|
i18next
|
||||||
.use(resourcesToBackend((language, namespace) => import(`./${namespace}/${language}.json`)))
|
.use(resourcesToBackend((language, namespace) => import(`./${namespace}/${language}.json`)))
|
||||||
.init({
|
.init({
|
||||||
// debug: true,
|
// debug: true,
|
||||||
ns: ['common'], // Preload this namespace, no need to add the others
|
ns: ["common"], // Preload this namespace, no need to add the others
|
||||||
defaultNS: 'common',
|
defaultNS: "common",
|
||||||
fallbackLng: 'en',
|
fallbackLng: "en",
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { Operator } from "../functions";
|
|||||||
import { Impose } from "../functions/impose";
|
import { Impose } from "../functions/impose";
|
||||||
export const Operators = {
|
export const Operators = {
|
||||||
Impose: Impose
|
Impose: Impose
|
||||||
}
|
};
|
||||||
|
|
||||||
// TODO: Convert this to a map or similar
|
// TODO: Convert this to a map or similar
|
||||||
export function getOperatorByName(name: string): typeof Operator | undefined {
|
export function getOperatorByName(name: string): typeof Operator | undefined {
|
||||||
@ -13,7 +13,7 @@ export function getOperatorByName(name: string): typeof Operator | undefined {
|
|||||||
// Loop over each default export
|
// Loop over each default export
|
||||||
Object.entries(Operators).some(([className, exportedClass]) => {
|
Object.entries(Operators).some(([className, exportedClass]) => {
|
||||||
// Check if the exported item is a class
|
// Check if the exported item is a class
|
||||||
if (typeof exportedClass === 'function' && exportedClass.prototype) {
|
if (typeof exportedClass === "function" && exportedClass.prototype) {
|
||||||
if (exportedClass.type === name) {
|
if (exportedClass.type === name) {
|
||||||
foundClass = exportedClass;
|
foundClass = exportedClass;
|
||||||
return true; // Stop the iteration
|
return true; // Stop the iteration
|
||||||
|
@ -4,8 +4,8 @@ import { PdfFile } from "../wrappers/PdfFile";
|
|||||||
export function organizeWaitOperations(actions: Action[]) {
|
export function organizeWaitOperations(actions: Action[]) {
|
||||||
|
|
||||||
// Initialize an object to store the counts and associated "done" operations
|
// Initialize an object to store the counts and associated "done" operations
|
||||||
const waitCounts: {[key: string]: number} = {};
|
const waitCounts: Record<string, number> = {};
|
||||||
const doneOperations: {[key: string]: Action} = {};
|
const doneOperations: Record<string, Action> = {};
|
||||||
|
|
||||||
// Function to count "type: wait" operations and associate "done" operations per id
|
// Function to count "type: wait" operations and associate "done" operations per id
|
||||||
function countWaitOperationsAndDone(actions: Action[]) {
|
function countWaitOperationsAndDone(actions: Action[]) {
|
||||||
@ -43,10 +43,8 @@ export function organizeWaitOperations(actions: Action[]) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ResultType = {
|
export type ResultType = Record<string, {
|
||||||
[key: string]: {
|
|
||||||
waitCount: number,
|
waitCount: number,
|
||||||
doneOperation: Action,
|
doneOperation: Action,
|
||||||
input: PdfFile[]
|
input: PdfFile[]
|
||||||
}
|
}>;
|
||||||
}
|
|
@ -49,7 +49,7 @@ export async function traverseOperations(operations: Action[], input: PdfFile[],
|
|||||||
default:
|
default:
|
||||||
const operator = getOperatorByName(action.type);
|
const operator = getOperatorByName(action.type);
|
||||||
if(operator) {
|
if(operator) {
|
||||||
let operation = new operator(action);
|
const operation = new operator(action);
|
||||||
input = await operation.run(input, progressCallback);
|
input = await operation.run(input, progressCallback);
|
||||||
await nextOperation(action.actions, input, progressCallback);
|
await nextOperation(action.actions, input, progressCallback);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason?
|
|||||||
|
|
||||||
const operator = getOperatorByName(action.type);
|
const operator = getOperatorByName(action.type);
|
||||||
if(!operator) {
|
if(!operator) {
|
||||||
return { valid: false, reason: `action.type ${action.type} does not exist` }
|
return { valid: false, reason: `action.type ${action.type} does not exist` };
|
||||||
}
|
}
|
||||||
const validationResult = operator.schema.validate({values: action.values});
|
const validationResult = operator.schema.validate({values: action.values});
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action.type === "done") {
|
else if (action.type === "done") {
|
||||||
return { valid: false, reason: `There shouldn't be a done action here.` };
|
return { valid: false, reason: "There shouldn't be a done action here." };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const receivingOperator = getOperatorByName(childAction.type);
|
const receivingOperator = getOperatorByName(childAction.type);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as PDFJS from 'pdfjs-dist';
|
import * as PDFJS from "pdfjs-dist";
|
||||||
import type { PDFDocumentProxy as PDFJSDocument } from 'pdfjs-dist/types/src/display/api';
|
import type { PDFDocumentProxy as PDFJSDocument } from "pdfjs-dist/types/src/display/api";
|
||||||
import { PDFDocument as PDFLibDocument } from 'pdf-lib';
|
import { PDFDocument as PDFLibDocument } from "pdf-lib";
|
||||||
import Joi from 'joi';
|
import Joi from "joi";
|
||||||
|
|
||||||
export enum RepresentationType {
|
export enum RepresentationType {
|
||||||
Uint8Array,
|
Uint8Array,
|
||||||
@ -23,18 +23,18 @@ export class PdfFile {
|
|||||||
});
|
});
|
||||||
case RepresentationType.PDFLibDocument:
|
case RepresentationType.PDFLibDocument:
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
var uint8Array = await (this.representation as PDFLibDocument).save();
|
const uint8Array = await (this.representation as PDFLibDocument).save();
|
||||||
this.uint8Array = uint8Array;
|
this.uint8Array = uint8Array;
|
||||||
resolve(uint8Array);
|
resolve(uint8Array);
|
||||||
});
|
});
|
||||||
case RepresentationType.PDFJSDocument:
|
case RepresentationType.PDFJSDocument:
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
var uint8Array = await (this.representation as PDFJSDocument).getData();
|
const uint8Array = await (this.representation as PDFJSDocument).getData();
|
||||||
this.uint8Array = uint8Array;
|
this.uint8Array = uint8Array;
|
||||||
resolve(uint8Array);
|
resolve(uint8Array);
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
console.error("unhandeled PDF type: " + typeof this.representation as string);
|
console.error("unhandeled PDF type: " + typeof this.representation );
|
||||||
throw Error("unhandeled PDF type");
|
throw Error("unhandeled PDF type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,8 +51,8 @@ export class PdfFile {
|
|||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
var uint8Array = await this.uint8Array;
|
const uint8Array = await this.uint8Array;
|
||||||
var pdfLibDoc = await PDFLibDocument.load(uint8Array, {
|
const pdfLibDoc = await PDFLibDocument.load(uint8Array, {
|
||||||
updateMetadata: false,
|
updateMetadata: false,
|
||||||
});
|
});
|
||||||
this.pdfLibDocument = pdfLibDoc;
|
this.pdfLibDocument = pdfLibDoc;
|
||||||
|
@ -23,5 +23,5 @@ export const JoiPDFFileSchema = Joi.custom((value: Express.Multer.File[] /* <- a
|
|||||||
}, "pdffile validation");
|
}, "pdffile validation");
|
||||||
|
|
||||||
function isPdfFileArray(value: any[]): value is PdfFile[] { // "is" is a ts-typeguard - https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
|
function isPdfFileArray(value: any[]): value is PdfFile[] { // "is" is a ts-typeguard - https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
|
||||||
return value.every((e) => e instanceof PdfFile)
|
return value.every((e) => e instanceof PdfFile);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user