diff --git a/.gitignore b/.gitignore index 525e708..9071fa6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/ node_modules/ # ---> macOS # General diff --git a/image-watermark/deno.json b/image-watermark/deno.json index aec0520..255f223 100644 --- a/image-watermark/deno.json +++ b/image-watermark/deno.json @@ -6,6 +6,7 @@ }, "nodeModulesDir": "auto", "imports": { + "@std/cli/parse-args": "jsr:@std/cli/parse-args", "@std/assert": "jsr:@std/assert@1", "sharp": "npm:sharp@^0.33.0" } diff --git a/image-watermark/deno.lock b/image-watermark/deno.lock index a6620ea..33cfd82 100644 --- a/image-watermark/deno.lock +++ b/image-watermark/deno.lock @@ -254,6 +254,7 @@ "workspace": { "dependencies": [ "jsr:@std/assert@1", + "jsr:@std/cli@*", "npm:sharp@0.33" ] } diff --git a/image-watermark/main.ts b/image-watermark/main.ts index 7368c58..ce77156 100755 --- a/image-watermark/main.ts +++ b/image-watermark/main.ts @@ -1,8 +1,8 @@ #!/usr/bin/env -S deno run -A import sharp from "sharp"; -import { Buffer } from "node:buffer"; -import {parseArgs} from "jsr:@std/cli/parse-args"; +import {Buffer} from "node:buffer"; +import {parseArgs} from "@std/cli/parse-args"; export interface WatermarkOptions { /** 水印文字 */ @@ -23,6 +23,13 @@ export interface WatermarkOptions { outputPath?: string; } +function getDefaultOutputPath(inputPath: string): string { + const lastDot = inputPath.lastIndexOf("."); + return lastDot !== -1 + ? `${inputPath.slice(0, lastDot)}_watermarked${inputPath.slice(lastDot)}` + : `${inputPath}_watermarked`; +} + /** * 在图片右下角添加文字水印 * @param inputPath 输入图片路径 @@ -44,10 +51,7 @@ export async function addWatermark( let outputPath = options.outputPath; if (!outputPath) { - const lastDot = inputPath.lastIndexOf("."); - outputPath = lastDot !== -1 - ? `${inputPath.slice(0, lastDot)}_watermarked${inputPath.slice(lastDot)}` - : `${inputPath}_watermarked`; + outputPath = getDefaultOutputPath(inputPath); } // 获取原图信息 @@ -68,30 +72,40 @@ export async function addWatermark( // text-anchor="end" 意味着文字的右边缘在指定的 x 坐标 const textX = width - marginRight - 20; const textY = height - marginBottom; - + // 背景矩形的左上角坐标 // 背景右边缘与文字右边缘对齐 const rectX = textX - bgWidth + 20; const rectY = textY - bgHeight + backgroundPadding + 2; // 创建最终 SVG 水印 - const svg = ` - ${backgroundColor ? `` : ""} - ${escapeXml(text)} + const svg = + ` + ${ + backgroundColor + ? `` + : "" + } + ${ + escapeXml(text) + } `; // 将 SVG 水印叠加到原图 - await sharp(inputPath) - .composite([ - { - input: Buffer.from(svg), - top: 0, - left: 0, - }, - ]) + await sharp(inputPath).composite([ + { + input: Buffer.from(svg), + top: 0, + left: 0, + }, + ]) .toFile(outputPath); - console.log(`Added watermark: ${outputPath}`); + if (inputPath == outputPath) { + console.log(`Added watermark: ${inputPath}`); + } else { + console.log(`Added watermark: ${inputPath} -> ${outputPath}`); + } } /** @@ -108,8 +122,8 @@ function escapeXml(str: string): string { if (import.meta.main) { const flags = parseArgs(Deno.args, { - boolean: ["help"], - string: ["watermark"], + boolean: ["help", "unsafe-replace-file"], + string: ["watermark"], }); const watermarkText = flags.watermark; @@ -117,17 +131,26 @@ if (import.meta.main) { if (!watermarkText || imagePaths.length == 0) { console.log("Usage: deno run -A main.ts --watermark 'watermark' "); - console.log("Example: deno run -A main.ts --watermark '© 2026 MyName' image.jpg"); + console.log( + "Example: deno run -A main.ts --watermark '© 2026 MyName' image.jpg", + ); Deno.exit(1); } for (const imagePath of imagePaths) { - await addWatermark(imagePath+'', { + const imagePathStr = imagePath as string; + + const options = { fontSize: 32, text: watermarkText, - color: 'black', + color: "black", backgroundPadding: 2, - backgroundColor: 'white' - }); + backgroundColor: "white", + } as WatermarkOptions; + if (flags["unsafe-replace-file"]) { + options.outputPath = imagePathStr; + } + + await addWatermark(imagePathStr, options); } }