rewrote impose to use new Operator class,
dependencies (pdfcpu) can be made environment-aware using tsconfig.json
This commit is contained in:
@@ -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;
|
||||
}
|
||||
})
|
||||
58
shared-operations/src/functions/index.ts
Normal file
58
shared-operations/src/functions/index.ts
Normal 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?
|
||||
@@ -1,60 +0,0 @@
|
||||
|
||||
import { arrangePages, ArrangePagesParamsType } from './functions/arrangePages'
|
||||
import { extractPages, ExtractPagesParamsType } from "./functions/extractPages";
|
||||
import { impose, ImposeParamConstraints, ImposeParamsType } from "./functions/impose";
|
||||
import { mergePDFs, MergeParamsType } from './functions/mergePDFs';
|
||||
import { removeBlankPages, RemoveBlankPagesParamsType } from "./functions/removeBlankPages";
|
||||
import { removePages, RemovePagesParamsType } from "./functions/removePages";
|
||||
import { rotatePages, RotateParamsType } from './functions/rotatePages';
|
||||
import { scaleContent, ScaleContentParamsType} from './functions/scaleContent';
|
||||
import { scalePage, ScalePageParamsType } from './functions/scalePage';
|
||||
import { splitPagesByPreset, SplitPageByPresetParamsType } from './functions/splitPagesByPreset';
|
||||
import { splitPdfByIndex, SplitPdfByIndexParamsType } from './functions/splitPdfByIndex';
|
||||
import { updateMetadata, UpdateMetadataParams } from "./functions/updateMetadata";
|
||||
import { FieldConstraint, RecordConstraint } from '@stirling-pdf/shared-operations/src/dynamic-ui/OperatorConstraints'
|
||||
import { PdfFile } from "./wrappers/PdfFile";
|
||||
|
||||
import { Override, ValuesType } from '../declarations/TypeScriptUtils'
|
||||
|
||||
// Import injected libraries here!
|
||||
|
||||
const toExport = {
|
||||
/*arrangePages,
|
||||
extractPages,*/
|
||||
Impose: {exec: impose, spec: ImposeParamConstraints},
|
||||
/*mergePDFs,
|
||||
removeBlankPages,
|
||||
removePages,
|
||||
rotatePages,
|
||||
scaleContent,
|
||||
scalePage,
|
||||
splitPagesByPreset,
|
||||
splitPdfByIndex,
|
||||
updateMetadata,*/
|
||||
}
|
||||
export default toExport;
|
||||
|
||||
type OperatorsBaseType = typeof toExport;
|
||||
export type OperatorsType = Override<OperatorsBaseType, {
|
||||
Impose: {
|
||||
exec: (params: ImposeParamsType) => Promise<PdfFile>;
|
||||
spec: RecordConstraint;
|
||||
};
|
||||
}>;
|
||||
|
||||
export type OperatorType = ValuesType<OperatorsType>;
|
||||
|
||||
export type OperatorParametersType = {
|
||||
/*arrangePages: ArrangePagesParamsType
|
||||
extractPages: ExtractPagesParamsType;*/
|
||||
Impose: ImposeParamsType;
|
||||
/*mergePDFs: MergeParamsType;
|
||||
removeBlankPages: RemoveBlankPagesParamsType;
|
||||
removePages: RemovePagesParamsType;
|
||||
rotatePages: RotateParamsType;
|
||||
scaleContent: ScaleContentParamsType;
|
||||
scalePage: ScalePageParamsType;
|
||||
splitPagesByPreset: SplitPageByPresetParamsType;
|
||||
splitPdfByIndex: SplitPdfByIndexParamsType;
|
||||
updateMetadata: UpdateMetadataParams;*/
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { organizeWaitOperations } from "./organizeWaitOperations";
|
||||
import { Action, WaitAction } from "../../declarations/Action";
|
||||
import { OperatorsType } from "../../src/index";
|
||||
import { PdfFile } from "../wrappers/PdfFile";
|
||||
import { Progress } from "../functions";
|
||||
import { Impose } from "../functions/impose";
|
||||
|
||||
export async function * traverseOperations(operations: Action[], input: PdfFile[] | PdfFile, Operators: OperatorsType): AsyncGenerator<string, PdfFile[], void> {
|
||||
// TODO: Fix Operators Type
|
||||
export async function * traverseOperations(operations: Action[], input: PdfFile[] | PdfFile): AsyncGenerator<string, PdfFile[], void> {
|
||||
const waitOperations = organizeWaitOperations(operations);
|
||||
let results: PdfFile[] = [];
|
||||
yield* nextOperation(operations, input);
|
||||
@@ -31,7 +33,7 @@ export async function * traverseOperations(operations: Action[], input: PdfFile[
|
||||
}
|
||||
}
|
||||
|
||||
async function * computeOperation(action: Action, input: PdfFile|PdfFile[]): AsyncGenerator<string, void, void> {
|
||||
async function * computeOperation(action: Action, input: PdfFile[]): AsyncGenerator<string, void, void> {
|
||||
console.log("Input: ", input);
|
||||
yield "Starting: " + action.type;
|
||||
switch (action.type) {
|
||||
@@ -59,10 +61,12 @@ export async function * traverseOperations(operations: Action[], input: PdfFile[
|
||||
});
|
||||
break;*/
|
||||
case "impose":
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operators.Impose.exec({file: input, nup: action.values["nup"], format: action.values["format"]});
|
||||
return newPdf;
|
||||
});
|
||||
let impose = new Impose(action);
|
||||
if(impose.validate().valid) {
|
||||
impose.run(input, (state: Progress) => {
|
||||
console.log(state);
|
||||
});
|
||||
}
|
||||
break;
|
||||
/*case "merge":
|
||||
yield* nToOne(input, action, async (inputs) => {
|
||||
@@ -117,53 +121,4 @@ export async function * traverseOperations(operations: Action[], input: PdfFile[
|
||||
throw new Error(`${action.type} not implemented yet.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PdfFile|PdfFile[]} input
|
||||
* @param {JSON} action
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * nToOne(inputs: PdfFile|PdfFile[], action: Action, callback: (pdf: PdfFile[]) => Promise<PdfFile>): AsyncGenerator<string, void, void> {
|
||||
const input = Array.isArray(inputs) ? inputs : [inputs]; // Convert single values to array, keep arrays as is.
|
||||
|
||||
const newInputs = await callback(input);
|
||||
yield* nextOperation(action.actions, newInputs);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PdfFile|PdfFile[]} input
|
||||
* @param {JSON} action
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * oneToN(input: PdfFile|PdfFile[], action: Action, callback: (pdf: PdfFile) => Promise<PdfFile[]>): AsyncGenerator<string, void, void> {
|
||||
if(Array.isArray(input)) {
|
||||
let output: PdfFile[] = [];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output = output.concat(await callback(input[i]));
|
||||
}
|
||||
yield* nextOperation(action.actions, output);
|
||||
}
|
||||
else {
|
||||
const nextInput = await callback(input);
|
||||
yield* nextOperation(action.actions, nextInput);
|
||||
}
|
||||
}
|
||||
|
||||
async function * nToN(input: PdfFile|PdfFile[], action: Action, callback: (pdf: PdfFile) => Promise<PdfFile|PdfFile[]>): AsyncGenerator<string, void, void> {
|
||||
if(Array.isArray(input)) {
|
||||
console.log("inputs", input);
|
||||
let nextInputs: PdfFile[] = []
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
nextInputs = nextInputs.concat(await callback(input[i]));
|
||||
}
|
||||
console.log("nextInputs", nextInputs);
|
||||
yield* nextOperation(action.actions, nextInputs);
|
||||
}
|
||||
else {
|
||||
const nextInput = await callback(input);
|
||||
yield* nextOperation(action.actions, nextInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user