From b47e5755f677056e8acecad54c0c2e28a5e137f3 Mon Sep 17 00:00:00 2001 From: C4illin Date: Fri, 23 May 2025 21:18:47 +0200 Subject: [PATCH] feat: add ImageMagick issue: #295, #269 --- Dockerfile | 1 + src/converters/imagemagick.ts | 489 ++++++++++++++++++++++++++++++++++ src/converters/main.ts | 11 +- src/helpers/printVersions.ts | 10 + 4 files changed, 508 insertions(+), 3 deletions(-) create mode 100644 src/converters/imagemagick.ts diff --git a/Dockerfile b/Dockerfile index b56dacd..3c264ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ RUN apt-get update && apt-get install -y \ libjxl-tools \ libva2 \ libvips-tools \ + imagemagick-7.q16 \ pandoc \ poppler-utils \ potrace \ diff --git a/src/converters/imagemagick.ts b/src/converters/imagemagick.ts new file mode 100644 index 0000000..f4fc234 --- /dev/null +++ b/src/converters/imagemagick.ts @@ -0,0 +1,489 @@ +import { execFile } from "node:child_process"; + +// declare possible conversions +export const properties = { + from: { + images: [ + "3fr", + "3g2", + "3gp", + "aai", + "ai", + "apng", + "art", + "arw", + "avci", + "avi", + "avif", + "avs", + "bayer", + "bayera", + "bgr", + "bgra", + "bgro", + "bmp", + "bmp2", + "bmp3", + "cal", + "cals", + "canvas", + "caption", + "cin", + "clip", + "clipboard", + "cmyk", + "cmyka", + "cr2", + "cr3", + "crw", + "cube", + "cur", + "cut", + "data", + "dcm", + "dcr", + "dcraw", + "dcx", + "dds", + "dfont", + "dng", + "dpx", + "dxt1", + "dxt5", + "emf", + "epdf", + "epi", + "eps", + "epsf", + "epsi", + "ept", + "ept2", + "ept3", + "erf", + "exr", + "farbfeld", + "fax", + "ff", + "fff", + "file", + "fits", + "fl32", + "flif", + "flv", + "fractal", + "ftp", + "fts", + "ftxt", + "g3", + "g4", + "gif", + "gif87", + "gradient", + "gray", + "graya", + "group4", + "hald", + "hdr", + "heic", + "heif", + "hrz", + "http", + "https", + "icb", + "ico", + "icon", + "iiq", + "inline", + "ipl", + "j2c", + "j2k", + "jng", + "jnx", + "jp2", + "jpc", + "jpe", + "jpeg", + "jpg", + "jpm", + "jps", + "jpt", + "jxl", + "k25", + "kdc", + "label", + "m2v", + "m4v", + "mac", + "map", + "mask", + "mat", + "mdc", + "mef", + "miff", + "mkv", + "mng", + "mono", + "mos", + "mov", + "mp4", + "mpc", + "mpeg", + "mpg", + "mpo", + "mrw", + "msl", + "msvg", + "mtv", + "mvg", + "nef", + "nrw", + "null", + "ora", + "orf", + "otb", + "otf", + "pal", + "palm", + "pam", + "pango", + "pattern", + "pbm", + "pcd", + "pcds", + "pcl", + "pct", + "pcx", + "pdb", + "pdf", + "pdfa", + "pef", + "pes", + "pfa", + "pfb", + "pfm", + "pgm", + "pgx", + "phm", + "picon", + "pict", + "pix", + "pjpeg", + "plasma", + "png", + "png00", + "png24", + "png32", + "png48", + "png64", + "png8", + "pnm", + "pocketmod", + "ppm", + "ps", + "psb", + "psd", + "ptif", + "pwp", + "qoi", + "radial", + "raf", + "ras", + "raw", + "rgb", + "rgb565", + "rgba", + "rgbo", + "rgf", + "rla", + "rle", + "rmf", + "rsvg", + "rw2", + "rwl", + "scr", + "screenshot", + "sct", + "sfw", + "sgi", + "six", + "sixel", + "sr2", + "srf", + "srw", + "stegano", + "sti", + "strimg", + "sun", + "svg", + "svgz", + "text", + "tga", + "tiff", + "tiff64", + "tile", + "tim", + "tm2", + "ttc", + "ttf", + "txt", + "uyvy", + "vda", + "vicar", + "vid", + "viff", + "vips", + "vst", + "wbmp", + "webm", + "webp", + "wmf", + "wmv", + "wpg", + "x3f", + "xbm", + "xc", + "xcf", + "xpm", + "xps", + "xv", + "ycbcr", + "ycbcra", + "yuv", + ], + }, + to: { + images: [ + "aai", + "ai", + "apng", + "art", + "ashlar", + "avif", + "avs", + "bayer", + "bayera", + "bgr", + "bgra", + "bgro", + "bmp", + "bmp2", + "bmp3", + "brf", + "cal", + "cals", + "cin", + "cip", + "clip", + "clipboard", + "cmyk", + "cmyka", + "cur", + "data", + "dcx", + "dds", + "dpx", + "dxt1", + "dxt5", + "epdf", + "epi", + "eps", + "eps2", + "eps3", + "epsf", + "epsi", + "ept", + "ept2", + "ept3", + "exr", + "farbfeld", + "fax", + "ff", + "fits", + "fl32", + "flif", + "flv", + "fts", + "ftxt", + "g3", + "g4", + "gif", + "gif87", + "gray", + "graya", + "group4", + "hdr", + "histogram", + "hrz", + "htm", + "html", + "icb", + "ico", + "icon", + "info", + "inline", + "ipl", + "isobrl", + "isobrl6", + "j2c", + "j2k", + "jng", + "jp2", + "jpc", + "jpe", + "jpeg", + "jpg", + "jpm", + "jps", + "jpt", + "json", + "jxl", + "m2v", + "m4v", + "map", + "mask", + "mat", + "matte", + "miff", + "mkv", + "mng", + "mono", + "mov", + "mp4", + "mpc", + "mpeg", + "mpg", + "msl", + "msvg", + "mtv", + "mvg", + "null", + "otb", + "pal", + "palm", + "pam", + "pbm", + "pcd", + "pcds", + "pcl", + "pct", + "pcx", + "pdb", + "pdf", + "pdfa", + "pfm", + "pgm", + "pgx", + "phm", + "picon", + "pict", + "pjpeg", + "png", + "png00", + "png24", + "png32", + "png48", + "png64", + "png8", + "pnm", + "pocketmod", + "ppm", + "ps", + "ps2", + "ps3", + "psb", + "psd", + "ptif", + "qoi", + "ras", + "rgb", + "rgba", + "rgbo", + "rgf", + "rsvg", + "sgi", + "shtml", + "six", + "sixel", + "sparse", + "strimg", + "sun", + "svg", + "svgz", + "tga", + "thumbnail", + "tiff", + "tiff64", + "txt", + "ubrl", + "ubrl6", + "uil", + "uyvy", + "vda", + "vicar", + "vid", + "viff", + "vips", + "vst", + "wbmp", + "webm", + "webp", + "wmv", + "wpg", + "xbm", + "xpm", + "xv", + "yaml", + "ycbcr", + "ycbcra", + "yuv", + ], + }, +}; + +export function convert( + filePath: string, + fileType: string, + convertTo: string, + targetPath: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + options?: unknown, +): Promise { + let outputArgs: string[] = []; + let inputArgs: string[] = []; + + if (convertTo === "ico") { + outputArgs = [ + "-define", + "icon:auto-resize=256,128,64,48,32,16", + "-background", + "none", + ]; + + if (fileType === "svg") { + // this might be a bit too much, but it works + inputArgs = ["-background", "none", "-density", "512"]; + } + } + + return new Promise((resolve, reject) => { + execFile( + "magick", + [...inputArgs, filePath, ...outputArgs, targetPath], + (error, stdout, stderr) => { + if (error) { + reject(`error: ${error}`); + } + + if (stdout) { + console.log(`stdout: ${stdout}`); + } + + if (stderr) { + console.error(`stderr: ${stderr}`); + } + + resolve("Done"); + }, + ); + }); +} diff --git a/src/converters/main.ts b/src/converters/main.ts index 47cbcea..d544c0a 100644 --- a/src/converters/main.ts +++ b/src/converters/main.ts @@ -10,7 +10,8 @@ import { convert as convertImage, properties as propertiesImage } from "./vips"; import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex"; import { convert as convertCalibre, properties as propertiesCalibre } from "./calibre"; import { convert as convertLibheif, properties as propertiesLibheif } from "./libheif"; -import { convert as convertpotrace, properties as propertiespotrace } from "./potrace"; +import { convert as convertPotrace, properties as propertiesPotrace } from "./potrace"; +import { convert as convertImagemagick, properties as propertiesImagemagick } from "./imagemagick"; // This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular @@ -71,6 +72,10 @@ const properties: Record< properties: propertiesPandoc, converter: convertPandoc, }, + imagemagick: { + properties: propertiesImagemagick, + converter: convertImagemagick, + }, graphicsmagick: { properties: propertiesGraphicsmagick, converter: convertGraphicsmagick, @@ -88,8 +93,8 @@ const properties: Record< converter: convertFFmpeg, }, potrace: { - properties: propertiespotrace, - converter: convertpotrace, + properties: propertiesPotrace, + converter: convertPotrace, }, }; diff --git a/src/helpers/printVersions.ts b/src/helpers/printVersions.ts index 312b56b..42044d2 100644 --- a/src/helpers/printVersions.ts +++ b/src/helpers/printVersions.ts @@ -44,6 +44,16 @@ if (process.env.NODE_ENV === "production") { } }); + exec("magick --version", (error, stdout) => { + if (error) { + console.error("ImageMagick is not installed."); + } + + if (stdout) { + console.log(stdout.split("\n")[0]?.replace("Version: ", "")); + } + }); + exec("gm version", (error, stdout) => { if (error) { console.error("GraphicsMagick is not installed.");