From 26b7089cc204cb4b3bb0e30656957627b0ebd043 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 30 Apr 2025 03:06:19 -0600 Subject: [PATCH 1/5] adds flag coalescing to argparser adds handling of inlined args to call tools fixes terminal layout clearing troubles --- cli/TerminalLayout.ts | 7 +++---- cli/argParser.ts | 23 +++++++++++++++++++++-- cli/index.ts | 34 ++++++++++++++++++++++++---------- cli/prompts.ts | 13 +++++++------ tools/fieldRename.ts | 41 +++-------------------------------------- 5 files changed, 58 insertions(+), 60 deletions(-) diff --git a/cli/TerminalLayout.ts b/cli/TerminalLayout.ts index 59addaf..a4f400c 100644 --- a/cli/TerminalLayout.ts +++ b/cli/TerminalLayout.ts @@ -79,15 +79,15 @@ export class TerminalLayout { } clearAll() { + for (const name of this.layoutOrder) { + this.blocks[name].clear(); + } Deno.stdout.writeSync( new TextEncoder().encode( TerminalLayout.ALT_BUFFER_DISABLE, ), ); Cursor.show(); - for (const name of this.layoutOrder) { - this.blocks[name].clear(); - } } clear() { @@ -223,7 +223,6 @@ export class TerminalBlock { for (let i = 0; i < this.renderedLineCount; i++) { Deno.stdout.writeSync(new TextEncoder().encode(`\x1b[2K\x1b[1E`)); } - this.renderedLineCount = 0; } clearAll() { diff --git a/cli/argParser.ts b/cli/argParser.ts index 842378b..89907b9 100644 --- a/cli/argParser.ts +++ b/cli/argParser.ts @@ -1,5 +1,6 @@ -export class ArgParser { +export class ArgParser> { private args: string[]; + private flags: Map = new Map(); constructor(args: string[]) { this.args = args; @@ -11,7 +12,22 @@ export class ArgParser { return this.args[index + 1]; } - get flags() { + setFlagDefs(flagDefs: T) { + for (const [flag, defs] of Object.entries(flagDefs)) { + for (const def of defs) { + if (this.argFlags.includes(def)) { + this.flags.set(flag, true); + } + } + } + return this; + } + + getFlag(flag: keyof T) { + return this.flags.get(flag); + } + + get argFlags() { return this.args.filter((arg) => arg.startsWith("-")); } @@ -26,6 +42,9 @@ export class ArgParser { get task() { return this.nonFlags[0]; } + get taskArgs() { + return this.nonFlags.slice(1); + } static parse(args: string[]) { return new ArgParser(args); diff --git a/cli/index.ts b/cli/index.ts index 1ce38b4..0e0f955 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -9,8 +9,11 @@ import { cliAlert, cliLog } from "./prompts.ts"; export class PdfToolsCli { private tools: Map = new Map(); private terminalLayout = new TerminalLayout(); + closeMessage?: string; - private args = ArgParser.parse(Deno.args); + private args = ArgParser.parse(Deno.args).setFlagDefs({ + help: ["-h", "--help"], + }); async importTools(tools?: string) { tools = tools?.replace(/\/$/, "").replace(/^\.?\//, "") || "tools"; @@ -51,17 +54,22 @@ export class PdfToolsCli { this.terminalLayout.register("title", titleBlock); const bodyBlock = new TerminalBlock(); this.terminalLayout.register("body", bodyBlock); - this.embiggenHeader(); - if (Deno.args.length === 0) { - // console.log( - // colorize("No tool specified. Importing all tools...", "gray"), - // ); + if (this.args.getFlag("help") && !this.args.task) { + await this.help(); + 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")); } - await this.toolMenu(); } finally { this.terminalLayout.clearAll(); Deno.stdin.setRaw(false); + if (this.closeMessage) console.log(this.closeMessage); } } @@ -95,9 +103,15 @@ export class PdfToolsCli { const bodyBlock = this.terminalLayout.getBlock("body"); bodyBlock.clearAll(); tool.setBlock?.(bodyBlock); - await tool.run(); - await tool.done?.(); - this.embiggenHeader(); + if (this.args.getFlag("help")) { + await tool.help?.(); + } else { + await tool.run(...this.args.taskArgs); + await tool.done?.(); + } + await this.embiggenHeader(); + } else { + this.closeMessage = "No tool found for " + toolName; } } diff --git a/cli/prompts.ts b/cli/prompts.ts index 9e0fe1c..ec8bbf6 100644 --- a/cli/prompts.ts +++ b/cli/prompts.ts @@ -1,4 +1,5 @@ import { Cursor } from "./cursor.ts"; +import { ScrollManager } from "./scrollManager.ts"; import { colorize } from "./style.ts"; import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts"; @@ -11,11 +12,8 @@ export async function cliPrompt( await Deno.stdin.setRaw(true); - let cursorVisible = true; - if (!block) { - cursorVisible = Cursor["visible"]; - Cursor.show(); - } + const cursorVisible = Cursor["visible"]; + Cursor.show(); let range: [number, number] = [0, 1]; if (block) { @@ -54,7 +52,7 @@ export async function cliPrompt( } await Deno.stdin.setRaw(false); - if (!block && !cursorVisible) { + if (!cursorVisible) { Cursor.hide(); } Deno.stdout.writeSync(encoder.encode("\n")); @@ -86,10 +84,12 @@ export function cliLog(message: string, block?: TerminalBlock) { } if (import.meta.main) { + Cursor.hide(); const layout = new TerminalLayout(); const title = new TerminalBlock(); const block = new TerminalBlock(); block.setPreserveHistory(true); + // ScrollManager.enable(block); title.setLines(["Hello, World!"]); title.setFixedHeight(1); @@ -105,6 +105,7 @@ if (import.meta.main) { cliLog(`Hello, ${name}!`, block); const single = await cliConfirm("Are you single?", block); cliLog(single ? "Do you want to go out with me?" : "Okay", block); + // ScrollManager.enable(block); const loopingConvo = [ "No response?", "I guess that's okay", diff --git a/tools/fieldRename.ts b/tools/fieldRename.ts index 0f5898c..f0d027a 100644 --- a/tools/fieldRename.ts +++ b/tools/fieldRename.ts @@ -4,25 +4,9 @@ import { callWithArgPrompt } from "util/call.ts"; import { TerminalBlock } from "../cli/TerminalLayout.ts"; import { forceArgs } from "../cli/forceArgs.ts"; import { colorize } from "../cli/style.ts"; -import { cliLog, cliPrompt } from "../cli/prompts.ts"; +import { cliAlert, cliLog, cliPrompt } from "../cli/prompts.ts"; import { multiSelectMenuInteractive } from "../cli/selectMenu.ts"; -// const thing = PDFAcroField.prototype.getFullyQualifiedName; -// PDFAcroField.prototype.getFullyQualifiedName = function () { -// const name = thing.call(this) -// // if (name?.includes('langauge')) -// console.log(name) -// return name; -// } - -// const thing = PDFHexString.prototype.copyBytesInto -// PDFHexString.prototype.copyBytesInto = function (buffer: Uint8Array, offset: number) { -// console.log((this as any).value) - -// const result = thing.call(this, buffer, offset) -// return result; -// } - async function renameFields( path: string, pattern: string | RegExp, @@ -48,29 +32,10 @@ async function renameFields( if (mName) { changesMade = true; cField.dict.set(PDFName.of("T"), PDFString.of(mName)); - // console.log(cField.getPartialName()) } } cField = cField.getParent(); - // console.log(cField?.getPartialName()) } - console.log(field.getName()); - // const newName = name.replace(pattern, change); - // console.log("Change to: %c" + newName, "color: yellow"); - // if (confirm('Ok?')) { - // let parent = field.acroField.getParent(); - // field.acroField.setPartialName(segments.pop()) - // while (parent && segments.length) { - // console.log(parent.getPartialName()) - // parent.setPartialName(segments.pop()) - // parent = parent.getParent(); - // } - // changesMade = true; - // console.log(field.getName()) - // // dict.set(PDFName.of("T"), PDFHexString.fromText(newName)) - // console.log("%cDone!", "color: lime") - // } - // break; } } if (changesMade) { @@ -123,8 +88,8 @@ class RenameFields implements ITool { } help(standalone = false) { - cliLog( - "Usage: renamefields ", + cliAlert( + "Usage: rename-fields \n", standalone ? undefined : this.block, ); } -- 2.47.2 From 03a1e3ed210bea1c8c3e2bec7848518a68c6131a Mon Sep 17 00:00:00 2001 From: Emmaline Date: Fri, 2 May 2025 01:11:15 -0600 Subject: [PATCH 2/5] updates checkCode to new framework --- cli/prompts.ts | 8 ++- deno.json | 6 ++- must_await_cli_prompts.ts | 45 ++++++++++++++++ testing/test.ts | 7 +++ tools/checkCode.ts | 109 ++++++++++++++++++++++---------------- tools/fieldRename.ts | 4 +- 6 files changed, 128 insertions(+), 51 deletions(-) create mode 100644 must_await_cli_prompts.ts diff --git a/cli/prompts.ts b/cli/prompts.ts index ec8bbf6..e183ef5 100644 --- a/cli/prompts.ts +++ b/cli/prompts.ts @@ -1,5 +1,5 @@ +// deno-lint-disable-must-await-calls import { Cursor } from "./cursor.ts"; -import { ScrollManager } from "./scrollManager.ts"; import { colorize } from "./style.ts"; import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts"; @@ -75,10 +75,14 @@ export function cliAlert(message: string, block?: TerminalBlock) { }); } -export function cliLog(message: string, block?: TerminalBlock) { +export function cliLog( + message: string | object | Array, + block?: TerminalBlock, +) { if (!block) { console.log(message); } else { + if (typeof message === "object") message = Deno.inspect(message); block.setLines(message.split("\n")); } } diff --git a/deno.json b/deno.json index b87cf86..60c29cd 100644 --- a/deno.json +++ b/deno.json @@ -19,10 +19,14 @@ "rules": { "exclude": [ "no-explicit-any" + ], + "include": [ + "require-await" ] }, "plugins": [ - "./no-log.ts" + "./no-log.ts", + "./must_await_cli_prompts.ts" ] } } \ No newline at end of file diff --git a/must_await_cli_prompts.ts b/must_await_cli_prompts.ts new file mode 100644 index 0000000..a45197a --- /dev/null +++ b/must_await_cli_prompts.ts @@ -0,0 +1,45 @@ +const TARGET_FUNCTIONS = new Set(["cliAlert", "cliPrompt", "cliConfirm"]); + +const plugin: Deno.lint.Plugin = { + name: "must-await-calls", + rules: { + "must-await-calls": { + create(context) { + return { + CallExpression(node) { + if ( + node.callee.type !== "Identifier" || + !TARGET_FUNCTIONS.has(node.callee.name) + ) return; + + const parent = node.parent; + + // Allow `await fetchData()` + if (parent?.type === "AwaitExpression") return; + + // Allow `return fetchData()` or `return await fetchData()` + if (parent?.type === "ReturnStatement") return; + + // Allow `fetchData().then(...)` + if ( + parent?.type === "MemberExpression" && + parent.property.type === "Identifier" && + parent.property.name === "then" + ) return; + + context.report({ + node, + message: + `Call to "${node.callee.name}" must be awaited, returned, or .then-chained.`, + fix(fixer) { + return fixer.insertTextBefore(node, "await "); + }, + }); + }, + }; + }, + }, + }, +}; + +export default plugin; diff --git a/testing/test.ts b/testing/test.ts index e69de29..ea306bd 100644 --- a/testing/test.ts +++ b/testing/test.ts @@ -0,0 +1,7 @@ +const thing: string = ""; +switch (thing) { + case "Text1": + break; + default: + break; +} diff --git a/tools/checkCode.ts b/tools/checkCode.ts index a9a1a3c..8e3f142 100644 --- a/tools/checkCode.ts +++ b/tools/checkCode.ts @@ -1,57 +1,74 @@ +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 { loadPdfForm } from "../util/saveLoadPdf.ts"; -import { callWithArgPrompt } from "util/call.ts"; -export async function checkFile(pdfPath: string, csPath: string) { - while (!pdfPath || !pdfPath.endsWith(".pdf")) { - pdfPath = prompt("Please provide path to PDF file:") || ""; - } - while (!csPath || !csPath.endsWith(".cs")) { - csPath = prompt("Please provide path to CS class file:") || ""; - } - - const form = await loadPdfForm(pdfPath); - - const fields = form.getFields(); - const csFiles = await Promise.all( - csPath.split(",").map((c) => Deno.readTextFile(c.trim())), - ); - - const fieldNames: string[] = fields.map((f) => f.getName()) - .filter((f) => { - const rx = new RegExp( - `(? rx.test(c)); - }) - .filter((f) => !f.toLowerCase().includes("signature")); - - if (fieldNames.length) { - console.log( - "%cThe following field names are not present in the CS code", - "color: red", - ); - console.log(fieldNames); - alert("Your princess is in another castle..."); - } else { - console.log("%cAll form fields present", "color: lime"); - alert("Ok!"); +function getCaseSyntaxPatternByFileExtension( + extenstion: string, + field: string, +) { + switch (extenstion.trim().toLowerCase().replace(".", "")) { + case "cs": + case "js": + case "ts": + default: + return `(? "); + description = "Checks if form fields are present in a given code file"; + private block?: TerminalBlock; + setBlock(block: TerminalBlock) { + this.block = block; + this.block.setPreserveHistory(true); } - async run(...args: string[]) { - await callWithArgPrompt(checkFile, [ - ["Please provide path to PDF file:", (p) => !!p && p.endsWith(".pdf")], - [ - "Please provide path to CS file (comma separated for multiple):", - (p) => !!p && p.endsWith(".cs"), - ], - ], args); + async help() { + cliLog("Usage: checkcode ", this.block); + await cliAlert("", this.block); + } + async run(pdfPath: string, codePaths: string) { + [pdfPath, codePaths] = await forceArgs([pdfPath, codePaths], [ + "Please provide path to PDF file:", + "Please provide path(s) to code file(s) (comma separated for multiple):", + ]); + + const form = await loadPdfForm(pdfPath); + + const fields = form.getFields(); + const codeFiles: [string, string][] = codePaths.split(",").map(( + c, + ) => [c, Deno.readTextFileSync(c.trim())]); + + const fieldNames: string[] = fields.map((f) => f.getName()) + .filter((f) => !f.toLowerCase().includes("signature")); + let unfound = fieldNames.slice(); + + for (const [path, content] of codeFiles) { + unfound = unfound.filter((f) => { + const rx = new RegExp( + getCaseSyntaxPatternByFileExtension(path.split(".").at(-1) ?? "", f), + ); + return rx.test(content); + }); + } + + if (unfound.length) { + cliLog( + colorize( + "The following field names are not present in the CS code", + "red", + ), + this.block, + ); + cliLog(unfound, this.block); + await cliAlert("Your princess is in another castle...", this.block); + } else { + cliLog(colorize("All form fields present", "green"), this.block); + await cliAlert("Ok!", this.block); + } } } diff --git a/tools/fieldRename.ts b/tools/fieldRename.ts index f0d027a..36c97e2 100644 --- a/tools/fieldRename.ts +++ b/tools/fieldRename.ts @@ -87,8 +87,8 @@ class RenameFields implements ITool { this.block = block; } - help(standalone = false) { - cliAlert( + async help(standalone = false) { + await cliAlert( "Usage: rename-fields \n", standalone ? undefined : this.block, ); -- 2.47.2 From 6346b285814d096393a9d9a71c4b9b3cdd620230 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 6 May 2025 17:53:17 -0600 Subject: [PATCH 3/5] v1 ready for publish --- cli/index.ts | 32 ++++++++++++++++++++------------ cli/selectMenu.ts | 1 + deno.json | 4 +++- deno.lock | 7 ++++++- main.ts | 1 + tools/checkCode.ts | 3 ++- tools/fieldRename.ts | 1 + tools/listFormFields.ts | 1 + types.ts | 22 ++++++++++------------ util/call.ts | 2 ++ util/saveLoadPdf.ts | 7 ++++++- 11 files changed, 53 insertions(+), 28 deletions(-) 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); -- 2.47.2 From d1072d8a810237dfe9d2ccb8509eb44b3ccbe556 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 6 May 2025 19:22:25 -0600 Subject: [PATCH 4/5] setup ci --- .gitea/workflows/build-and-release.yml | 30 ++++++++++++++++++++++++++ .gitea/workflows/tag-cli.yml | 13 +++++++++++ 2 files changed, 43 insertions(+) create mode 100644 .gitea/workflows/build-and-release.yml create mode 100644 .gitea/workflows/tag-cli.yml diff --git a/.gitea/workflows/build-and-release.yml b/.gitea/workflows/build-and-release.yml new file mode 100644 index 0000000..bce1add --- /dev/null +++ b/.gitea/workflows/build-and-release.yml @@ -0,0 +1,30 @@ +name: Build and Release + +on: + push: + tags: + - "v*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build and release binaries + uses: bearmetal/ci-actions/deno-publish@main + with: + entrypoint: main.ts + compile-flags: "--allow-read --allow-write --allow-env --allow-net" + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: 2.3.1 + + - name: Publish to JSR + run: deno publish --token ${{ secrets.JSR_TOKEN }} diff --git a/.gitea/workflows/tag-cli.yml b/.gitea/workflows/tag-cli.yml new file mode 100644 index 0000000..579cdc0 --- /dev/null +++ b/.gitea/workflows/tag-cli.yml @@ -0,0 +1,13 @@ +name: Create Version Tag + +on: + push: + branches: + - main + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bearmetal/ci-actions/version-check@main -- 2.47.2 From 91eb569d4b81b5f1ef70f1a61c38dfebfe3f26ca Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 6 May 2025 19:24:21 -0600 Subject: [PATCH 5/5] bump version --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index d1a927f..c620ea1 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@bearmetal/pdf-tools", - "version": "0.0.6", + "version": "1.0.0", "license": "GPL 3.0", "tasks": { "dev": "deno run -A --env-file=.env --watch main.ts", -- 2.47.2