import gm from 'gm'; import axios from 'axios'; import sizeOf from 'image-size'; import path from 'node:path'; import fs from 'node:fs'; import PDFDocument from 'pdfkit'; import logger from 'electron-log/renderer'; import { getImagicPath, getTempPath } from './utils'; import { DrawTrackItem, DrawTrackCircleOption, DrawTrackLineOption, DrawTrackTextOption, } from './types'; // macos install gm imagemagick https://github.com/aheckmann/gm/blob/master/README.md const gmInst = process.platform === 'win32' ? gm.subClass({ imageMagick: '7+', appPath: getImagicPath(), }) : gm.subClass({ imageMagick: '7+' }); function cropImage(imgPath: string): Promise { return new Promise((resolve, reject) => { const outpath = path.join(getTempPath(), '001.png'); gmInst(imgPath) .crop(500, 200, 0, 0) .write(outpath, (err) => { if (!err) { return resolve(outpath); } return reject(err); }); }); } function drawTrack( imgPath: string, drawTrackList: DrawTrackItem[], outpath: string ): Promise { return new Promise((resolve, reject) => { const gmObj = gmInst(imgPath); const defaultColor = '#f53f3f'; const defaultFontSize = 14; drawTrackList.forEach((track) => { // text if (track.type === 'text') { const { x, y, text, color, fontSize } = track.option as DrawTrackTextOption; gmObj .fill(color || defaultColor) .fontSize(fontSize || defaultFontSize) .drawText(x, y, text); return; } // circle if (track.type === 'circle') { const { x0, y0, x1, y1 } = track.option as DrawTrackCircleOption; gmObj.drawCircle(x0, y0, x1, y1).stroke(defaultColor, 2); return; } // line if (track.type === 'line') { const { x0, y0, x1, y1 } = track.option as DrawTrackLineOption; gmObj.drawLine(x0, y0, x1, y1).stroke(defaultColor, 2); } }); gmObj.write(outpath, (err) => { if (!err) { return resolve(outpath); } return reject(err); }); }); } async function downloadFile(url: string, outputPath: string) { const writer = fs.createWriteStream(outputPath); const response = await axios({ url, method: 'GET', responseType: 'stream', }); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', resolve); writer.on('error', reject); }); } async function downloadImage(url: string, outputPath: string) { await downloadFile(url, outputPath); const size = sizeOf(outputPath); return { url: outputPath, width: size.width || 100, height: size.height || 100, }; } function joinPath(paths: string[]) { return path.join(...paths); } interface ImageItem { url: string; width: number; height: number; } async function imagesToPdf( images: ImageItem[], outpath: string ): Promise { return new Promise((resolve, reject) => { const doc = new PDFDocument(); const steam = fs.createWriteStream(outpath); doc.pipe(steam); images.forEach((image) => { const { url, width, height } = image; doc.addPage({ size: [width, height] }); doc.image(url, 0, 0, { width, height }); }); doc.end(); steam.on('finish', () => { resolve(outpath); }); steam.on('error', (err) => { reject(err); }); }); } const commonApi = { cropImage, drawTrack, joinPath, downloadImage, imagesToPdf, logger, }; export type CommonApi = typeof commonApi; export default commonApi;