您的位置:首页 > 编程语言 > Java开发

java图片动态添加水印(利用Graphics2D)

2014-03-10 20:18 471 查看
大家可能都对水印有了解,但是一般的水印都是事先生成的图片,比如



右下角会有一个水印logo,用来标明图片的出处。

但是如果有一个需求是,需要对某个网站的信息进行加密,对于每个信息都需要针对浏览者进行动态水印的添加,也就是浏览的时候才生成,如下图:



这时,我们就需要借用java中的2D画笔,来进行这个过程,代码如下:
public class WordIcon {

private static String CSSCOLOR = "#bf8f79";
private static float ALPHA = 0.4f;

public static ImgStreamBO createMark(WaterMarkBO waterMarkBO) {
BufferedImage bimage = buildGraph2d(waterMarkBO);
ImgStreamBO imgStreamBO = outputPic(bimage);
String imgName = waterMarkBO.getFileUrl().substring(waterMarkBO.getFileUrl().lastIndexOf("/")+1, waterMarkBO.getFileUrl().length());
imgStreamBO.setFileName(imgName+waterMarkBO.getUserId());
return imgStreamBO;
}

//构造2d画笔
private static BufferedImage buildGraph2d(WaterMarkBO waterMarkBO){
Color markContentColor = Color.gray;
URL url;
try {
url = new URL(waterMarkBO.getFileUrl());
} catch (MalformedURLException e) {
LoggerHelper.err(WordIcon.class, "pic url error");
return null;
}
ImageIcon imgIcon = new ImageIcon(url);
Image theImg = imgIcon.getImage();
//Image可以获得 输入图片的信息
int width = theImg.getWidth(null);
int height = theImg.getHeight(null);
//为画出图片的大小
BufferedImage bimage = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
//2d 画笔
Graphics2D g = bimage.createGraphics();
g.setColor(markContentColor);
g.setBackground(Color.white);

//画出图片-----------------------------------
g.drawImage(theImg, 0, 0, null);

//--------对要显示的文字进行处理--------------
AttributedCharacterIterator iter = buildFont(waterMarkBO.getMarkContent(), waterMarkBO.getFontType(),
waterMarkBO.getFontSize());
Color color = Color.decode(CSSCOLOR);
g.setColor(color);
if (null != waterMarkBO.getDegree()) {
// 设置水印旋转
g.rotate(Math.toRadians(waterMarkBO.getDegree()),
(double) bimage.getWidth() / 2, (double) bimage
.getHeight() / 2);
}
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
ALPHA));
for(int i=0;i<9;i++){
for(int j = 0;j<6;j++){
g.drawString(iter, (int) (width - (width*j/4)), (int) (height - (height*i/8)));
}
}
//添加水印的文字和设置水印文字出现的内容 ----位置
g.dispose();//画笔结束
return bimage;
}

//输出文件
private static ImgStreamBO outputPic(BufferedImage bimage) {
ByteArrayOutputStream out = null;
ImgStreamBO imgStreamBO = new ImgStreamBO();
try {
//输出 文件 到指定的路径
out = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimage);
param.setQuality(1, true);
encoder.encode(bimage, param);
byte[] buff = out.toByteArray();
imgStreamBO.setInput(new ByteArrayInputStream(buff));
imgStreamBO.setLength((long)buff.length);
return imgStreamBO;
} catch (Exception e) {
LoggerHelper.err(WordIcon.class, "get inputStream error");
return null;
}finally{
if(out!=null){
try {
out.close();
} catch (IOException e) {
LoggerHelper.err(WordIcon.class, "close outputStream error");
}
}
}
}

//形成字体属性
private static AttributedCharacterIterator buildFont(String markContent,
String fontType, int fontSize) {
AttributedString ats = new AttributedString(markContent);
Font f = new Font(fontType, Font.PLAIN, fontSize);
ats.addAttribute(TextAttribute.FONT, f, 0, markContent.length());
AttributedCharacterIterator iter = ats.getIterator();
return iter;
}
}


这个类传入的参数为一个封装好的BO,代码为:

public class WaterMarkBO {

private String fileUrl;
private String markContent;
private String fontType;
private int fontSize;
private Integer degree;
private Long userId;

public WaterMarkBO(){

}

public WaterMarkBO(String fileUrl, String markContent, String fontType, int fontSize, Integer degree,Long userId){
this.fileUrl = fileUrl;
this.markContent = markContent;
this.fontType = fontType;
this.fontSize = fontSize;
this.degree = degree;
this.userId = userId;
}

……省略get,set方法

}
这样的话,就可以动态的给图片添加水印。

如果是要给新闻添加水印的话,我们会选择将文章word转为pdf,然后再后台先将pdf转为图片,这样就可以保证前面的图片来源了。

附上将pdf转为图片的类:

public class PDFchangToImage {
public static List<String> changePdfToImg(String serverPath,String filePath,long articleId) {
List<String> picUrl = new ArrayList<String>();
try {
String path = serverPath
+ File.separatorChar;
File file = new File(filePath);
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
MappedByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
PDFFile pdffile = new PDFFile(buf);
for (int i = 1; i <= pdffile.getNumPages(); i++) {
PDFPage page = pdffile.getPage(i);
Rectangle rect = new Rectangle(0, 0, ((int) page.getBBox().getWidth()), ((int) page.getBBox().getHeight()));
int n = 2;/**图片清晰度(n>0且n<7)【pdf放大参数】*/
Image img = page.getImage(rect.width * n, rect.height * n, rect,  /**放大pdf到n倍,创建图片。*/null, /**null for the ImageObserver  */true, /** fill background with white  */true /** block until drawing is done  */);
BufferedImage tag = new BufferedImage(rect.width * n, rect.height * n, BufferedImage.TYPE_INT_RGB);
tag.getGraphics().drawImage(img, 0, 0, rect.width * n, rect.height * n, null);          /** File imgfile = new File("D:\\work\\mybook\\FilesNew\\img\\" + i + ".jpg");          if(imgfile.exists()){                if(imgfile.createNewFile())                {                    System.out.println("创建图片:"+"D:\\work\\mybook\\FilesNew\\img\\" + i + ".jpg");                } else {                    System.out.println("创建图片失败!");                }            }  */
String singleFilePath = path + articleId + i +".jpg";
FileOutputStream out = new FileOutputStream(singleFilePath); /** 输出到文件流*/
picUrl.add(singleFilePath);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param2 = encoder.getDefaultJPEGEncodeParam(tag);
param2.setQuality(1f, true);/**1f~0.01f是提高生成的图片质量  */encoder.setJPEGEncodeParam(param2);
encoder.encode(tag);/** JPEG编码  */out.close();
}
channel.close();
raf.close();
unmap(buf);/**如果要在转图片之后删除pdf,就必须要这个关闭流和清空缓冲的方法*/
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return picUrl;
}

private static void unmap(final Object buffer) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
getCleanerMethod.setAccessible(true);
sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod.invoke(buffer, new Object[0]);
cleaner.clean();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
}
}


如果觉得前面进行浏览时,速度跟不上,可以使用多线程和futuretask来帮你快速的完成图片处理。我试过多线程处理响应速度一般也就是1秒多一点点,基本处于可以接受的状态,而且如果觉得这样还不行,你可以考虑使用缓存文件系统,将生成的图片存储起来,下次如果发现有就不用再生成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: