在app的后台中,有时候为了标示版权,需要给图片加上水印。

在liunx中,IM4JAVA+GraphicsMagick是个高效处理图片的方案,图片的裁剪是使用了这个技术方案,为了减少不必要的开发成本和运维成本,对应水印,我们是打算继续采用这个方案。

但在开发的过程中,发现这个方案对中文水印支持得不好。

根据网上的搜索结果,就算采用了im4java的GMOperation,并将水印的字符串转成GBK的编码,添加中文水印时,对于奇数个数的中文,没问题;但对于偶数个数的中文,就出现乱码了。

试了多次后,发现这个问题没法解决,于是试了JMagick+ ImageMagick。

但是,在网上找到一份资料,http://javantsky.iteye.com/blog/747807, 里面提到JMagick+ImageMagick作为应用服务的缺点,并建议可以使用IM4JAVA:

The "JNI hazard" here is that if something you use (f.ex libtiff for reading
TIFF files) has a memory bug then it can make your whole JVM crash. Thats of
course frustrating and therefore its great to have im4java around, which
starts IM as an external process, so JVM crashes are avoided.
* *
Coolest thing would be if JMagick and im4java could have the same API so it
was easy to switch depending on luckyness. Ive asked the author of im4java
to attemt to be as compatible as possible, but as im4java is very much
different internally its limited how much can be done in that direction. If you don't like the risk, stick to im4java. If your want optimal
performance give JMagick a try. And, its not JMagick that is buggy, its what it depends on (hereunder IM)
that is not always (memory) bug free on long running processes.
I also have never seen a mismatch between JMagick binary and ImageMagick
binaries leading to crashes.

最后,采用了以下的方案视线图片的中文水印:

1.先把中文生成背景透明的png图片

2.把这些png图片作为图片水印贴在图上

代码如下:

package test;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Transparency;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException; import javax.imageio.ImageIO; import org.im4java.core.CompositeCmd;
import org.im4java.core.ConvertCmd;
import org.im4java.core.GMOperation;
import org.im4java.core.GraphicsMagickCmd;
import org.im4java.core.IM4JavaException;
import org.im4java.core.IMOperation; public class Watermark { /**
* 按九宫格位置添加水印
* @param srcPath 原图片路径
* @param distPath 新图片路径
* @param watermarkImg 水印图片路径
* @param position 九宫格位置[1-9],从上往下,从左到右排序
* @param x 横向边距
* @param y 纵向边距
* @param alpha 透明度
* @throws IOException
* @throws InterruptedException
* @throws IM4JavaException
*/
public void WatermarkImg(String srcPath,String distPath,String watermarkImg, int position, int x, int y, int alpha) throws IOException, InterruptedException, IM4JavaException{
int[] watermarkImgSide = getImageSide(watermarkImg);
int[] srcImgSide = getImageSide(srcPath);
int[] xy = getXY(srcImgSide, watermarkImgSide, position, y, x);
watermarkImg(srcPath,distPath,watermarkImg,watermarkImgSide[0],watermarkImgSide[1],xy[0],xy[1],alpha);
} private int[] getImageSide(String imgPath) throws IOException {
int[] side = new int[2];
Image img = ImageIO.read(new File(imgPath));
side[0] = img.getWidth(null);
side[1] =img.getHeight(null);
return side;
} /**
* 添加图片水印
* @param srcPath 原图片路径
* @param distPath 新图片路径
* @param watermarkImg 水印图片路径
* @param width 水印宽度(可以于水印图片大小不同)
* @param height 水印高度(可以于水印图片大小不同)
* @param x 水印开始X坐标
* @param y 水印开始Y坐标
* @param alpha 透明度[0-100]
* @throws IOException
* @throws InterruptedException
* @throws IM4JavaException
*/
private synchronized void watermarkImg(String srcPath,String distPath,String watermarkImg, int width, int height, int x, int y, int alpha) throws IOException, InterruptedException, IM4JavaException{
CompositeCmd cmd = new CompositeCmd(true);
String path = "C://Program Files//GraphicsMagick-1.3.19-Q8";
cmd.setSearchPath(path);
IMOperation op = new IMOperation();
op.dissolve(alpha);
op.geometry(width, height, x, y);
op.addImage(watermarkImg);
op.addImage(srcPath);
op.addImage(distPath);
cmd.run(op);
} private int[] getXY(int[] image, int[] watermark, int position, int x, int y) {
int[] xy = new int[2];
if(position==1){
xy[0] = x;
xy[1] = y;
}else if(position==2){
xy[0] = (image[0]-watermark[0])/2; //横向边距
xy[1] = y; //纵向边距
}else if(position==3){
xy[0] = image[0]-watermark[0]-x;
xy[1] = y;
}else if(position==4){
xy[0] = x;
xy[1] = (image[1]-watermark[1])/2;
}else if(position==5){
xy[0] = (image[0]-watermark[0])/2;
xy[1] = (image[1]-watermark[1])/2;
}else if(position==6){
xy[0] = image[0]-watermark[0]-x;
xy[1] = (image[1] - watermark[1])/2;
}else if(position==7){
xy[0] = x;
xy[1] = image[1] - watermark[1] - y;
}else if(position==8){
xy[0] = (image[0]-watermark[0])/2;
xy[1] = image[1] - watermark[1] - y;
}else{
xy[0] = image[0]-watermark[0]-x;
xy[1] = image[1] - watermark[1] - y;
}
return xy;
} /**
* 把文字转化为一张背景透明的png图片
* @param str 文字的内容
* @param fontType 字体,例如宋体
* @param fontSize 字体大小
* @param colorStr 字体颜色,不带#号,例如"990033"
* @param outfile png图片的路径
* @throws Exception
*/
public void converFontToImage(String str,String fontType,int fontSize,String colorStr, String outfile) throws Exception{ Font font=new Font(fontType,Font.BOLD,fontSize);
File file=new File(outfile);
//获取font的样式应用在str上的整个矩形
Rectangle2D r=font.getStringBounds(str, new FontRenderContext(AffineTransform.getScaleInstance(1, 1),false,false));
int unitHeight=(int)Math.floor(r.getHeight());//获取单个字符的高度
//获取整个str用了font样式的宽度这里用四舍五入后+1保证宽度绝对能容纳这个字符串作为图片的宽度
int width=(int)Math.round(r.getWidth())+1;
int height=unitHeight+3;//把单个字符的高度+3保证高度绝对能容纳字符串作为图片的高度
//创建图片 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
g2d.setColor(Color.WHITE);
g2d.setStroke(new BasicStroke(1));
g2d.setColor(new Color(Integer.parseInt(colorStr, 16)));//在换成所需要的字体颜色
g2d.setFont(font);
g2d.drawString(str, 0,font.getSize()); ImageIO.write(image, "png", file);//输出png图片
} }

测试实例:

package test;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File; import javax.imageio.ImageIO; import org.im4java.core.CompositeCmd;
import org.im4java.core.ConvertCmd;
import org.im4java.core.GMOperation;
import org.im4java.core.IMOperation; public class test { public static void main(String[] args) { try { String src="D://src.jpg"; //需要加水印的源图片
String desc="D://desc.jpg"; //生成的水印图片的路径
String water="D://water.png"; //用中文转换成的背景透明的png图片
String fontType="C:\\Windows\\Fonts\\simsun.ttc"; //指定字体文件为宋体
String colorStr="990033"; //颜色
int fontSize=18; Watermark watermark=new Watermark(); /*
* 把文字转化为一张背景透明的png图片
* @param str 文字的内容
* @param fontType 字体,例如宋体
* @param fontSize 字体大小
* @param colorStr 字体颜色,不带#号,例如"990033"
* @param outfile png图片的路径
* @throws Exception
*/
watermark.converFontToImage("中华人们", fontType, fontSize, colorStr, water); /*
* 把文字的png图片贴在原图上,生成水印
* @param srcPath 原图片路径
* @param distPath 新图片路径
* @param watermarkImg 水印图片路径
* @param position 九宫格位置[1-9],从上往下,从左到右排序
* @param x 横向边距
* @param y 纵向边距
* @param alpha 透明度
*/
watermark.WatermarkImg(src, desc, water, 1, 20,20, 100); } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }

源码已放在github:https://github.com/newjueqi/ChineseWaterMark#

app后端系列文章总目录

如果您觉得这系列的文章对你有所帮助,欢迎打赏。

支付宝账号:190678908@qq.com 收款人:曾健生

新建了“app后端技术” 交流qq群:254659220

[文章作者]曾健生

[作者邮箱]h6k65@126.com

[作者QQ]190678908

[新浪微博] @newjueqi

[博客]http://blog.csdn.net/newjueqi

版权声明:本文为博主原创文章,未经博主允许不得转载。

app后端设计(13)--IM4JAVA+GraphicsMagick实现中文水印的更多相关文章

  1. app后端设计--总目录 (转)

    特此说明,我转载的!!! app后端设计(1)--api app后端设计(2)--xmpp的使用 app后端设计(3)--短信,邮件,推送服务 app后端设计(4)-- 通讯的安全性 app后端设计( ...

  2. app后端设计--总目录

    做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经历过手机网页端,android客户端,iphone客户端,现就职于app云后端平台bmob(想了解bmob点击这里).其中的乐与苦 ...

  3. [置顶] app后端设计--总目录

    版权声明:本文为博主原创文章,未经博主允许不得转载. 做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经历过手机网页端,Android客户端,iphone客户端,现就职于app云后 ...

  4. app后端设计(php)

    来源:http://blog.csdn.net/column/details/mobilebackend.html?page=1 做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经 ...

  5. app后端设计(12)--图片的处理

    app上线后,不断接受用户的反馈,于是,反馈非常差的情况下,都会有app的改版. 一旦app的改版,都会有比较大的UI改动,一改动UI,那么图片的尺寸也就必须要改变. 在app后端设计(1)—api( ...

  6. app后端设计(0)--总文件夹

    原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...

  7. app后端设计(0)--总目录(转)

    原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...

  8. gm(GraphicsMagick)图片中文水印乱码问题

    1.GraphicsMagick图片中文水印乱码问题处理方式 如出现乱码是由于服务器中缺少中文字库所致,为避免系统中存在多个中文字库冲突, 所以没有必要在安装GraphicsMagick时就将字库文件 ...

  9. app后端设计(2)--xmpp的使用(2014.01.14更新)

    在app中有时候是需要添加聊天服务,在这里谈谈曾经开发聊天服务的经验: (1)聊天服务端选的openfire,这是一个基于xmpp协议的聊天服务器(XMPP是一种基于XML的协议,它继承了在XML环境 ...

随机推荐

  1. 一个清除Xcode项目占用大量空间的脚本

    如果将Xcode项目的Derived Data保留在每个项目的相对路径里的话,久而久之该文件夹里会占用大量的空间,多达百兆计算. 其中DerivedData中很多文件是Xcode项目编译和执行的缓存, ...

  2. JAVA加密技术-----MD5 与SHA 加密

    关于JAVA的加密技术有很多很多,这里只介绍加密技术的两种 MD5与 SHA. MD5与SHA是单向加密算法,也就是说加密后不能解密. MD5 ---信息摘要算法,广泛用于加密与解密技术,常用于文件校 ...

  3. ftp实现普通账号和vip账号限速

    ftp工作流程: ftp回话包含了两个通道,控制通道和数据通道,ftp的工作有两种模式,一种是主动模式,一种是被动模式,以ftpserver为参照物,主动模式,服务器主动连接客户端传输,被动模式,等待 ...

  4. createClass方法

    1.getInitialState 用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取. 2.getDefaultProps() 方法为 props 设置默认值

  5. pop弹簧动画实现

    POP是一个在iOS与OS X上通用的极具扩展性的动画引擎.它在基本的静态动画的基础上增加的弹簧动画与衰减动画,使之能创造出更真实更具物理性的交互动画.POP的API可以快速的与现有的ObjC代码集成 ...

  6. Spring Aop中,获取被代理类的工具

    在实际应用中,顺着过去就是一个类被代理.反过来,可能需要逆向进行,拿到被代理的类,实际工作中碰到了,就拿出来分享下. /** * 获取被代理类的Object * @author Monkey */ p ...

  7. Access Treeview树节点代码一

    Private Sub TreeView0_Updated(Code As Integer)Dim ndeindex As NodeSet ndeindex = TreeView0.Nodes.Add ...

  8. 在MinGW下编译ffmpeg

    因为需要使用ffmpeg的相关库和执行文件,所以需要编译最新的ffmpeg代码.为了能在编译成Windows native执行程序(需要在.net中调用该执行程序),这里我们使用MinGW. 1,安装 ...

  9. java数据库基本操作(sqlserver 2000为例)

    一.环境搭建 1.下载对应数据库连接驱动包并引入. 2.如果在web中调用必须在tomcat中也放入对应的驱动包. 3.在jre的lib\ext中也加入对应的驱动包. 二.连接数据库 public s ...

  10. 利用nginx解决cookie跨域

    一.写在前面 最近需要把阿里云上的四台服务器的项目迁移到客户提供的新的项目中,原来的四台服务器中用到了一级域名和二级域名.比如aaa.abc.com 和bbb.abc.com 和ccc.abc.com ...