文档章节

Java HTML转换为图片

核桃白果
 核桃白果
发布于 2014/06/17 18:04
字数 1687
阅读 289
收藏 1
点赞 0
评论 0

第一次尝试:用awt 包将HTML源码转换为图片

优点:不依赖任何外部JAR包,缺点:对CSS的支持比较差,复杂点的样式就无法展示,且不支持外部引入的CSS和写在style中的CSS,只能写在标签上

Eg:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.UUID;

import javax.swing.JTextPane;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicEditorPaneUI;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class GraphUtils {

    private final static Log log                  = LogFactory.getLog(GraphUtils.class);
    public static int        DEFAULT_IMAGE_WIDTH  = 1000;
    //默认值最好设置大点,因为我们再导之前,不知道这个流有多大,如果过小,则生成的图片后面的为黑色,因为流没有读取完整
    public static int        DEFAULT_IMAGE_HEIGHT = 200;

    public static boolean paintPage(Graphics g, int hPage, int pageIndex, JTextPane panel) {
        Graphics2D g2 = (Graphics2D) g;
        Dimension d = ((BasicEditorPaneUI) panel.getUI()).getPreferredSize(panel);
        double panelHeight = d.height;
        double pageHeight = hPage;
        int totalNumPages = (int) Math.ceil(panelHeight / pageHeight);
        g2.translate(0f, -(pageIndex - 1) * pageHeight);
        panel.paint(g2);
        boolean ret = true;
        if (pageIndex >= totalNumPages) {
            ret = false;
            return ret;
        }
        return ret;
    }

    /**
     * 将BufferedImage转换为图片的信息
     */
    public static String toJpeg(BufferedImage image) {
        // 获取图片文件的在服务器的路径
        String imageName = "G:\\" + UUID.randomUUID().toString() + ".jpg";
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baos);
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
            param.setQuality(1.0f, false);
            encoder.setJPEGEncodeParam(param);
            encoder.encode(image);
            byte[] buff = baos.toByteArray();
            baos.close();
            // 将字节流写入文件保存为图片
            FileUtils.writeByteArrayToFile(new File(imageName), buff);
        } catch (Exception ex) {
            log.error("保存删除图片失败:" + ex.getMessage());
        }
        return imageName;
    }

    /**
     * html转换为jpeg文件
     * @param bgColor 图片的背景色
     * @param html html的文本信息
     * @param width 显示图片的Text容器的宽度
     * @param height 显示图片的Text容器的高度
     * @param eb 設置容器的边框
     * @return
     * @throws Exception
     */
    private static ArrayList<String> html2jpeg(Color bgColor, String html, int width, int height, EmptyBorder eb)
            throws Exception {
        ArrayList<String> ret = new ArrayList<String>();
        try {
            JTextPane tp = new JTextPane();
            tp.setSize(width, height);
            if (eb == null) {
                eb = new EmptyBorder(0, 50, 0, 50);
            }
            if (bgColor != null) {
                tp.setBackground(bgColor);
            }
            if (width <= 0) {
                width = DEFAULT_IMAGE_WIDTH;
            }
            if (height <= 0) {
                height = DEFAULT_IMAGE_HEIGHT;
            }

            tp.setBorder(eb);
            tp.setContentType("text/html");
            tp.setText(html);

            int pageIndex = 1;
            boolean bcontinue = true;
            while (bcontinue) {
                BufferedImage image = new java.awt.image.BufferedImage(width, height,
                        java.awt.image.BufferedImage.TYPE_INT_RGB);
                Graphics g = image.getGraphics();
                g.setClip(0, 0, width, height);
                bcontinue = paintPage(g, height, pageIndex, tp);
                g.dispose();
                String path = toJpeg(image);
                ret.add(path);
                pageIndex++;
            }
        } catch (Exception ex) {
            throw ex;
        }
        return ret;
    }

    /**
     * 将一個html转换为图片
     * @param htmls
     * @return
     * @throws Exception
     */
    public static ArrayList<String> toImages(String html) throws Exception {
        return html2jpeg(Color.white, html, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, new EmptyBorder(0, 0, 0, 0));
    }

TestCase:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath*:spring/application.xml", "classpath*:spring/plugin-*.xml" })
public class GraphUtilsTest extends TestCase {

    @Autowired
    private TaskService            taskService;

    @Autowired
    private VelocitySimpleRelolver velocitySimpleRelolver;

    @Test
    public void testHtml2Image() {
        try {
            String imageTemplateFile = "mail/weekly/mile_schedule_task.vm";
            //邮件内容
            Map<String, Object> velocityData = new HashMap<String, Object>();
            String projId = "6a9fb54e3439453e86e819a4d46fdafb";
            MileSchedule mileSchedule = taskService.getMileSchedule(projId);
            velocityData.put("mileSchedule", mileSchedule);

            String htmlstr = velocitySimpleRelolver.relolve(imageTemplateFile, velocityData);

            Assert.assertNotNull(htmlstr);
            GraphUtils.toImages(htmlstr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Second: 利用html2image组件

优点:方便,代码简单  缺点:CSS支持极差,比不上Java自身的转化

maven引入:

<dependency> 
    <groupId>gui.ava</groupId> 
    <artifactId>html2image</artifactId> 
    <version>0.9</version> 
</dependency>
TestCase:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath*:spring/application.xml", "classpath*:spring/plugin-*.xml" })
public class Html2ImageTest extends TestCase {

    @Autowired
    private TaskService            taskService;

    @Autowired
    private VelocitySimpleRelolver velocitySimpleRelolver;

    @Test
    public void testHtml2Image() {
        try {
            String imageTemplateFile = "mail/weekly/mile_schedule_task.vm";
            //邮件内容
            Map<String, Object> velocityData = new HashMap<String, Object>();
            String projId = "6a9fb54e3439453e86e819a4d46fdafb";
            MileSchedule mileSchedule = taskService.getMileSchedule(projId);
            velocityData.put("mileSchedule", mileSchedule);

            String htmlstr = velocitySimpleRelolver.relolve(imageTemplateFile, velocityData);
            System.out.println(htmlstr);
            Assert.assertNotNull(htmlstr);
            String imageName = "G:/weekly_mile_" + projId + ".png";

            HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
            imageGenerator.setSize(new Dimension(1000, 200));
            imageGenerator.loadHtml(htmlstr);
            imageGenerator.getBufferedImage();
            imageGenerator.saveAsImage(imageName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testImage() {
        HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
        imageGenerator.setSize(new Dimension(1000, 200));
        imageGenerator.loadUrl("http://www.baidu.com");
        imageGenerator.getBufferedImage();
        imageGenerator.saveAsImage("G:/aaa.png");
    }
}
Third:DJNativeSwing

参考:http://blog.csdn.net/cping1982/article/details/5353049

其中:JAR包下载地址:

http://sourceforge.net/projects/djproject/?source=navbar  此地址下down DJNativeSwing.jar 和DJNativeSwing-SWT.jar

http://maven-repository.com/artifact/org.eclipse.swt.org.eclipse.swt.win32.win32.x86_64.4.3.swt/org.eclipse.swt.win32.win32.x86_64/4.3

此地址下down org.eclipse.swt 包,选择适合你的开发环境

注意:swt 包针对不同的环境有不同的要求,windows与Linux下的不同,使用maven引入时采用properties方式,将下载的包放入你的maven库,然后在POM中进行maven引入

eg:

<!-- DJNativeSwing -->
		<dependency> 
		    <groupId>chrriis.dj.nativeswing</groupId> 
		    <artifactId>DJNativeSwing</artifactId> 
		    <version>1.0.2</version> 
		</dependency> 
		<dependency> 
		    <groupId>chrriis.dj.nativeswing.swt</groupId> 
		    <artifactId>DJNativeSwing-SWT</artifactId> 
		    <version>1.0.2</version> 
		</dependency>
		<dependency>
		  <groupId>${swt.groupId}</groupId>
		  <artifactId>${swt.artifactId}</artifactId>
		  <version>4.3</version>
		</dependency>


import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Semaphore;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import chrriis.dj.nativeswing.swtimpl.NativeComponent;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;
import chrriis.dj.nativeswing.swtimpl.components.WebBrowserAdapter;
import chrriis.dj.nativeswing.swtimpl.components.WebBrowserEvent;

public class DJNativeSwingUtils extends JPanel {

    private static final long  serialVersionUID = 8675106494733722832L;
    // 行分隔符  
    final static public String     LS = System.getProperty("line.separator", "/n");
    // 文件分割符  
    final static public String     FS = System.getProperty("file.separator", "//");
    //以javascript脚本获得网页全屏后大小  
    final static StringBuffer      jsDimension;

    static {
        jsDimension = new StringBuffer();
        jsDimension.append("var width = 0;").append(LS);
        jsDimension.append("var height = 0;").append(LS);
        jsDimension.append("if(document.documentElement) {").append(LS);
        jsDimension.append("  width = Math.max(width, document.documentElement.scrollWidth);").append(LS);
        jsDimension.append("  height = Math.max(height, document.documentElement.scrollHeight);").append(LS);
        jsDimension.append("}").append(LS);
        jsDimension.append("if(self.innerWidth) {").append(LS);
        jsDimension.append("  width = Math.max(width, self.innerWidth);").append(LS);
        jsDimension.append("  height = Math.max(height, self.innerHeight);").append(LS);
        jsDimension.append("}").append(LS);
        jsDimension.append("if(document.body.scrollWidth) {").append(LS);
        jsDimension.append("  width = Math.max(width, document.body.scrollWidth);").append(LS);
        jsDimension.append("  height = Math.max(height, document.body.scrollHeight);").append(LS);
        jsDimension.append("}").append(LS);
        jsDimension.append("return width + ':' + height;");
    }


    public static JPanel createContent(String htmlStr, final String fileName, final Semaphore semp) {
        //浏览器面板
        JPanel webBrowserPanel = new JPanel(new BorderLayout());
        //swing的内嵌浏览器
        final JWebBrowser webBrowser = new JWebBrowser();
        //隐藏所有的栏
        webBrowser.setBarsVisible(false);
        //向浏览器写入html内容
        webBrowser.setHTMLContent(htmlStr);
        //将浏览器嵌入浏览器面板
        webBrowserPanel.add(webBrowser, BorderLayout.CENTER);
        //向浏览器增加监听事件
        webBrowser.addWebBrowserListener(new WebBrowserAdapter() {
            // 监听加载进度  
            public void loadingProgressChanged(WebBrowserEvent e) {
                // 当加载完毕时  
                if (e.getWebBrowser().getLoadingProgress() == 100) {
                    try {
                        //执行JS获取图片的宽度,高度
                        String result = (String) webBrowser.executeJavascriptWithResult(jsDimension.toString());
                        int index = result == null ? -1 : result.indexOf(":");

                        NativeComponent nativeComponent = webBrowser.getNativeComponent();
                        //获取图片的原始尺寸
                        Dimension originalSize = nativeComponent.getSize();
                        //根据JS返回结果设定新的图片尺寸
                        Dimension imageSize = new Dimension(Integer.parseInt(result.substring(0, index)), Integer
                                .parseInt(result.substring(index + 1)));
                        //计算图片的新尺寸
                        imageSize.width = Math.max(originalSize.width, imageSize.width);
                        imageSize.height = Math.max(originalSize.height, imageSize.height);
                        nativeComponent.setSize(imageSize);

                        //创建一个不带透明色的BufferedImage对象
                        BufferedImage image = new BufferedImage(imageSize.width, imageSize.height,
                                BufferedImage.TYPE_INT_RGB);
                        //对浏览器中图片进行绘色
                        nativeComponent.paintComponent(image);
                        //截图
                        image = image.getSubimage(0, 0, imageSize.width - 25, imageSize.height);

                        try {
                            // 输出图像  
                            ImageIO.write(image, "jpg", new File(fileName));

                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }

                    } finally {
                        //  退出操作  :释放线程
                        NativeInterface.close();
                        semp.release();
                    }

                }
            }
        });
        return webBrowserPanel;
    }
}
service调用:
private String createImage(final String htmlStr, final Semaphore semaphore) {
        
        final String fileName = FileUtils.getNewFileName("jpg");
        
        NativeInterface.open();
        UIUtils.setPreferredLookAndFeel();
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                    // SWT组件转Swing组件,不初始化父窗体将无法启动webBrowser  
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    // 加载指定页面
                frame.getContentPane().add(DJNativeSwingUtils.createContent(htmlStr, fileName, semaphore),
                        BorderLayout.CENTER);
                    // 仅初始化,但不显示  
                    frame.invalidate();
                    frame.pack();
                    frame.setVisible(false);
            }
        });
        // 在MAC上使用时,因为MAC不支持双进程,仅支持进程内模式,调用此方法进行事件调度。windows和Linux可以忽略此事件调度
        //NativeInterface.runEventPump();
        return new File(fileName).getAbsolutePath();
    }
 private int fillPicture(HSSFSheet sheet, Report report, int startRow) throws IOException {
        if(null == weekly.getMileSchedule()){
            return startRow + 1;
        }
        // 将高度设置为图片的高度
        HSSFRow pictureRow = sheet.getRow(startRow);
        pictureRow.setHeightInPoints(162);
        String htmlStr = this.createHtml(report);
        String imagePath = null;
        final Semaphore semp = new Semaphore(1);//设置信号量
       try {
            semp.acquire();
            // 子线程生成图片
            imagePath = this.createImage(htmlStr, semp);
            // 等待子线程释放信号量
            semp.acquire();
        } catch (InterruptedException e) {
            LOG.error("", e);
        } finally {
            semp.release();
        }

        try {
            ExcelExportUtils.insertPicture(sheet, startRow, (short) 0, (short) 15, imagePath);
        } catch (Exception e) {
            e.printStackTrace();
       } finally {
            // delete 文件
            new File(imagePath).delete();
        }
        return ++startRow;
    }

windows 下截图正常,Linux下异常,Linux下必须安装Mollizia内核的浏览器,在服务器上安装了一个Firefox后,也必须安装图形界面,刚开始采用安装虚拟x-server,但是在CentOS 下安装各种依赖,尝试安装图形界面后tomcat也必须在图形界面下启动才能正常截图,后废弃此种方式,采用java原生绘图方式,下一篇博文介绍




© 著作权归作者所有

共有 人打赏支持
核桃白果
粉丝 7
博文 49
码字总数 30643
作品 0
成都
程序员
那些提升开发人员工作效率的在线工具

点击上方“程序员小灰”,选择“置顶公众号” 有趣有内涵的文章第一时间送达! 本文转载自公众号 Hollis 作为一个Java开发人员,经常要和各种各样的工具打交道,除了我们常用的IDE工具以外,...

bjweimengshu ⋅ 04/18 ⋅ 0

14、Java并发性和多线程-Java ThreadLocal

以下内容转自http://ifeve.com/java-theadlocal/: Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有...

easonjim ⋅ 2017/06/16 ⋅ 0

Java编程语言:Java的类型转换与多态

对于Java语言应该都不陌生,今天我们就将Java中的入门部分概念做一具体的讲解一下。 1.什么叫JVM,JRE,JDK? JRE 全称为JavaRunningEnvironment,就是我们所说的java运行环境,由java虚拟机和一...

启示录是真的 ⋅ 05/22 ⋅ 0

java程序员基础进阶篇,万丈高楼平地起

一.final,finally,finalize 三者区别 Final是一个修饰符: 当final修饰一个变量的时候,变量变成一个常量,它不能被二次赋值 当final修饰的变量为静态变量(即由static修饰)时,必须在声明这...

启示录是真的 ⋅ 05/26 ⋅ 0

Java开发每日复盘2018_0514

今天主要跟大家分享3个部分: 一、「Java的核心机制」 二、「Java命名规范」 三、「Java数据类型相关」 下面我们来一个一个的说: 「Java核心机制」 Java是一种强制面向对象的解释型语言(O...

ZeroOSTalk ⋅ 05/14 ⋅ 0

sharding-jdbc源码分析—准备工作

原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/7831817c1da8 接下来对sharding-jdbc源码的分析基于tag为源码,根据sharding-jdbc Features深入学习sharding-jdbc的几个主要特性...

飞哥-Javaer ⋅ 05/03 ⋅ 0

Common Gui Tools 1.4 发布,Java GUI 实用小工具集

Java GUI实用小工具集 Common Gui Tools 新增:二维码生成解析、时间戳转换、Json格式化、文本文件切分等。 Common Gui Tools 是用java编写,GUI界面的实用小工具集,1.4版有20个小工具: 1,...

bs2004 ⋅ 04/22 ⋅ 0

Java 已老,Kotlin 或将取而代之!

点击上方“CSDN”,选择“置顶公众号” 关键时刻,第一时间送达! Java已经成为历史。它无法发展成现代语言,同时保证向后兼容性。但它为我们带来了最好的JVM生态系统,并引导了许多优秀语言...

CSDN ⋅ 05/12 ⋅ 0

JAVA使用字节流将本地图片传到前端

JAVA使用字节流将本地图片传到前端 01.基本介绍 在我们日常的开发中,会遇到对验证码的使用问题(验证码的作用这里不多多说,避免程序被恶意攻击等),如何是的前端和后端保持一致是一个问题...

meiqi0538 ⋅ 04/09 ⋅ 0

android -------- java虚拟机和Dalvik虚拟机

java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚...

切切歆语 ⋅ 04/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Linux kernel脉络和主干总结

写在前面 前人常说,对Linux操作系统/内核的理解,是计算机行业从业者的内功,决定了你在技术领域想走多远。但内核的庞大以及学习曲线之陡峭,总让我在学习途中觉得犹如“管中窥豹”。 随着工...

Markz0928 ⋅ 29分钟前 ⋅ 0

在gcc中使用intel风格的内联汇编

很简单,内联汇编使用asm(“.intel_syntax noprefix/n”)声明一下,以后的内联汇编就可以用intel风格了,构建可执行文件时给gcc加上-masm=intel参数。 先写一个小程序测试一下: [cpp] view...

simpower ⋅ 40分钟前 ⋅ 0

NIO 之 ByteBuffer实现原理

相关文章 BIO、NIO、AIO 内部原理分析 NIO 之 Selector实现原理 NIO 之 Channel实现原理 前言 Java NIO 主要由下面3部分组成: Buffer Channel Selector 在传统IO中,流是基于字节的方式进行...

轨迹_ ⋅ 49分钟前 ⋅ 0

Jenkins docker权限问题

环境Ubuntu Server 工具 jenkins-war:2.89.2 报错信息 Cannot connect to the Docker daemon. Is the docker daemon running on this host?Build step 'Execute shell' marked build as fai......

Pulsar-V ⋅ 49分钟前 ⋅ 0

180621-一个简单的时间窗口设计与实现

如何设计一个计数的时间窗口 时间窗口,通常对于一些实时信息展示中用得比较多,比如维持一个五分钟的交易明细时间窗口,就需要记录当前时间,到五分钟之前的所有交易明细,而五分钟之前的数...

小灰灰Blog ⋅ 今天 ⋅ 0

Android之Dalvik、ART、JIT、AOT

Android之Dalvik、ART、JIT、AOT 本文内容:Dalvik、ART、JIT、AOT之间关系 本文定位:知识记录 学习过程记录,加深理解,提升文字组合表达能力。也希望能给学习的同学一些灵感 本文整理于[...

lichuangnk ⋅ 今天 ⋅ 0

Thrift RPC实战(五) thrift连接池

Thrift本身没有提供连接池,我们可以用Apache Commons Pool2来实现一个 一、定义对象工厂 BasePooledObjectFactory<T> extends BaseObject implements PooledObjectFactory<T> public class......

lemonLove ⋅ 今天 ⋅ 0

git 命令简写

简写 命令 g git gst git status gd git diff gdc git diff --cached gdv git diff -w "$@" | view - gl git pull gup git pull --rebase gp git push gc git commit -v gc! git commit -v ......

charley158 ⋅ 今天 ⋅ 0

Java中的锁使用与实现

1.Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。 在Lock出现之前,java程序是靠synchronized关键字实现锁功能的,而Java SE5之后,...

ZH-JSON ⋅ 今天 ⋅ 0

Intellij IDEA神器常用技巧四-类和方法注释模板设置

IDEA自带的注释模板不是太好用,我本人到网上搜集了很多资料系统的整理了一下制作了一份比较完整的模板来分享给大家,我不是专业玩博客的,写这篇文章只是为了让大家省事。 这里设置的注释模...

Mkeeper ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部