一、ImageMagick安装
在Linux上安装ImageMagick 7.0及以上版本可以通过源码编译或第三方仓库实现(6.x版本可以直接yum下载)。以下是详细步骤:
方法一:通过源码编译安装(通用方法)
1. 安装依赖
# Debian/Ubuntu
sudo apt update
sudo apt install build-essential libwebp-dev libpng-dev libjpeg-dev libtiff-dev libbz2-dev liblzma-dev zlib1g-dev pkg-config
# CentOS/RHEL
sudo yum groupinstall "Development Tools"
sudo yum install libwebp-devel libpng-devel libjpeg-devel libtiff-devel bzip2-devel xz-devel zlib-devel
2. 下载源码
wget https://imagemagick.org/archive/ImageMagick.tar.gz
tar xvzf ImageMagick.tar.gz
cd ImageMagick-7.*
3. 配置和编译
./configure --prefix=/usr/local
make
sudo make install
4. 更新动态链接库
sudo ldconfig /usr/local/lib
5. 验证安装
magick --version
方法二:使用第三方仓库(以Ubuntu为例)
1. 添加官方APT仓库(支持最新版)
sudo add-apt-repository ppa:imagemagick/stable
sudo apt update
2. 安装ImageMagick 7
sudo apt install imagemagick
3. 验证版本
magick --version
方法三:CentOS/RHEL(使用预编译包)
1. 启用EPEL仓库
sudo yum install epel-release
2. 安装ImageMagick 7
sudo yum install ImageMagick ImageMagick-devel
3. 验证安装
magick --version
注意事项
- 命令变化:ImageMagick 7将部分工具(如
convert
)整合到magick
命令,建议使用新语法:magick input.jpg output.png
- 卸载旧版本:若已安装旧版,建议先卸载:
sudo apt remove imagemagick # Ubuntu sudo yum remove ImageMagick # CentOS
- 路径问题:源码安装后,若找不到命令,尝试注销重新登录或更新环境变量。
二、ImageMagick配置检查
要查看ImageMagick安装时配置的组件和支持的功能,可以使用以下命令:
1. 查看完整的编译配置信息
magick -version
输出会包含详细的编译选项(Configured with: 部分),例如:
Version: ImageMagick 7.1.1-20
...
Configured with:
--prefix=/usr/local
--enable-shared
--enable-openmp
--with-jpeg
--with-png
--with-tiff
--with-webp
--with-heic
...
2. 仅查看启用的组件和委托(Delegates)
magick -list configure
输出示例:
Name Value
-----------------------------------------------------------------------------
DELEGATES bzlib cairo djvu fftw fontconfig freetype heic jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png raqm raw rsvg tiff webp wmf x xml zip zlib
FEATURES DPC Cipher OpenMP
CONFIGURE ./configure '--prefix=/usr/local' '--enable-shared' '--enable-openmp' '--with-jpeg' '--with-png' '--with-webp' '--with-heic'
...
关键信息说明
DELEGATES
:支持的图像格式和依赖库(如jpeg
、png
、webp
等)。FEATURES
:启用的功能(如OpenMP
并行加速、HDRI
高动态范围等)。CONFIGURE
:编译时的配置参数(安装时的自定义选项)。
3. 检查特定功能是否启用
示例:检查是否支持WebP格式
magick -list format | grep -i webp
输出示例:
WEBP* WEBP rw- WebP Image Format (libwebp 1.3.0 [020C])
4. 查看所有支持的图像格式
magick -list format
常见问题排查
-
命令找不到
如果通过源码安装到自定义路径(如/usr/local
),可能需要指定完整路径:/usr/local/bin/magick -version
-
缺少组件支持
如果发现缺少某些格式(如heic
、webp
),需重新编译安装并添加对应依赖库:# 安装依赖(以Ubuntu为例) sudo apt install libwebp-dev libheif-dev # 重新编译ImageMagick时添加选项: ./configure --with-webp --with-heic
通过以上方法,你可以快速验证ImageMagick的配置信息,确保所需功能已正确启用。
三、ImageMagick应用问题排查
(一). 安装后使用指令magick 合成图片和PDF为新的PDF文件报错
问题分析
从错误日志可以看出,主要存在以下问题:
1. 命令语法错误
- 错误原因:
convert_
是拼写错误(可能应为convert
),但ImageMagick 7中已弃用convert
命令。- 参数顺序和语法不符合
magick
命令规范(如-geometry
和-composite
的使用方式)。
2. 缺少Ghostscript依赖
- 错误提示:
gs: 未找到命令
- ImageMagick依赖Ghostscript(
gs
)处理PDF文件,但系统中未安装该工具。
- ImageMagick依赖Ghostscript(
3. 缺少必要的委托库(Delegate Libraries)
- 错误提示:
delegate library support not built-in 'suofang.png'
no decode delegate for this image format
- 编译ImageMagick时未启用某些格式支持(如PDF、PNG),或相关依赖库未安装。
解决方案
1. 修正命令语法
使用 magick
代替 convert
,并调整参数顺序:
magick kongba1A4.pdf suofang.png -geometry +34+0 -density 300 -composite out.pdf
或更明确的合成操作:
magick kongba1A4.pdf -density 300 \( suofang.png -geometry +34+0 \) -composite out.pdf
2. 安装Ghostscript
-
Ubuntu/Debian:
sudo apt install ghostscript
-
CentOS/RHEL:
sudo yum install ghostscript
3. 确保ImageMagick支持PDF和PNG格式
重新编译ImageMagick并启用必要依赖:
# 安装依赖库
# Ubuntu/Debian
sudo apt install libpng-dev libjpeg-dev libtiff-dev ghostscript libgs-dev
# CentOS/RHEL
sudo yum install libpng-devel libjpeg-devel libtiff-devel ghostscript ghostscript-devel
# 重新编译ImageMagick
cd ImageMagick-7.*
./configure --with-png --with-jpeg --with-tiff --with-gslib
make clean && make && sudo make install
sudo ldconfig /usr/local/lib
4. 验证修复
- 检查Ghostscript是否安装成功:
gs --version
- 检查ImageMagick是否支持PDF和PNG:
输出应包含:magick -list format | grep -E "PDF|PNG"
PDF* PDF rw- Portable Document Format PNG* PNG rw- Portable Network Graphics
总结
- 命令修正:使用
magick
代替convert
,并调整参数顺序。 - 安装依赖:确保Ghostscript和图像格式依赖库已安装。
- 重新编译:若问题仍存在,重新编译ImageMagick并启用缺失的委托库。
完成以上步骤后,再次执行命令即可正常生成 out.pdf
。
(二). 安装后使用指令magick 缩放图片时告警
从错误信息来看,ImageMagick 在处理 JPEG 文件时遇到了与 XMP(可扩展元数据平台)相关的问题。具体来说,错误提示 delegate library support not built-in
表明 ImageMagick 没有正确配置或缺少处理 XMP 元数据所需的库。
解决方法:
-
更新 ImageMagick
确保你使用的是最新版本的 ImageMagick,因为较新的版本可能已经修复了与 XMP 相关的问题。bash复制
sudo yum update sudo yum install imagemagick
-
安装支持 XMP 的依赖库
ImageMagick 需要libxml2
和libxmp
等库来正确处理 XMP 元数据。确保这些库已安装:bash复制
sudo yum install libxml2 libxml2-devel libxmp-devel
-
重新编译 ImageMagick
如果更新和安装依赖库后问题仍然存在,可以尝试重新编译 ImageMagick,确保启用了 XMP 支持:bash复制
cd ImageMagick-7.1.0 ./configure --with-xmp make sudo make install
-
验证安装
重新安装后,验证 ImageMagick 是否支持 XMP:bash复制
magick -version
在输出中检查是否包含
XMP
支持。 -
忽略 XMP 元数据
如果你不需要处理 XMP 元数据,可以通过以下命令忽略它:bash复制
magick dc91b649-2343-47cf-93f3-f856c0df3f1aOriginalImageTemp.jpg -resize 45% -filter Lanczos -density 300 -strip suofang.png
-strip
参数会移除所有元数据,包括 XMP。
总结:
- 更新 ImageMagick 和相关依赖库。
- 如果问题仍然存在,重新编译 ImageMagick 并启用 XMP 支持。
- 如果不需要 XMP 元数据,可以使用
-strip
参数忽略它。
四、ImageMagick应用指令
以下是 ImageMagick 常用命令大全,涵盖图像格式转换、调整、编辑、合成等场景。基于 ImageMagick 7+(推荐使用 magick
替代 convert
)。
(一)、基础操作
1. 格式转换
magick input.jpg output.png # JPG 转 PNG
magick input.pdf output.png # PDF 转 PNG(需要Ghostscript)
magick input.webp output.tiff # WebP 转 TIFF
2. 调整图像尺寸
magick input.jpg -resize 800x600 output.jpg # 等比缩放到 800x600
magick input.jpg -resize 50% output.jpg # 缩放为原图的50%
magick input.jpg -resize 800x600\! output.jpg # 强制拉伸到 800x600(忽略比例)
3. 裁剪图像
magick input.jpg -crop 800x600+100+50 output.jpg # 从坐标(100,50)裁剪800x600区域
magick input.jpg -gravity center -crop 50%x50%+0+0 output.jpg # 居中裁剪一半大小
4. 旋转与翻转
magick input.jpg -rotate 90 output.jpg # 顺时针旋转90度
magick input.jpg -flip output.jpg # 垂直翻转
magick input.jpg -flop output.jpg # 水平翻转
(二)、图像增强与特效
1. 调整质量与压缩
magick input.jpg -quality 80 output.jpg # 设置JPEG质量为80%
magick input.png -compress Zip output.pdf # 将PNG压缩为PDF
2. 添加水印
magick input.jpg -draw "text 50,50 'Copyright'" output.jpg # 在(50,50)添加文字水印
magick input.jpg watermark.png -gravity southeast -composite output.jpg # 叠加图片水印到右下角
3. 颜色调整
magick input.jpg -brightness-contrast 10x20 output.jpg # 亮度+10,对比度+20
magick input.jpg -negate output.jpg # 反色
magick input.jpg -grayscale Rec709Luma output.jpg # 灰度化
4. 模糊与锐化
magick input.jpg -blur 0x8 output.jpg # 高斯模糊(半径8)
magick input.jpg -sharpen 0x2 output.jpg # 锐化
magick input.jpg -motion-blur 0x15+30 output.jpg # 运动模糊(强度15,角度30度)
(三)、多图像操作
1. 合并图像(水平/垂直拼接)
magick input1.jpg input2.jpg +append output.jpg # 水平拼接
magick input1.jpg input2.jpg -append output.jpg # 垂直拼接
2. 创建GIF动画
magick -delay 100 frame1.jpg frame2.jpg -loop 0 animation.gif # 每帧延迟100ms,无限循环
3. 多页PDF操作
magick input.pdf[0] output.jpg # 提取PDF第一页为JPG
magick input.pdf -density 150 output.pdf # 设置PDF分辨率(默认72 DPI)
(四)、高级功能
1. 蒙版与合成
magick input.jpg mask.png -alpha off -compose CopyOpacity -composite output.png # 添加透明蒙版
2. 批量处理
mogrify -resize 800x600 *.jpg # 批量调整当前目录所有JPG文件(直接覆盖)
mogrify -format png *.jpg # 批量转换JPG为PNG
3. 获取图像信息
magick identify input.jpg # 显示图像基本信息(格式、尺寸、大小)
magick input.jpg -format "%w x %h" info: # 仅输出宽高
(五)、常见问题解决
1. 缺少依赖库
- 错误提示:
no decode delegate for this image format
# 安装缺失的依赖(以Ubuntu为例) sudo apt install libjpeg-dev libpng-dev libtiff-dev libwebp-dev ghostscript # 重新编译ImageMagick并启用支持 ./configure --with-jpeg --with-png --with-webp --with-gslib
2. PDF处理失败
- 错误提示:
gs: command not found
sudo apt install ghostscript # 安装Ghostscript
3. 命令弃用警告
- 警告提示:
The convert command is deprecated
- 使用
magick
替代convert
,例如:magick input.jpg -resize 800x600 output.jpg # 正确
- 使用
(六)、更多资源
- 官方文档:https://imagemagick.org/script/command-line-tools.php
- 命令速查表:https://imagemagick.org/script/command-line-options.php
(七)、示例命令参数
1-- 生成空白PDF
magick -size 2480x3508 xc:white kongbaiA4.pdf
2-- 缩放图片
magick input.jpg -rotate 90 -resize 45% -filter Lanczos -density 300 suofang.png
3-- 合成PDF
magick kongbaiA4.pdf suofang.png -geometry +141+0 -density 300 -composite out.pdf
4-- 查看尺寸
magick suofang.png -format "%wx%h" info:
五、代码应用范例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* 图片转pdf
*/
public class ImageToPdfConverter {
private static final Logger log = LoggerFactory.getLogger(ImageToPdfConverter.class);
// A4宽度
private static final double widthA4 = 2480;
// A4长度
private static final double heightA4 = 3508;
public static byte[] convertImageToPdf(String suffix, byte[] imageBytes){
log.info("转换开始:{},suffix:{}",new Timestamp(System.currentTimeMillis()).toString(),suffix);
// 临时主键
String uuid = UUID.randomUUID().toString();
// 原临时文件
String originalPath = "/root/"+uuid+"OriginalImageTemp."+suffix;
File originalImageTempFile = null;
// 旋转、缩放图片临时文件
String resizedRotatedPath = "/root/"+uuid+"resizedRotatedTemp.png";
File resizedRotatedFile = null;
// 空白PDF
String blankPdfPath = "/root/"+uuid+"blankPdfTemp.pdf";
File blankPdfTempFile = null;
// pdf临时文件
String pdfPath = "/root/"+uuid+"pdfTemp.pdf";
File pdfTempFile = null;
try{
if (imageBytes == null || imageBytes.length == 0) {
throw new BusinessSilentException("图片字节流为空");
}
// 1、原图片临时文件
log.info("原图片临时文件开始:{}",new Timestamp(System.currentTimeMillis()).toString());
String originalImageTempFileName = uuid+"OriginalImageTemp."+suffix;
saveFileToLocal(imageBytes,originalImageTempFileName);
originalImageTempFile = new File(originalImageTempFileName);
log.info("原图片临时文件结束:{}",new Timestamp(System.currentTimeMillis()).toString());
// 2、计算旋转和缩放后的图片尺寸
log.info("计算旋转和缩放后的图片尺寸开始:{}",new Timestamp(System.currentTimeMillis()).toString());
ImageProcessingInfo info = imageProcessingInfo(originalPath);
log.info("计算旋转和缩放后的图片尺寸结束:{}",new Timestamp(System.currentTimeMillis()).toString());
// 3、旋转并缩放图片
log.info("旋转并缩放图片开始:{}",new Timestamp(System.currentTimeMillis()).toString());
imageProcess(info,resizedRotatedPath);
resizedRotatedFile = new File(resizedRotatedPath);
log.info("旋转并缩放图片结束:{}",new Timestamp(System.currentTimeMillis()).toString());
// 4、创建空白的 A4 PDF
log.info("创建空白的 A4 PDF开始:{}",new Timestamp(System.currentTimeMillis()).toString());
blankPdfProcess(blankPdfPath);
blankPdfTempFile = new File(blankPdfPath);
log.info("创建空白的 A4 PDF结束:{}",new Timestamp(System.currentTimeMillis()).toString());
// 5、将处理后的图片合成到空白 PDF 上并居中
log.info("将处理后的图片合成到空白PDF上并居中开始:{}",new Timestamp(System.currentTimeMillis()).toString());
pdfProcess(info,resizedRotatedPath,blankPdfPath,pdfPath);
log.info("将处理后的图片合成到空白PDF上并居中结束:{}",new Timestamp(System.currentTimeMillis()).toString());
pdfTempFile = new File(pdfPath);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Files.copy(Paths.get(pdfPath), outputStream);
return outputStream.toByteArray();
}catch (Exception e){
log.error("图片转PDF异常:{}",e.getMessage(),e);
throw new BusinessSilentException(String.format("图片转PDF异常:%s",e.getMessage()));
}finally {
// 原文件
if(originalImageTempFile != null && originalImageTempFile.exists()){
originalImageTempFile.delete();
}
// 旋转或缩放的临时图片文件
if(resizedRotatedFile != null && resizedRotatedFile.exists()){
resizedRotatedFile.delete();
}
// 转换为png的临时图片文件
if(blankPdfTempFile != null && blankPdfTempFile.exists()){
blankPdfTempFile.delete();
}
// pdf临时文件
if(pdfTempFile != null && pdfTempFile.exists()){
pdfTempFile.delete();
}
log.info("转换结束:{},suffix:{}",new Timestamp(System.currentTimeMillis()).toString(),suffix);
}
}
public static void saveFileToLocal(byte[] imageBytes, String localPath) {
// 使用 FileOutputStream 将 byte[] 写入到文件
try (FileOutputStream fileOutputStream = new FileOutputStream(localPath)) {
// 将 byte[] 写入文件
fileOutputStream.write(imageBytes);
// 确保所有数据都被写入
fileOutputStream.flush();
} catch (IOException e) {
log.error("保存文件到本地时出错", e);
}
}
/**
* 获取图片转换所需信息
* @param path 原图片临时文件
*/
public static ImageProcessingInfo imageProcessingInfo(String path){
try{
log.info("计算旋转和缩放后的图片尺寸");
String[] identifyCommand = {"magick", path, "-format", "%wx%h", "info:"};
Process identifyProcess = Runtime.getRuntime().exec(identifyCommand);
BufferedReader identifyReader = new BufferedReader(new InputStreamReader(identifyProcess.getInputStream()));
String[] dimensions = identifyReader.readLine().split("x");
int originalWidth = Integer.parseInt(dimensions[0]);
int originalHeight = Integer.parseInt(dimensions[1]);
log.info("图片,宽度:{};高度:{}",originalWidth,originalHeight);
identifyProcess.waitFor();
identifyReader.close();
// 是否旋转
// boolean rotate = originalWidth > widthA4 && originalWidth > originalHeight;
boolean rotate = originalWidth > originalHeight;
// 3、计算缩放比例,以确保图片适应 A4 纸大小(A4 纸在 72dpi 下为 595x842 像素)
// 是否缩小
boolean resized = false;
double scaleRatio = 1;
int newWidth = originalWidth;
int newHeight = originalHeight;
String percentageString = "100%";
if(rotate){
scaleRatio = Math.min(widthA4 / originalHeight, heightA4 / originalWidth);
if(scaleRatio != 1){
resized = true;
newWidth = (int) (originalHeight * scaleRatio);
newHeight = (int) (originalWidth * scaleRatio);
int percentage = (int) (scaleRatio*100);
percentageString = percentage+"%";
}
}else{
// 不旋转
scaleRatio = Math.min(widthA4 / originalWidth, heightA4 / originalHeight);
if(scaleRatio != 1){
resized = true;
newWidth = (int) (originalWidth * scaleRatio);
newHeight = (int) (originalHeight * scaleRatio);
int percentage = (int) (scaleRatio*100);
percentageString = percentage+"%";
}
}
log.info("缩放比例:{};缩放百分比:{};宽度:{};高度:{}",scaleRatio,percentageString,newWidth,newHeight);
return new ImageProcessingInfo(path, newWidth, newHeight,percentageString,rotate,resized);
}catch (Exception e){
log.error("获取图片转换所需信息异常:{}",e.getMessage(),e);
throw new BusinessSilentException(String.format("获取图片转换所需信息异常:%s",e.getMessage()));
}
}
// 存储图片处理信息的内部类
static class ImageProcessingInfo {
String processedImagePath;
int newWidth;
int newHeight;
boolean rotate;
boolean resized;
String percentageString;
ImageProcessingInfo(String processedImagePath, int newWidth, int newHeight,String percentageString,boolean rotate,boolean resized) {
this.processedImagePath = processedImagePath;
this.newWidth = newWidth;
this.newHeight = newHeight;
this.rotate = rotate;
this.resized = resized;
this.percentageString = percentageString;
}
}
/**
* 图片旋转、缩小
* @param info 图片信息
* @param path 处理后文件路径
*/
public static void imageProcess(ImageProcessingInfo info,String path){
try{
List<String> resizedCommandList = new ArrayList<>();
resizedCommandList.add("magick");
resizedCommandList.add(info.processedImagePath);
if(info.rotate){
resizedCommandList.add("-rotate");
resizedCommandList.add("90");
}
if(info.resized){
resizedCommandList.add("-resize");
// 用指定像素宽x长 转换后的图片会变模糊 改用百分比(缩小至多少)
// resizedCommandList.add(info.newWidth + "x" + info.newHeight + "!");
resizedCommandList.add(info.percentageString);
}
resizedCommandList.add("-filter");
resizedCommandList.add("Lanczos");
resizedCommandList.add("-density");
resizedCommandList.add("300");
resizedCommandList.add(path);
String[] rotateResizeCommand = resizedCommandList.toArray(new String[resizedCommandList.size()]);
Process rotateResizeProcess = Runtime.getRuntime().exec(rotateResizeCommand);
rotateResizeProcess.waitFor();
if (rotateResizeProcess.exitValue() != 0) {
log.info("旋转、缩放图片异常:ImageMagick command "+ Arrays.toString(rotateResizeCommand) +" failed.");
throw new BusinessSilentException("创建空白A4PDF异常。");
}
}catch (Exception e){
log.error("旋转、缩放图片异常:{}",e.getMessage(),e);
throw new BusinessSilentException(String.format("旋转、缩放图片异常:%s",e.getMessage()));
}
}
public static void blankPdfProcess(String path){
try{
List<String> blankPdfCommandList = new ArrayList<>();
blankPdfCommandList.add("magick");
blankPdfCommandList.add("-size");
blankPdfCommandList.add(widthA4+"x"+heightA4);
blankPdfCommandList.add("xc:white");
blankPdfCommandList.add(path);
String[] createBlankPdfCommand = blankPdfCommandList.toArray(new String[blankPdfCommandList.size()]);
Process createBlankPdfProcess = Runtime.getRuntime().exec(createBlankPdfCommand);
createBlankPdfProcess.waitFor();
if (createBlankPdfProcess.exitValue() != 0) {
log.info("创建空白A4PDF异常:ImageMagick command "+ Arrays.toString(createBlankPdfCommand) +" failed.");
throw new BusinessSilentException("创建空白A4PDF异常。");
}
}catch (Exception e){
log.error("创建空白A4PDF异常:{}",e.getMessage(),e);
throw new BusinessSilentException(String.format("创建空白A4PDF异常:%s",e.getMessage()));
}
}
public static void pdfProcess(ImageProcessingInfo info,String resizedRotatedPath,String blankPdfPath,String path){
try{
// 1、计算图片在 A4 纸上的居中偏移量
int xOffset = ((int)widthA4 - info.newWidth) / 2;
int yOffset = ((int)heightA4 - info.newHeight) / 2;
// int yOffset = 0;
log.info("偏移量:X:{};Y:{}",xOffset,yOffset);
// 2、图片转pdf
List<String> pdfCommandList = new ArrayList<>();
pdfCommandList.add("magick");
pdfCommandList.add(blankPdfPath);
pdfCommandList.add(resizedRotatedPath);
pdfCommandList.add("-geometry");
pdfCommandList.add("+" + xOffset + "+" + yOffset);
pdfCommandList.add("-density");
pdfCommandList.add("300");
pdfCommandList.add("-composite");
pdfCommandList.add(path);
String[] compositeCommand = pdfCommandList.toArray(new String[pdfCommandList.size()]);
Process compositeProcess = Runtime.getRuntime().exec(compositeCommand);
compositeProcess.waitFor();
if (compositeProcess.exitValue() != 0) {
log.info("图片转PDF异常:ImageMagick command "+ Arrays.toString(compositeCommand) +" failed.");
throw new BusinessSilentException("图片转PDF异常。");
}else{
log.info("图片转PDF:ImageMagick command "+Arrays.toString(compositeCommand)+" success.");
}
}catch (Exception e){
log.error("图片转PDF:{}",e.getMessage(),e);
throw new BusinessSilentException(String.format("图片转PDF:%s",e.getMessage()));
}
}
}
老铁们,觉得文章还可以的话,麻烦一键三连吧(点赞、评论、收藏),感谢各位!!!