将pdf转换为jpg

2017-09-22  本文已影响447人  xiaojieLu

近期遇到一个需求:
公司的电子税票是以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才能支持哦!

上一篇下一篇

猜你喜欢

热点阅读