1. 程式人生 > >Java:Excel轉PDF實現方案;基於POI與Itext進行搭配.

Java:Excel轉PDF實現方案;基於POI與Itext進行搭配.

由於此份文章比較久遠並且發現點選率也蠻高的,為了方便大夥的測試與研究我另外建立

了一份程式碼倉庫在GitHub中,請大夥直接從這個地址拉取:

https://github.com/caryyu/excel2pdf

注意!注意!注意!分隔線以下的程式碼比較久遠了,最新的程式碼以 Github 倉庫為主,評論請轉移至倉庫的 Issue 中進行處理,謝謝!

---------------------------------------分割線----------------------------------------------

說明:

1.最近業務需求涉及到了關於這方面的知識,在網上尋找了很多次都是一些零零碎碎的程式碼,現在歸檔記錄下來以免以後忘記再回來翻閱一下;同時讓有需求的朋友也可以借鑑一下,如果有興趣的朋友可以自己寫一套新方案出來,順便發我一個連結我,這樣共同學習哈。

2.此種方式可以實現多個Excel轉PDF多頁情況,對邊框和一些精細的部分實現的可能不太好,所以有能力的朋友自己可以參考以下程式碼進行適量的修改.

3.原理:使用PDF的Table與Excel表格進行對應,並解析Excel的行、列、單元格樣式與Table的進行匹配.(樣式有些部分實現的不是太好,所有大家有問題多自己解決一下啊)

進入正題,首先下載所需的Jar包:

commons-codec.jar
commons-io-1.1.jar
dom4j-1.6.1.jar
poi-3.9-20121203.jar
poi-ooxml-3.9-20121203.jar
poi-ooxml-schemas-3.9-20121203.jar
stax-api-1.0.1.jar
xmlbeans-2.3.0.jar
itext-asian.jar (亞洲語言支援包)
itextpdf-5.4.4.jar
xmlworker-5.4.4.jar (支援Html轉PDF,可選..)

注:我採用的是 itext5.4.4 與 poi3.9.

(*)效果圖如下:

(*)關聯程式碼如下:

class Excel {

    protected Workbook wb;
    protected Sheet sheet;

    public Excel(InputStream is) {
        try {
            this.wb = WorkbookFactory.create(is);
            this.sheet = wb.getSheetAt(wb.getActiveSheetIndex());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
    }

    public Sheet getSheet() {
        return sheet;
    }
    
    public Workbook getWorkbook(){
        return wb;
    }
}
public class ExcelObject {
    /**
     * 錨名稱
     */
    private String anchorName;
    /**
     * Excel Stream
     */
    private InputStream inputStream;
    /**
     * POI Excel
     */
    private Excel excel;
    
    public ExcelObject(InputStream inputStream){
        this.inputStream = inputStream;
        this.excel = new Excel(this.inputStream);
    }

    public ExcelObject(String anchorName , InputStream inputStream){
        this.anchorName = anchorName;
        this.inputStream = inputStream;
        this.excel = new Excel(this.inputStream);
    }
    public String getAnchorName() {
        return anchorName;
    }
    public void setAnchorName(String anchorName) {
        this.anchorName = anchorName;
    }
    public InputStream getInputStream() {
        return this.inputStream;
    }
    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }
    Excel getExcel() {
        return excel;
    }
}
public class PdfTool {
    //
    protected Document document;
    //
    protected OutputStream os;
    
    public Document getDocument() {
        if (document == null) {
            document = new Document();
        }
        return document;
    }
}
public class Excel2Pdf extends PdfTool{
    //
    protected List<ExcelObject> objects = new ArrayList<ExcelObject>();
    
    /**
     * <p>Description: 匯出單項PDF,不包含目錄</p>
     * @param object
     */
    public Excel2Pdf(ExcelObject object , OutputStream os) {
        this.objects.add(object);
        this.os = os;
    }
    
    /**
     * <p>Description: 匯出多項PDF,包含目錄</p>
     * @param objects
     */
    public Excel2Pdf(List<ExcelObject> objects , OutputStream os) {
        this.objects = objects;
        this.os = os;
    }
    
    /**
     * <p>Description: 轉換呼叫</p>
     * @throws DocumentException
     * @throws MalformedURLException
     * @throws IOException
     */
    public void convert() throws DocumentException, MalformedURLException, IOException {
        getDocument().setPageSize(PageSize.A4.rotate());
        PdfWriter writer = PdfWriter.getInstance(getDocument(), os);
        writer.setPageEvent(new PDFPageEvent());
        //Open document
        getDocument().open();
        //Single one 
        if(this.objects.size() <= 1){
            PdfPTable table = this.toCreatePdfTable(this.objects.get(0) ,  getDocument() , writer);
            getDocument().add(table);
        }
        //Multiple ones 
        if(this.objects.size() > 1){
            toCreateContentIndexes(writer , this.getDocument() , this.objects);
            //
            for (int i = 0; i < this.objects.size(); i++) {
                PdfPTable table = this.toCreatePdfTable(this.objects.get(i) , getDocument() , writer);
                getDocument().add(table);
            }
        }
        //
        getDocument().close();
    }
    
    protected PdfPTable toCreatePdfTable(ExcelObject object , Document document , PdfWriter writer) throws MalformedURLException, IOException, DocumentException{
        PdfPTable table = new PdfTableExcel(object).getTable();
        table.setKeepTogether(true);
//      table.setWidthPercentage(new float[]{100} , writer.getPageSize());
        table.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
        return table;
    }
    
    /**
     * <p>Description: 內容索引建立</p>
     * @throws DocumentException 
     */
    protected void toCreateContentIndexes(PdfWriter writer , Document document , List<ExcelObject> objects) throws DocumentException{
        PdfPTable table = new PdfPTable(1);
        table.setKeepTogether(true);
        table.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
        //
        Font font = new Font(Resource.BASE_FONT_CHINESE , 12 , Font.NORMAL);
        font.setColor(new BaseColor(0,0,255));
        //
        for (int i = 0; i < objects.size(); i++) {
            ExcelObject o = objects.get(i);
            String text = o.getAnchorName();
            Anchor anchor = new Anchor(text , font);
            anchor.setReference("#" + o.getAnchorName());
            //
            PdfPCell cell = new PdfPCell(anchor);
            cell.setBorder(0);
            //
            table.addCell(cell);
        }
        //
        document.add(table);
    }
    
    /**
     * <p>ClassName: PDFPageEvent</p>
     * <p>Description: 事件 -> 頁碼控制</p>
     * <p>Author: Cary</p>
     * <p>Date: Oct 25, 2013</p>
     */
    private static class PDFPageEvent extends PdfPageEventHelper{
        protected PdfTemplate template;
        public BaseFont baseFont;
        
        @Override
        public void onStartPage(PdfWriter writer, Document document) {
            try{
                this.template = writer.getDirectContent().createTemplate(100, 100);
                this.baseFont = new Font(Resource.BASE_FONT_CHINESE , 8, Font.NORMAL).getBaseFont();
            } catch(Exception e) {
                throw new ExceptionConverter(e);
            }
        }
        
        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            //在每頁結束的時候把“第x頁”資訊寫道模版指定位置
            PdfContentByte byteContent = writer.getDirectContent();
            String text = "第" + writer.getPageNumber() + "頁";
            float textWidth = this.baseFont.getWidthPoint(text, 8);
            float realWidth = document.right() - textWidth;
            //
            byteContent.beginText();
            byteContent.setFontAndSize(this.baseFont , 10);
            byteContent.setTextMatrix(realWidth , document.bottom());
            byteContent.showText(text);
            byteContent.endText();
            byteContent.addTemplate(this.template , realWidth , document.bottom());
        }
    }  
}
public class PdfTableExcel {
    //ExcelObject
    protected ExcelObject excelObject;
    //excel
    protected Excel excel;
    //
    protected boolean setting = false;
    
    /**
     * <p>Description: Constructor</p>
     * @param excel
     */
    public PdfTableExcel(ExcelObject excelObject){
        this.excelObject = excelObject;
        this.excel = excelObject.getExcel();
    }
    
    /**
     * <p>Description: 獲取轉換過的Excel內容Table</p>
     * @return PdfPTable
     * @throws BadElementException
     * @throws MalformedURLException
     * @throws IOException
     */
    public PdfPTable getTable() throws BadElementException, MalformedURLException, IOException{
        Sheet sheet = this.excel.getSheet();
        return toParseContent(sheet);
    }
    
    protected PdfPTable toParseContent(Sheet sheet) throws BadElementException, MalformedURLException, IOException{
        int rowlength = sheet.getLastRowNum();
        List<PdfPCell> cells = new ArrayList<PdfPCell>();
        float[] widths = null;
        float mw = 0;
        for (int i = 0; i < rowlength; i++) {
            Row row = sheet.getRow(i);
            float[] cws = new float[row.getLastCellNum()];
            for (int j = 0; j < row.getLastCellNum(); j++) {
                Cell cell = row.getCell(j);
                float cw = getPOIColumnWidth(cell);
                cws[cell.getColumnIndex()] = cw;
                if(isUsed(cell.getColumnIndex(), row.getRowNum())){
                    continue;
                }
                cell.setCellType(Cell.CELL_TYPE_STRING);
                CellRangeAddress range = getColspanRowspanByExcel(row.getRowNum(), cell.getColumnIndex());
                //
                int rowspan = 1;
                int colspan = 1;
                if (range != null) {
                    rowspan = range.getLastRow() - range.getFirstRow() + 1;
                    colspan = range.getLastColumn() - range.getFirstColumn() + 1;
                }
                //PDF單元格
                PdfPCell pdfpCell = new PdfPCell();
                pdfpCell.setBackgroundColor(new BaseColor(getBackgroundColorByExcel(cell.getCellStyle())));
                pdfpCell.setColspan(colspan);
                pdfpCell.setRowspan(rowspan);
                pdfpCell.setVerticalAlignment(getVAlignByExcel(cell.getCellStyle().getVerticalAlignment()));
                pdfpCell.setHorizontalAlignment(getHAlignByExcel(cell.getCellStyle().getAlignment()));
                pdfpCell.setPhrase(getPhrase(cell));
                pdfpCell.setFixedHeight(this.getPixelHeight(row.getHeightInPoints()));
                addBorderByExcel(pdfpCell, cell.getCellStyle());
                addImageByPOICell(pdfpCell , cell , cw);
                //
                cells.add(pdfpCell);
                j += colspan - 1;
            }
            float rw = 0;
            for (int j = 0; j < cws.length; j++) {
                rw += cws[j];
            }
            if (rw > mw ||  mw == 0) {
                widths = cws;
                mw = rw;
            }
        }
        //
        PdfPTable table = new PdfPTable(widths);
        table.setWidthPercentage(100);
//        table.setLockedWidth(true);
        for (PdfPCell pdfpCell : cells) {
            table.addCell(pdfpCell);
        }
        return table;
    }
    
    protected Phrase getPhrase(Cell cell){
        if(this.setting || this.excelObject.getAnchorName() == null){
           return new Phrase(cell.getStringCellValue(), getFontByExcel(cell.getCellStyle()));
        }
        Anchor anchor = new Anchor(cell.getStringCellValue() , getFontByExcel(cell.getCellStyle()));
        anchor.setName(this.excelObject.getAnchorName());
        this.setting = true;
        return anchor;
    }
    
    protected void addImageByPOICell(PdfPCell pdfpCell , Cell cell , float cellWidth) throws BadElementException, MalformedURLException, IOException{
       POIImage poiImage = new POIImage().getCellImage(cell);
       byte[] bytes = poiImage.getBytes();
       if(bytes != null){
//           double cw = cellWidth;
//           double ch = pdfpCell.getFixedHeight();
//           
//           double iw = poiImage.getDimension().getWidth();
//           double ih = poiImage.getDimension().getHeight();
//           
//           double scale = cw / ch;
//           
//           double nw = iw * scale;
//           double nh = ih - (iw - nw);
//           
//           POIUtil.scale(bytes , nw  , nh);
           pdfpCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
           pdfpCell.setHorizontalAlignment(Element.ALIGN_CENTER);
           Image image = Image.getInstance(bytes);
           pdfpCell.setImage(image);
       }
    }
    
    protected float getPixelHeight(float poiHeight){
        float pixel = poiHeight / 28.6f * 26f;
        return pixel;
    }
    
    /**
     * <p>Description: 此處獲取Excel的列寬畫素(無法精確實現,期待有能力的朋友進行改善此處)</p>
     * @param cell 
     * @return 畫素寬
     */
    protected int getPOIColumnWidth(Cell cell) {
        int poiCWidth = excel.getSheet().getColumnWidth(cell.getColumnIndex());
        int colWidthpoi = poiCWidth;
        int widthPixel = 0;
        if (colWidthpoi >= 416) {
            widthPixel = (int) (((colWidthpoi - 416.0) / 256.0) * 8.0 + 13.0 + 0.5);
        } else {
            widthPixel = (int) (colWidthpoi / 416.0 * 13.0 + 0.5);
        }
        return widthPixel;
    }
    
    protected CellRangeAddress getColspanRowspanByExcel(int rowIndex, int colIndex) {
        CellRangeAddress result = null;
        Sheet sheet = excel.getSheet();
        int num = sheet.getNumMergedRegions();
        for (int i = 0; i < num; i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            if (range.getFirstColumn() == colIndex && range.getFirstRow() == rowIndex) {
                result = range;
            }
        }
        return result;
    }
    
    protected boolean isUsed(int colIndex , int rowIndex){
        boolean result = false;
        Sheet sheet = excel.getSheet();
        int num = sheet.getNumMergedRegions();
        for (int i = 0; i < num; i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            int firstRow = range.getFirstRow();
            int lastRow = range.getLastRow();
            int firstColumn = range.getFirstColumn();
            int lastColumn = range.getLastColumn();
            if (firstRow < rowIndex && lastRow >= rowIndex) {
                if(firstColumn <= colIndex && lastColumn >= colIndex){
                    result = true;
                }
            }
        }
        return result;
    }

    protected Font getFontByExcel(CellStyle style) {
       Font result = new Font(Resource.BASE_FONT_CHINESE , 8 , Font.NORMAL);
       Workbook wb = excel.getWorkbook();
       //字型樣式索引
       short index = style.getFontIndex();
       org.apache.poi.ss.usermodel.Font font = wb.getFontAt(index);
       //字型顏色
       int colorIndex = font.getColor();
       if(font.getBoldweight() == org.apache.poi.ss.usermodel.Font.BOLDWEIGHT_BOLD){
           result.setStyle(Font.BOLD);
       }
       HSSFColor color = HSSFColor.getIndexHash().get(colorIndex);
       if(color != null){
           int rbg = POIUtil.getRGB(color);
           result.setColor(new BaseColor(rbg));
       }
       //下劃線
       FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
       if(underline == FontUnderline.SINGLE){
           String ulString = FontStyle.UNDERLINE.getValue();
           result.setStyle(ulString);
       }
       return result;
    }
    
    protected int getBackgroundColorByExcel(CellStyle style) {
        Color color = style.getFillForegroundColorColor();
        return POIUtil.getRGB(color);
    }
    
    protected void addBorderByExcel(PdfPCell cell , CellStyle style) {
        Workbook wb = excel.getWorkbook();
        cell.setBorderColorLeft(new BaseColor(POIUtil.getBorderRBG(wb,style.getLeftBorderColor())));
        cell.setBorderColorRight(new BaseColor(POIUtil.getBorderRBG(wb,style.getRightBorderColor())));
        cell.setBorderColorTop(new BaseColor(POIUtil.getBorderRBG(wb,style.getTopBorderColor())));
        cell.setBorderColorBottom(new BaseColor(POIUtil.getBorderRBG(wb,style.getBottomBorderColor())));
    }
    
    protected int getVAlignByExcel(short align) {
        int result = 0;
        if (align == CellStyle.VERTICAL_BOTTOM) {
            result = Element.ALIGN_BOTTOM;
        }
        if (align == CellStyle.VERTICAL_CENTER) {
            result = Element.ALIGN_MIDDLE;
        }
        if (align == CellStyle.VERTICAL_JUSTIFY) {
            result = Element.ALIGN_JUSTIFIED;
        }
        if (align == CellStyle.VERTICAL_TOP) {
            result = Element.ALIGN_TOP;
        }
        return result;
    }

    protected int getHAlignByExcel(short align) {
        int result = 0;
        if (align == CellStyle.ALIGN_LEFT) {
            result = Element.ALIGN_LEFT;
        }
        if (align == CellStyle.ALIGN_RIGHT) {
            result = Element.ALIGN_RIGHT;
        }
        if (align == CellStyle.ALIGN_JUSTIFY) {
            result = Element.ALIGN_JUSTIFIED;
        }
        if (align == CellStyle.ALIGN_CENTER) {
            result = Element.ALIGN_CENTER;
        }
        return result;
    }
}
public class Resource {
    /**
     * 中文字型支援
     */
    protected static BaseFont BASE_FONT_CHINESE;
    static {
        try {
            BASE_FONT_CHINESE = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class POIImage {
    protected Dimension dimension;
    protected byte[] bytes;
    protected ClientAnchor anchor;
    
    public POIImage getCellImage(Cell cell) {
        byte[] result = null;
        Sheet sheet = cell.getSheet();
//      Workbook wb = sheet.getWorkbook();
//      List<PictureData> pictures = (List<PictureData>) wb.getAllPictures();
        if (sheet instanceof HSSFSheet) {
            HSSFSheet hssfSheet = (HSSFSheet) sheet;
            List<HSSFShape> shapes = hssfSheet.getDrawingPatriarch().getChildren();
            for (HSSFShape shape : shapes) {
                HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
                if (shape instanceof HSSFPicture) {
                    HSSFPicture pic = (HSSFPicture) shape;
                    PictureData data = pic.getPictureData();
                    String extension = data.suggestFileExtension();
                    int row1 = anchor.getRow1();
                    int row2 = anchor.getRow2();
                    int col1 = anchor.getCol1();
                    int col2 = anchor.getCol2();
                    if(row1 == cell.getRowIndex() && col1 == cell.getColumnIndex()){
                        dimension = pic.getImageDimension();
                        this.anchor = anchor;
                        this.bytes = data.getData();
                    }
                }
            }
        }
        return this;
    }
    
    public Dimension getDimension() {
        return dimension;
    }

    public void setDimension(Dimension dimension) {
        this.dimension = dimension;
    }

    public byte[] getBytes() {
        return bytes;
    }
    
    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }
    
    public ClientAnchor getAnchor() {
        return anchor;
    }

    public void setAnchor(ClientAnchor anchor) {
        this.anchor = anchor;
    }
}
public class POIUtil {
    
    public static int getRGB(Color color){
        int result = 0x00FFFFFF;
        
        int red = 0;
        int green = 0;
        int blue = 0;
        
        if (color instanceof HSSFColor) {
            HSSFColor hssfColor = (HSSFColor) color;
            short[] rgb = hssfColor.getTriplet();
            red = rgb[0];
            green = rgb[1];
            blue = rgb[2];
        }
        
        if (color instanceof XSSFColor) {
            XSSFColor xssfColor = (XSSFColor) color;
            byte[] rgb = xssfColor.getRgb();
            red = (rgb[0] < 0) ? (rgb[0] + 256) : rgb[0];
            green = (rgb[1] < 0) ? (rgb[1] + 256) : rgb[1];
            blue = (rgb[2] < 0) ? (rgb[2] + 256) : rgb[2];
        }
        
        if(red != 0 || green != 0 || blue != 0){
            result = new java.awt.Color(red, green, blue).getRGB();
        }
        return result;
    }
    
    public static int getBorderRBG(Workbook wb  , short index){
        int result = 0;
        
        if(wb instanceof HSSFWorkbook){
            HSSFWorkbook hwb = (HSSFWorkbook)wb;
            HSSFColor color =  hwb.getCustomPalette().getColor(index);
            if(color != null){
                result = getRGB(color);
            }
        }
        
        if(wb instanceof XSSFWorkbook){
            XSSFColor color = new XSSFColor();
            color.setIndexed(index);
            result = getRGB(color);
        }
        
        return result;
    }
    
    @SuppressWarnings("finally")
    public static byte[] scale(byte[] bytes , double width, double height) {
        BufferedImage bufferedImage = null;
        BufferedImage bufTarget = null;
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            bufferedImage = ImageIO.read(bais);
            double sx =  width / bufferedImage.getWidth();
            double sy =  height / bufferedImage.getHeight();
            int type = bufferedImage.getType();
            if (type == BufferedImage.TYPE_CUSTOM) {
                ColorModel cm = bufferedImage.getColorModel();
                WritableRaster raster = cm.createCompatibleWritableRaster((int)width, (int)height);
                boolean alphaPremultiplied = cm.isAlphaPremultiplied();
                bufTarget = new BufferedImage(cm, raster, alphaPremultiplied, null);
            } else {
                bufTarget = new BufferedImage((int)width, (int)height, type);
            }
            
            Graphics2D g = bufTarget.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.drawRenderedImage(bufferedImage, AffineTransform.getScaleInstance(sx, sy));
            g.dispose();
            
            if(bufTarget != null){
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ImageIO.write(bufTarget, "png", baos);
                byte[] result = baos.toByteArray();
                return result;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public static void main(String[] args) throws Exception {
//      FileInputStream fis = new FileInputStream(new File(directory+"副本Common-ReportList-AssistPDF-Temp.xls"));
//      FileInputStream fis = new FileInputStream(new File("D:\\tmp.xls"));
        
        FileInputStream fis1 = new FileInputStream(new File("D:\\pdfexport\\MAD 5-3-05-Octavia NF-20131025.xls"));
        FileInputStream fis2 = new FileInputStream(new File("D:\\pdfexport\\MAD 6-1-47-Octavia NF-20131025.xls"));
        FileInputStream fis3 = new FileInputStream(new File("D:\\pdfexport\\MAD 038-Superb FL DS-20131025.xls"));
        //
        FileOutputStream fos = new FileOutputStream(new File("D:\\test.pdf"));
        //
        List<ExcelObject> objects = new ArrayList<ExcelObject>();
        objects.add(new ExcelObject("1.MAD 5-3-05-Octavia NF-20131025.xls",fis1));
        objects.add(new ExcelObject("2.MAD 6-1-47-Octavia NF-20131025.xls",fis2));
        objects.add(new ExcelObject("3.MAD 038-Superb FL DS-20131025.xls",fis3));
//        
        Excel2Pdf pdf = new Excel2Pdf(objects , fos);
        pdf.convert();
}

程式碼完畢,如果有什麼問題的可以給我留言,大家可以共同探討一下!