將pdf轉(zhuǎn)換為jpg

近期遇到一個需求:
公司的電子稅票是以pdf文件格式保存的,這也是統(tǒng)一的標準格式。但是為了方便在手機App上查看,需要將稅票轉(zhuǎn)換為jpg格式。

解決:
拿到這個題目后,我第一反應就是去找一個Java的第三方庫來實現(xiàn)轉(zhuǎn)換。
嘗試了不少的第三方庫,比如pdfbox,ImageMagick等,但是效果都不理想。要么是轉(zhuǎn)換出來的中文丟失,要么是表格數(shù)據(jù)排版混亂,有的甚至連圖片都丟失。網(wǎng)上說jpedal效果不錯,不過由于是商業(yè)版的無法嘗試。
Java的搜索了一圈后發(fā)現(xiàn)都不行。想想要不看看C#有沒有好的庫呢。然后就搜索到了今天的解決方案所用的Adobe Acorbat的Acrobat.dll 來進行轉(zhuǎn)換的辦法。
Java無法直接使用dll,于是還用了jacob來調(diào)用dll 來實現(xiàn)。
不再羅嗦,直接上代碼。
jacob項目地址 https://sourceforge.net/projects/jacob-project/

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

我主要做的修改是:將原來程序的每頁pdf轉(zhuǎn)換為一個jpg文件。修改為整個pdf所有頁面拼接為一個jpg文件。這樣方便存儲。
主要思路是:先用一個循環(huán)獲取到pdf文件的寬度w和高度h。然后設置一個寬度w,高度h的BufferedImage對象。然后在用一個循環(huán)讀取每頁的pdf內(nèi)容,再粘貼到BufferedImage上,循環(huán)中適當調(diào)整粘貼的坐標。最后在循環(huán)外將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頁數(shù)  
        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文件的頁數(shù)  
            pageNum = Dispatch.call(pdfObject, "GetNumPages").toInt();  
            int allimgHeight = 0;
            int allimgWidth = 0;
            
            for (int i = 0; i < pageNum; i++) {  
                // 根據(jù)頁碼得到單頁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++) {  
                // 根據(jù)頁碼得到單頁PDF  
                Dispatch page = Dispatch.call(pdfObject, "AcquirePage",  
                        new Variant(i)).toDispatch();  
                // 得到PDF單頁大小的Point對象  
                Dispatch pagePoint = Dispatch.call(page, "GetSize")  
                        .toDispatch();  
                // 創(chuàng)建PDF位置對象,為拷貝圖片到剪貼板做準備  
                ActiveXComponent pdfRect = new ActiveXComponent("AcroExch.Rect");  
                // 得到單頁PDF的寬  
                //int imgWidth = (int) (Dispatch.get(pagePoint, "x").toInt() * 2 * times);  
                //使用最寬的頁面作為統(tǒng)一寬度。
                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剪切板,參數(shù):位置對象,寬起點,高起點,分辨率  
                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 {  
            // 關(guān)閉PDF  
            app.invoke("Close", new Variant[] {});  
            ComThread.Release();//關(guān)閉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數(shù)據(jù)庫。具體代碼如下:

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位編碼轉(zhuǎn)成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編碼內(nèi)容轉(zhuǎn)換為字節(jié)數(shù)組
            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轉(zhuǎn)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);
            }
            // 刷新此輸出流并強制寫出所有緩沖的輸出字節(jié)
            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) {
        // 從數(shù)據(jù)庫中讀取圖片。
        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) {
        // 把圖片更新回數(shù)據(jù)庫
        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();
        }
        
    }

}

這個文件包含很多方法:
將數(shù)據(jù)庫中base64編碼的pdf編碼讀出保存為pdf文件 base64StringToPdf
將圖片寫入Oracle數(shù)據(jù)庫 WirteImgToDB
從Oracle讀出圖片并保存為文件 ReadImgFromDB
最后通過一個AllInOne將這些方法串聯(lián)起來。就可以達到讀取數(shù)據(jù)庫中的pdf base64編碼,然后轉(zhuǎn)換為pdf,再轉(zhuǎn)換為jpg,最后將jpg的二進制寫入數(shù)據(jù)庫。
通過將這兩個源代碼打包為一個jar,再做一個定時任務,就可以自動去尋找沒有轉(zhuǎn)換生成jpg的稅票,自動生成了。

需要注意的是:使用jacob.jar 需要找對版本,并將對應的平臺(x86或者x64)的dll放到對應的java.library.path中。一般復制到java運行環(huán)境jre對應的bin目錄下即可。
推薦使用jacob 1.18版本。因為1.17版本在Eclispe下面運行沒有問題,但是在命令行里面運行的時候總是提示 NoSuchFieldError: m_pDispatch 一切都是對的找不到原因。最后果斷換為1.18版本,就ok了。
jacob 1.18 需要Java7才能支持哦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容