diff --git a/assets/STHUPO.TTF b/assets/STHUPO.TTF new file mode 100644 index 0000000..d1104b9 Binary files /dev/null and b/assets/STHUPO.TTF differ diff --git a/output.png b/output.png deleted file mode 100644 index 4d504b6..0000000 Binary files a/output.png and /dev/null differ diff --git a/output_with_watermark.png b/output_with_watermark.png deleted file mode 100644 index 6d8607f..0000000 Binary files a/output_with_watermark.png and /dev/null differ diff --git a/package.json b/package.json index 78509df..af1ac96 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,14 @@ "test": "echo \"Error: no test specified\" && exit 1", "cover": "node ./src/product_cover_img.js", "ppt": "node ./src/ppt_to_img.js", - "merge": "node ./src/img_merge.js" + "merge": "node ./src/img_merge.js", + "water": "node ./src/water_market.js", + "pptToImg": "npm run ppt && npm run merge && npm run water && npm run cover" }, "author": "", "license": "ISC", "dependencies": { - "sharp": "^0.33.5" + "sharp": "^0.33.5", + "text-to-svg": "^3.1.5" } } diff --git a/src/ppt_to_img.js b/src/ppt_to_img.js index d68b626..5028088 100644 --- a/src/ppt_to_img.js +++ b/src/ppt_to_img.js @@ -6,7 +6,6 @@ const { ppt } = require('../config/index'); //工作目录文件夹路径 const inputDir = getInputDir(); - if (!inputDir) { console.log('未执行ppt转图片功能,工作目录不存在'); return; diff --git a/src/product_cover_img.js b/src/product_cover_img.js index 173db3e..d99dfd8 100644 --- a/src/product_cover_img.js +++ b/src/product_cover_img.js @@ -6,8 +6,8 @@ const { cover } = require('../config/index'); //工作目录文件夹路径 let inputDir = getInputDir(); - -if (!inputDir) { +const previewDir = `${inputDir}/预览图`; +if (!inputDir || !previewDir) { console.log('未执行裁剪主图功能,工作目录不存在'); return; } @@ -45,10 +45,13 @@ async function resizeImage(imageDir, outputDir, scale) { } //读取输入目录下所有文件 -fs.readdirSync(inputDir).forEach(async (file) => { +fs.readdirSync(previewDir).forEach(async (file, index) => { + if (index > 4) { + return; + } const ext = file.split('.').pop().toLowerCase(); if (cover.imgFormat.has(ext)) { - const inputPath = path.join(inputDir, file); + const inputPath = path.join(previewDir, file); cover.scaleArr.forEach((item) => { const outputPath = path.join(item.dir, file); resizeImage(inputPath, outputPath, item.scale) diff --git a/src/water_market.js b/src/water_market.js new file mode 100644 index 0000000..99d8255 --- /dev/null +++ b/src/water_market.js @@ -0,0 +1,99 @@ +const fs = require('fs'); +const path = require('path'); +const sharp = require('sharp'); +const Text2SVG = require('text-to-svg'); +const { getInputDir } = require('../utils/index'); +const { cover } = require('../config/index'); + +//工作目录文件夹路径 +const inputDir = getInputDir(); +if (!inputDir) { + console.log('未执行添加水印功能,工作目录不存在'); + return; +} + +//需要拼图的文件夹 +const previewDir = `${inputDir}/预览图`; +if (!previewDir) { + console.log('未执行添加水印功能,预览图目录不存在'); + return; +} + +async function nodeGenWatermark({ img, text, filepath }) { + /** + * @desc 将水印文字转换成 svg,再转换成buffer + * @param {string} text 水印文字 + * @param {number} fontSize 字体大小 + * @param {string} color 字体颜色 + * @return {Buffer} + */ + function text2SVG({ text, fontSize = 24, color = 'rgba(204, 204, 204, 0.3)' }) { + const fontPath = path.join(__dirname, '../assets/STHUPO.TTF'); + // 加载字体文件 + const text2SVG = Text2SVG.loadSync(fontPath); + const options = { + fontSize, + anchor: 'top', // 坐标中的对象锚点 + attributes: { fill: color }, // 文字颜色 + }; + const textSVG = text2SVG.getSVG(text, options); + return Buffer.from(textSVG); + } + + /** + * @desc 水印图片旋转45度倾斜 + * @param {string} text 水印文字 + * @return {Promise} + */ + async function rotateWatermarkBuffer(text) { + // ` ${text} ` 增加下文字间距 + const textBuffer = text2SVG({ text: ` ${text} ` }); + return sharp(textBuffer) + .rotate(330, { background: { r: 255, g: 255, b: 255, alpha: 0 } }) // 旋转330度,并且透明色 + .toBuffer(); + } + + /** + * @desc 入口文件 + * @param {string|Buffer} img 图片本地路径或图片 Buffer 数据 + * @param {string} text 水印文字 + * @param {string} filepath 保存合成水印后的文件路径 + * @return {Promise} + */ + async function init({ img, text, filepath }) { + const textBuffer = await rotateWatermarkBuffer(text); + const imgInfo = await sharp(img) + // 重复(tile)合并图像 + .composite([{ input: textBuffer, tile: true }]) + .toFile(filepath); + return imgInfo; + } + + await init({ img, text, filepath }); +} + +fs.readdirSync(previewDir).forEach((file) => { + const ext = file.split('.').pop().toLowerCase(); + if (cover.imgFormat.has(ext)) { + const imgPath = path.join(previewDir, file); + const tempImgPath = path.join(previewDir, `${file}_temp`); + nodeGenWatermark({ + img: imgPath, + text: '创意素材铺', + filepath: tempImgPath, + }) + .then(() => { + // 处理完后,将临时文件重命名为原始文件 + fs.rename(tempImgPath, imgPath, (err) => { + if (err) { + console.error('重命名失败:', err); + } else { + console.log('水印添加成功,文件已被覆盖:', imgPath); + } + }); + }) + .catch((err) => { + console.error('发生错误:', err); + }); + } +});