将pdf转换为jpg

近期遇到一个需求:
公司的电子税票是以pdf文件格式保存的,这也是统一的标准格式。但是为了方便在手机App上查看,需要将税票转换为jpg格式。

解决:
拿到这个题目后,我第一反应就是去找一个Java的第三方库来实现转换。
尝试了不少的第三方库,比如pdfbox,ImageMagick等,但是效果都不理想。要么是转换出来的中文丢失,要么是表格数据排版混乱,有的甚至连图片都丢失。网上说jpedal效果不错,不过由于是商业版的无法尝试。
Java的搜索了一圈后发现都不行。想想要不看看C#有没有好的库呢。然后就搜索到了今天的解决方案所用的Adobe Acorbat的Acrobat.dll 来进行转换的办法。
Java无法直接使用dll,于是还用了jacob来调用dll 来实现。
不再罗嗦,直接上代码。
jacob项目地址 https://sourceforge.net/projects/jacob-project/

以下代码Pdf2Jpg2参考自 http://blog.csdn.net/love_5209/article/details/19162185

我主要做的修改是:将原来程序的每页pdf转换为一个jpg文件。修改为整个pdf所有页面拼接为一个jpg文件。这样方便存储。
主要思路是:先用一个循环获取到pdf文件的宽度w和高度h。然后设置一个宽度w,高度h的BufferedImage对象。然后在用一个循环读取每页的pdf内容,再粘贴到BufferedImage上,循环中适当调整粘贴的坐标。最后在循环外将BufferedImage对象输出到jpg文件,就完成了。

package com.invoicetopdf;
import java.awt.Graphics;  
import java.awt.Image;  
import java.awt.Toolkit;  
import java.awt.datatransfer.Clipboard;  
import java.awt.datatransfer.DataFlavor;  
import java.awt.datatransfer.Transferable;  
import java.awt.image.BufferedImage;  
import java.io.File;  
import java.io.FileOutputStream;  
import java.io.IOException;  
  
import java.util.Stack;

import javax.imageio.ImageIO;  
  

import com.jacob.activeX.ActiveXComponent;  
import com.jacob.com.ComThread;  
import com.jacob.com.Dispatch;  
import com.jacob.com.Variant;  
  
public class Pdf2Jpg2 {  
    /** 
     *  
     * @param filepath 
     *            pdf路径 
     * @param savePath 
     *            img保存路径 
     * @param times 
     *            缩放比例 如:1f为原图比例 
     * @throws IOException  
     */  
    public static void savaPageAsJpgByAcrobat(String filepath, String savePath,  
            float times) throws IOException {  
        ComThread.InitSTA();//初始化com的线程   
        // 输出  
        FileOutputStream out = null;  
        // PDF页数  
        int pageNum = 0;  
        // PDF宽、高  
        int x, y = 0;  
        // PDF控制对象  
        Dispatch pdfObject = null;  
        // PDF坐标对象  
        Dispatch pointxy = null;  
        // pdfActiveX PDDoc对象 主要建立PDF对象  
        ActiveXComponent app = new ActiveXComponent("AcroExch.PDDoc");  
        // pdfActiveX PDF的坐标对象  
        ActiveXComponent point = new ActiveXComponent("AcroExch.Point");  
        try {  
            // 得到控制对象  
            pdfObject = app.getObject();  
            // 得到坐标对象  
            pointxy = point.getObject();  
            // 打开PDF文件,建立PDF操作的开始  
            Dispatch.call(pdfObject, "Open", new Variant(filepath));  
            // 得到当前打开PDF文件的页数  
            pageNum = Dispatch.call(pdfObject, "GetNumPages").toInt();  
            int allimgHeight = 0;
            int allimgWidth = 0;
            
            for (int i = 0; i < pageNum; i++) {  
                // 根据页码得到单页PDF  
                Dispatch page = Dispatch.call(pdfObject, "AcquirePage",  
                        new Variant(i)).toDispatch();  
                // 得到PDF单页大小的Point对象  
                Dispatch pagePoint = Dispatch.call(page, "GetSize")  
                        .toDispatch(); 
                if (allimgWidth < (int) (Dispatch.get(pagePoint, "x").toInt() * 2 * times)){  
                    allimgWidth = (int) (Dispatch.get(pagePoint, "x").toInt() * 2 * times);
                }
                allimgHeight += (int) (Dispatch.get(pagePoint, "y").toInt() * 2 * times);
            }
            
            BufferedImage tag = new BufferedImage(allimgWidth, allimgHeight, 8);  
            Graphics graphics = tag.getGraphics();  
            
            Stack<Integer> pageHight = new Stack<Integer>();
            
            int tmpHight = 0;
            for (int i = 0; i < pageNum; i++) {  
                // 根据页码得到单页PDF  
                Dispatch page = Dispatch.call(pdfObject, "AcquirePage",  
                        new Variant(i)).toDispatch();  
                // 得到PDF单页大小的Point对象  
                Dispatch pagePoint = Dispatch.call(page, "GetSize")  
                        .toDispatch();  
                // 创建PDF位置对象,为拷贝图片到剪贴板做准备  
                ActiveXComponent pdfRect = new ActiveXComponent("AcroExch.Rect");  
                // 得到单页PDF的宽  
                //int imgWidth = (int) (Dispatch.get(pagePoint, "x").toInt() * 2 * times);  
                //使用最宽的页面作为统一宽度。
                int imgWidth = allimgWidth; 
                // 得到单页PDF的高  
                int imgHeight = (int) (Dispatch.get(pagePoint, "y").toInt() * 2 * times);  
                
                // 控制PDF位置对象  
                Dispatch pdfRectDoc = pdfRect.getObject();  
                // 设置PDF位置对象的值  
                Dispatch.put(pdfRectDoc, "Left", new Integer(0));  
                Dispatch.put(pdfRectDoc, "Right", new Integer(imgWidth));  
                Dispatch.put(pdfRectDoc, "Top", new Integer(0));  
                Dispatch.put(pdfRectDoc, "Bottom", new Integer(imgHeight));  
                // 将设置好位置的PDF拷贝到Windows剪切板,参数:位置对象,宽起点,高起点,分辨率  
                Dispatch.call(page, "CopyToClipboard", new Object[] {  
                        pdfRectDoc, 0, 0, 200 * times });  
                Image image = getImageFromClipboard();  
                
                if (i==0){
                    graphics.drawImage(image, 0, 0, null); 
                }
                else {
                    graphics.drawImage(image, 0, tmpHight, null);
                }
                
                tmpHight += imgHeight;
                //graphics.dispose();  
                // 输出图片  
                //ImageIO.write(tag, "JPEG", new File(savePath + (i+1) + ".jpg"));  
            }  
            graphics.dispose(); 
            ImageIO.write(tag, "JPEG", new File(savePath +  "all.jpg"));  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            // 关闭PDF  
            app.invoke("Close", new Variant[] {});  
            ComThread.Release();//关闭com的线程   真正kill进程  
        }  
  
    }  
  
    public static Image getImageFromClipboard() throws Exception {  
        Clipboard sysc = Toolkit.getDefaultToolkit().getSystemClipboard();  
        Transferable cc = sysc.getContents(null);  
        if (cc == null)  
            return null;  
        else if (cc.isDataFlavorSupported(DataFlavor.imageFlavor))  
            return (Image) cc.getTransferData(DataFlavor.imageFlavor);  
        return null;  
    }  
  
    public static void main(String[] args) throws IOException {
        //System.setProperty("java.library.path","d:/test/");
        savaPageAsJpgByAcrobat("d:/test/11.pdf", 
                "d:/test/", 1f); 
    }
}  

我在得到jpg文件后,还将jpg文件用二进制的形式回写到了Oracle数据库。具体代码如下:

package com.invoicetopdf;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

public class AutoInvoiceToPDF  {

    // base64位编码转成PDF
    public static void base64StringToPdf(String base64Content, String filePath)
            throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;

        try {
            byte[] bytes = decoder.decodeBuffer(base64Content);// base64编码内容转换为字节数组
            ByteArrayInputStream byteInputStream = new ByteArrayInputStream(
                    bytes);
            bis = new BufferedInputStream(byteInputStream);
            File file = new File(filePath);
            File path = file.getParentFile();
            if (!path.exists()) {
                path.mkdirs();
            }
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);

            byte[] buffer = new byte[1024];
            int length = bis.read(buffer);
            while (length != -1) {
                bos.write(buffer, 0, length);
                length = bis.read(buffer);
            }
            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // closeStream(bis, fos, bos);
            bis.close();
            fos.close();
            bos.close();
            // System.out.println("生成成功");
        }
    }

    // pdf转BASE64位编码
    public static String PDFToBase64(File file) {
        BASE64Encoder encoder = new BASE64Encoder();
        FileInputStream fin = null;
        BufferedInputStream bin = null;
        ByteArrayOutputStream baos = null;
        BufferedOutputStream bout = null;
        try {
            fin = new FileInputStream(file);
            bin = new BufferedInputStream(fin);
            baos = new ByteArrayOutputStream();
            bout = new BufferedOutputStream(baos);
            byte[] buffer = new byte[1024];
            int len = bin.read(buffer);
            while (len != -1) {
                bout.write(buffer, 0, len);
                len = bin.read(buffer);
            }
            // 刷新此输出流并强制写出所有缓冲的输出字节
            bout.flush();
            byte[] bytes = baos.toByteArray();
            return encoder.encodeBuffer(bytes).trim();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fin.close();
                bin.close();
                bout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    // GZIP压缩
    public static String gzip(String primStr) {
        if (primStr == null || primStr.length() == 0) {
            return primStr;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        GZIPOutputStream gzip = null;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(primStr.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (gzip != null) {
                try {
                    gzip.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return new sun.misc.BASE64Encoder().encode(out.toByteArray());
    }

    // GZIP解压
    public static String gunzip(String compressedStr) {
        if (compressedStr == null) {
            return null;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = null;
        GZIPInputStream ginzip = null;
        byte[] compressed = null;
        String decompressed = null;
        try {
            compressed = new sun.misc.BASE64Decoder()
                    .decodeBuffer(compressedStr);
            in = new ByteArrayInputStream(compressed);
            ginzip = new GZIPInputStream(in);

            byte[] buffer = new byte[1024];
            int offset = -1;
            while ((offset = ginzip.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            decompressed = out.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ginzip != null) {
                try {
                    ginzip.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                }
            }
        }

        return decompressed;
    }

    public static void ReadImgFromDB(Connection conn, int fphm) {
        // 从数据库中读取图片。
        String sql = "select jpgbm from pdfinfo where fphm = '39410355'";
        Statement stmt;
        FileOutputStream fout;
        ResultSet rs;
        try {
            stmt = conn.createStatement();

            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                Blob blob = rs.getBlob(1);
                byte barr[] = blob.getBytes(1, (int) blob.length());
                // System.out.println("blob length:" +blob.length());

                fout = new FileOutputStream("d:/dbjpg.jpg");
                fout.write(barr);
                fout.flush();
                fout.close();
            }
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }

    }

    public static void WirteImgToDB(Connection conn, int fphm) {
        // 把图片更新回数据库
        PreparedStatement ps;
        try {
            ps = conn
                    .prepareStatement("update pdfinfo  set  jpgbm = ? where  fphm = ? ");
            FileInputStream fin;
            fin = new FileInputStream("d:\\test\\all.jpg");
            // System.out.println("file size:" + fin.available());
            ps.setBinaryStream(1, fin, fin.available());
            ps.setInt(2, fphm);
            int i = ps.executeUpdate();
            ps.close();
            // System.out.println(i + " records affected");
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void AllInOne(Connection conn, int fphm) {

        String pdfbm = "";
        try {
            Statement stmt = conn.createStatement();
            String sql = "select pdfbm from pdfinfo where fphm = '" + fphm
                    + "'";
            ResultSet rs = stmt.executeQuery(sql);
            while (rs.next()) {
                pdfbm = rs.getString("pdfbm");
            }
            rs.close();
            stmt.close();
            pdfbm = gunzip(pdfbm);
            // 生成pdf
            base64StringToPdf(pdfbm, "d:\\test\\11.pdf");

            Pdf2Jpg2.savaPageAsJpgByAcrobat("d:\\test\\11.pdf", "d:\\test\\",
                    0.9f);
            WirteImgToDB(conn, fphm);
            System.out.println("fphm:" + fphm + " done!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String url = "jdbc:oracle:thin:@172.xx.x.xx:1521:xxxx";
        String user = "xxxxx";
        String password = "xxxxx";
        Connection conn = null;
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        int fphm = 0;
        String sqlString = " select fphm from pdfinfo where jpgbm is null and rownum<5000 order by indate desc ";
        Statement stmt;
        try {
            stmt = conn.createStatement();

            ResultSet rs = stmt.executeQuery(sqlString);
            while (rs.next()) {
                fphm = rs.getInt("fphm");
                AllInOne(conn, fphm);
            }
            rs.close();
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
    }

}

这个文件包含很多方法:
将数据库中base64编码的pdf编码读出保存为pdf文件 base64StringToPdf
将图片写入Oracle数据库 WirteImgToDB
从Oracle读出图片并保存为文件 ReadImgFromDB
最后通过一个AllInOne将这些方法串联起来。就可以达到读取数据库中的pdf base64编码,然后转换为pdf,再转换为jpg,最后将jpg的二进制写入数据库。
通过将这两个源代码打包为一个jar,再做一个定时任务,就可以自动去寻找没有转换生成jpg的税票,自动生成了。

需要注意的是:使用jacob.jar 需要找对版本,并将对应的平台(x86或者x64)的dll放到对应的java.library.path中。一般复制到java运行环境jre对应的bin目录下即可。
推荐使用jacob 1.18版本。因为1.17版本在Eclispe下面运行没有问题,但是在命令行里面运行的时候总是提示 NoSuchFieldError: m_pDispatch 一切都是对的找不到原因。最后果断换为1.18版本,就ok了。
jacob 1.18 需要Java7才能支持哦!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容