diff --git a/cli/index.ts b/cli/index.ts index 0e0f955..9decc4e 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -5,6 +5,16 @@ import { colorize } from "./style.ts"; import { selectMenuInteractive } from "./selectMenu.ts"; import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts"; import { cliAlert, cliLog } from "./prompts.ts"; +import type { ITool } from "../types.ts"; +import { join, toFileUrl } from "@std/path"; +import { log } from "util/logfile.ts"; + +// Register tools here (filename, no extension) +const toolRegistry: [string, Promise<{ default: ITool }>][] = [ + ["checkCode", import("../tools/checkCode.ts")], + ["fieldRename", import("../tools/fieldRename.ts")], + ["listFormFields", import("../tools/listFormFields.ts")], +]; export class PdfToolsCli { private tools: Map = new Map(); @@ -15,19 +25,18 @@ export class PdfToolsCli { help: ["-h", "--help"], }); - async importTools(tools?: string) { - tools = tools?.replace(/\/$/, "").replace(/^\.?\//, "") || "tools"; - for (const toolfile of Deno.readDirSync(tools)) { - if (toolfile.isFile) { - const tool = await import( - Deno.cwd() + "/" + tools + "/" + toolfile.name - ); - if (tool.default) { + async importTools() { + for (const [name, toolfile] of toolRegistry) { + const t = await toolfile; + try { + if (t.default) { this.tools.set( - toCase(toolfile.name.replace(".ts", ""), "title"), - tool.default, + toCase(name, "title"), + t.default, ); } + } catch (e) { + cliLog(e + "\n", this.terminalLayout.getBlock("body")); } } } @@ -50,6 +59,7 @@ export class PdfToolsCli { public async run() { try { + await this.importTools(); const titleBlock = new TerminalBlock(); this.terminalLayout.register("title", titleBlock); const bodyBlock = new TerminalBlock(); @@ -59,10 +69,8 @@ export class PdfToolsCli { return; } else if (this.args.nonFlags.length === 0 || !this.args.task) { this.embiggenHeader(); - await this.importTools(); await this.toolMenu(); } else { - await this.importTools(); const task = this.args.task; await this.runTool(toCase(task, "title")); } diff --git a/cli/selectMenu.ts b/cli/selectMenu.ts index a94ee69..6e2a774 100644 --- a/cli/selectMenu.ts +++ b/cli/selectMenu.ts @@ -1,3 +1,4 @@ +import type { callback } from "../types.ts"; import { colorize } from "./style.ts"; import { TerminalBlock } from "./TerminalLayout.ts"; diff --git a/deno.json b/deno.json index 60c29cd..d1a927f 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,7 @@ { "name": "@bearmetal/pdf-tools", - "version": "0.0.1", + "version": "0.0.6", + "license": "GPL 3.0", "tasks": { "dev": "deno run -A --env-file=.env --watch main.ts", "compile": "deno compile -o compare-form-fields.exe --target x86_64-pc-windows-msvc -R ./main.ts", @@ -9,6 +10,7 @@ }, "imports": { "@std/assert": "jsr:@std/assert@1", + "@std/path": "jsr:@std/path@^1.0.9", "pdf-lib": "npm:pdf-lib@^1.17.1", "util/": "./util/" }, diff --git a/deno.lock b/deno.lock index 1e00912..eda4b49 100644 --- a/deno.lock +++ b/deno.lock @@ -1,8 +1,9 @@ { - "version": "4", + "version": "5", "specifiers": { "jsr:@std/assert@1": "1.0.12", "jsr:@std/internal@^1.0.6": "1.0.6", + "jsr:@std/path@^1.0.9": "1.0.9", "npm:pdf-lib@^1.17.1": "1.17.1" }, "jsr": { @@ -14,6 +15,9 @@ }, "@std/internal@1.0.6": { "integrity": "9533b128f230f73bd209408bb07a4b12f8d4255ab2a4d22a1fd6d87304aca9a4" + }, + "@std/path@1.0.9": { + "integrity": "260a49f11edd3db93dd38350bf9cd1b4d1366afa98e81b86167b4e3dd750129e" } }, "npm": { @@ -48,6 +52,7 @@ "workspace": { "dependencies": [ "jsr:@std/assert@1", + "jsr:@std/path@^1.0.9", "npm:pdf-lib@^1.17.1" ] } diff --git a/main.ts b/main.ts index eae7bf6..0741d37 100644 --- a/main.ts +++ b/main.ts @@ -1,3 +1,4 @@ +/// import { PdfToolsCli } from "./cli/index.ts"; const app = new PdfToolsCli(); diff --git a/tools/checkCode.ts b/tools/checkCode.ts index 8e3f142..1993808 100644 --- a/tools/checkCode.ts +++ b/tools/checkCode.ts @@ -2,6 +2,7 @@ import { forceArgs } from "../cli/forceArgs.ts"; import { cliAlert, cliLog } from "../cli/prompts.ts"; import { colorize } from "../cli/style.ts"; import type { TerminalBlock } from "../cli/TerminalLayout.ts"; +import type { ITool } from "../types.ts"; import { loadPdfForm } from "../util/saveLoadPdf.ts"; function getCaseSyntaxPatternByFileExtension( @@ -33,7 +34,7 @@ class CheckCode implements ITool { [pdfPath, codePaths] = await forceArgs([pdfPath, codePaths], [ "Please provide path to PDF file:", "Please provide path(s) to code file(s) (comma separated for multiple):", - ]); + ], this.block); const form = await loadPdfForm(pdfPath); diff --git a/tools/fieldRename.ts b/tools/fieldRename.ts index 36c97e2..3fc6e6e 100644 --- a/tools/fieldRename.ts +++ b/tools/fieldRename.ts @@ -6,6 +6,7 @@ import { forceArgs } from "../cli/forceArgs.ts"; import { colorize } from "../cli/style.ts"; import { cliAlert, cliLog, cliPrompt } from "../cli/prompts.ts"; import { multiSelectMenuInteractive } from "../cli/selectMenu.ts"; +import type { callback, ITool } from "../types.ts"; async function renameFields( path: string, diff --git a/tools/listFormFields.ts b/tools/listFormFields.ts index d8c5daf..77f8542 100644 --- a/tools/listFormFields.ts +++ b/tools/listFormFields.ts @@ -2,6 +2,7 @@ import { forceArgs } from "../cli/forceArgs.ts"; import { cliAlert } from "../cli/prompts.ts"; import { TerminalBlock } from "../cli/TerminalLayout.ts"; import { loadPdfForm } from "util/saveLoadPdf.ts"; +import type { ITool } from "../types.ts"; export class ListFormFields implements ITool { name = "listformfields"; diff --git a/types.ts b/types.ts index de4feeb..bdc2f42 100644 --- a/types.ts +++ b/types.ts @@ -1,15 +1,13 @@ import type { TerminalBlock } from "./cli/TerminalLayout.ts"; -declare global { - type ToolFunc = (...args: T) => Promise; - interface ITool { - name: string; - description: string; - run: ToolFunc; - help?: () => Promise | void; - done?: () => Promise | void; - setBlock?: (block: TerminalBlock) => void; - } - - type callback = (...args: any[]) => any; +export type ToolFunc = (...args: T) => Promise; +export interface ITool { + name: string; + description: string; + run: ToolFunc; + help?: () => Promise | void; + done?: () => Promise | void; + setBlock?: (block: TerminalBlock) => void; } + +export type callback = (...args: any[]) => any; diff --git a/util/call.ts b/util/call.ts index 0d7cadb..10f1a47 100644 --- a/util/call.ts +++ b/util/call.ts @@ -1,3 +1,5 @@ +import type { ToolFunc } from "../types.ts"; + type transformer = (arg: string) => any; interface IConfig { multiTransform?: boolean; diff --git a/util/saveLoadPdf.ts b/util/saveLoadPdf.ts index 7adb71d..815aed6 100644 --- a/util/saveLoadPdf.ts +++ b/util/saveLoadPdf.ts @@ -1,4 +1,4 @@ -import { PDFDocument } from "pdf-lib"; +import { PDFDocument, PDFTextField } from "pdf-lib"; export async function loadPdfForm(path: string) { const pdfDoc = await loadPdf(path); @@ -13,6 +13,11 @@ export async function loadPdf(path: string) { } export async function savePdf(doc: PDFDocument, path: string) { + doc.getForm().getFields().forEach((field) => { + if (field instanceof PDFTextField) { + field.disableRichFormatting(); + } + }); const pdfBytes = await doc.save(); if (Deno.env.get("DRYRUN") || path.includes("dryrun")) return; await Deno.writeFile(path, pdfBytes);