rewrote impose to use new Operator class,

dependencies (pdfcpu) can be made environment-aware using tsconfig.json
This commit is contained in:
Felix Kaspar
2023-11-20 21:04:49 +01:00
parent 42904788bf
commit a5060f0fd3
17 changed files with 188 additions and 187 deletions

View File

@@ -1,6 +1,9 @@
import { PdfFile, RepresentationType } from "../wrappers/PdfFile";
import { FieldConstraint, RecordConstraint } from '../dynamic-ui/OperatorConstraints'
import { IOType, Operator, Progress } from ".";
import * as pdfcpuWrapper from "#pdfcpu"; // This is updated by tsconfig.json/paths for the context (browser, node, etc.) this module is used in.
export type ImposeParamsType = {
file: PdfFile;
@@ -10,36 +13,69 @@ export type ImposeParamsType = {
format: string;
}
export class Impose extends Operator {
static type: string = "impose";
static mayInput: IOType = IOType.PDF;
static willOutput: IOType = IOType.PDF;
/** PDF-Imposition, PDF-N-Up: Put multiple pages of the input document into a single page of the output document. - see: {@link https://en.wikipedia.org/wiki/N-up} */
async run(input: PdfFile[], progressCallback: (state: Progress) => void): Promise<PdfFile[]> {
return this.nToN<PdfFile, PdfFile>(input, async (input, index, max) => {
// https://pdfcpu.io/generate/nup.html
const uint8Array = await pdfcpuWrapper.oneToOne(
[
"pdfcpu.wasm",
"nup",
"-c",
"disable",
'f:' + this.actionValues.format,
"/output.pdf",
String(this.actionValues.nup),
"input.pdf",
],
await input.uint8Array
);
const result = new PdfFile(
input.originalFilename,
uint8Array,
RepresentationType.Uint8Array,
input.filename + "_imposed"
);
progressCallback({ curFileProgress: 1, operationProgress: index/max })
console.log("ImposeResult: ", result);
return [result];
})
}
validate(): { valid: boolean; reason?: string | undefined; } {
let baseValidationResults = super.validate();
if(!baseValidationResults.valid)
return baseValidationResults;
// TODO: This should be ported to SaudF's RecordValidator
if(this.actionValues.nup) {
if(![2, 3, 4, 8, 9, 12, 16].includes(this.actionValues.nup)) {
return { valid: false, reason: "NUp accepted values are 2, 3, 4, 8, 9, 12, 16 - see: https://pdfcpu.io/generate/nup.html#n-up-value"}
}
}
else
return { valid: false, reason: "nup is not defined" }
if(!this.actionValues.format) {
return { valid: false, reason: "format is not defined" }
}
// TODO: Format should be checked for all acceped formats
return { valid: true }
}
}
export const ImposeParamConstraints = new RecordConstraint({
file: new FieldConstraint("display.key", "file.pdf", true, "hint.key"),
nup: new FieldConstraint("display.key", [2, 3, 4, 8, 9, 12, 16], true, "hint.key"),
format: new FieldConstraint("display.key", ["A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","Letter","Legal"], true, "hint.key"),
})
/** PDF-Imposition, PDF-N-Up: Put multiple pages of the input document into a single page of the output document. - see: {@link https://en.wikipedia.org/wiki/N-up} */
export async function impose(params: ImposeParamsType, pdfcpuWrapper: any): Promise<PdfFile> {
// https://pdfcpu.io/generate/nup.html
const uint8Array = await pdfcpuWrapper.oneToOne(
[
"pdfcpu.wasm",
"nup",
"-c",
"disable",
'f:' + params.format,
"/output.pdf",
String(params.nup),
"input.pdf",
],
await params.file.uint8Array
);
const result = new PdfFile(
params.file.originalFilename,
uint8Array,
RepresentationType.Uint8Array,
params.file.filename + "_imposed"
);
console.log("ImposeResult: ", result);
return result;
}
})

View File

@@ -0,0 +1,58 @@
import { Action } from "../../declarations/Action";
export enum IOType {
PDF, Image, Text // TODO: Extend with Document File Types
}
export interface Progress {
/** 0-1 */
curFileProgress: number,
/** 0-1 */
operationProgress: number,
}
export class Operator {
/** The type of the operator in camelCase (impose, merge, etc.) */
static type: string;
// This will most likely be needed in the node Editor
static mayInput: IOType;
static willOutput: IOType;
actionValues: any;
constructor (action: Action) {
this.actionValues = action.values;
}
// TODO: Type callback state, it should give updates on the progress of the current operator
async run(input: any[], progressCallback: (progress: Progress) => void): Promise<any[]> {
return [];
}
validate(): { valid: boolean, reason?: string } {
if(!this.actionValues) {
return { valid: false, reason: "The Operators action values were empty."}
}
return { valid: true };
}
protected async nToOne <I, O>(inputs: I[], callback: (input: I[]) => Promise<O>): Promise<O[]> {
return [await callback(inputs)];
}
protected async oneToN <I, O>(inputs: I[], callback: (input: I, index: number, max: number) => Promise<O[]>): Promise<O[]> {
return this.nToN(inputs, callback); // nToN is able to handle single inputs now.
}
protected async nToN <I, O>(inputs: I[], callback: (input: I, index: number, max: number) => Promise<O[]>): Promise<O[]> {
let output: O[] = []
for (let i = 0; i < inputs.length; i++) {
output = output.concat(await callback(inputs[i], i, inputs.length));
}
return output;
}
}
// TODO: Export Operators?