itext5创建pdf表格及遇到的一些问题

        • 0. 核心依赖:
        • 1. 设置页眉图片及下划线
        • 2. document参数传递:
        • 3. 生成的pdf文件转base64编码:
        • 4. 平方²上标显示问题:
        • 5. 压缩包的文件流InputStream输出文件:
        • 6. itext5进行pdf合并:

0. 核心依赖:

<dependency>
    <groupId>com.itextpdfgroupId>
    <artifactId>itextpdfartifactId>
    <version>5.5.10version>
dependency>
<dependency>
    <groupId>com.itextpdfgroupId>
    <artifactId>itext-asianartifactId>
    <version>5.2.0version>
dependency>

1. 设置页眉图片及下划线

通过PdfPageEventHelper事件可以动态的创建页眉,数据构建出多少页pdf就有多少页页眉

package com.example.pdf;

import com.example.pdf.vo.RenovationDocNameEnum;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

/**
 * 内部类 添加页眉、页脚
 */
public class PdfEvent extends PdfPageEventHelper {

    // 一页加载完成触发,写入页眉和页脚
    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        PdfPTable head = new PdfPTable(1);
        PdfObject businessType = document.getAccessibleAttribute(new PdfName("businessType"));
        PdfObject flag = document.getAccessibleAttribute(new PdfName("flag"));
        try {
            if (RenovationDocNameEnum.CB.getName().equals(businessType.toString())
                    || (RenovationDocNameEnum.SG.getName().equals(businessType.toString()) && flag != null && "1".equals(flag.toString()))) {
                setCbPageHead(head, writer, document);
            } else if (RenovationDocNameEnum.SG.getName().equals(businessType.toString()) || RenovationDocNameEnum.JC.getName().equals(businessType.toString())) {
                setSinglePageHead(head, writer, document);
            }
        } catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }

    /**
     * Chunk的offsetX控制页眉图片水平位置,offsetY会影响到图片大小; 图片上下位置可通过showTextAligned的y来控制
     * 页眉下划线通过setTotalWidth控制先长度,writeSelectedRows的xPos控制线的起始水平位置
     *
     * @param head
     * @param writer
     * @param document
     * @throws DocumentException
     */
    private void setSinglePageHead(PdfPTable head, PdfWriter writer, Document document) throws DocumentException {
        Font font = PdfUtil.getFont(null);
        // 通过表格构建页眉下划线
        head.setTotalWidth(PageSize.A4.getWidth() - 105);
        head.setWidths(new int[]{24});
        head.setLockedWidth(true);
        head.getDefaultCell().setFixedHeight(-10);
        head.getDefaultCell().setBorder(Rectangle.BOTTOM);
        head.getDefaultCell().setBorderWidth(0.5f);
        head.addCell(new Paragraph(" ", font));
        // 将页眉写到document中,位置可以指定,指定到下面就是页脚
        head.writeSelectedRows(0, -1, 55, PageSize.A4.getHeight() - 20, writer.getDirectContent());
        PdfContentByte directContent = writer.getDirectContent();
        // 最重要的是这个,如果页眉需要设置图片的话,需要在Phrase对象中添加一个Chunk对象,在Chunk对象中添加图片信息即可
        Phrase phrase = new Phrase("", font);
        Image img = PdfUtil.getImg();
        if (img != null) {
            phrase.add(new Chunk(img, 30, -150));
        }
        // 写入页眉
        ColumnText.showTextAligned(directContent, Element.ALIGN_RIGHT, phrase, document.right(), PageSize.A4.getHeight() + 48, 0);
    }
  • 通过PdfPTable来构建下划线
  • 通过new Chunk来显示图片,并通过ColumnText.showTextAligned来展示在页眉
  • 详细见setSinglePageHead方法的注释,方正就是一顿调,知道合适位置

2. document参数传递:

  • 像下述这样定义一个字符串参数于document,就可以像上述PdfEvent中获取做业务判断
  • 使用的时候注意toString()
PdfName businessType = new PdfName("businessType");
document.setAccessibleAttribute(businessType, new PdfString(type));

3. 生成的pdf文件转base64编码:

  • 主要以下两个方法,FileUtil是hutool的工具类,Base64是java.util的工具类

	PdfUtil.getBase64(FileUtil.readBytes(file));
	
    public static String getBase64(byte[] buffer) {
        return Base64.getEncoder().encodeToString(buffer);
    }

4. 平方²上标显示问题:

本人生成pdf时用的是simkai.ttf字体,输出的pdf没有显示²上标

  • 要么可以换字体,百度即可
  • 要么像如下这样,通过setTextRise来控制文字位置,来大概展示成²效果
  • 方法正值为上标,负值可为下标,注意上标的2的文字specialFont字体要定义小点,本人定义5
 public static PdfPCell getPDFCellSpecial(String name, Font font, Integer alignment) {
        PdfPCell cell = new PdfPCell();
        if (name == null) {
            name = " ";
        }
        Paragraph p = new Paragraph(name, font);
        Font specialFont = PdfUtil.getSpecialFont(null);
        Chunk chunk = new Chunk("2", specialFont);
        chunk.setTextRise(5f);
        p.add(chunk);
        p.add(new Chunk(")", font));

        if (alignment == null) {
            p.setAlignment(Element.ALIGN_CENTER);
        } else {
            p.setAlignment(alignment);
        }
        cell.setUseAscender(true);
        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
        cell.addElement(p);
        return cell;
    }

5. 压缩包的文件流InputStream输出文件:

  • 业务上从别的接口获取到一个zip包的文件流,需要将zip包的文件输出到目录,如下代码:
  • 涉及IO流要注意关流

    private static List<String> extractAllByInputStream(InputStream inputStream, String pdfPath) {
        ArrayList<String> list = Lists.newArrayList();
        try {
            dealZipInputStream(inputStream, pdfPath, list);
        } catch (IOException e) {
            LOGGER.info("下载pdf到本地异常,异常信息:{}", e);
        }

        return list;
    }

    private static void dealZipInputStream(InputStream inputStream, String pdfPath, ArrayList<String> list) throws IOException {
        byte[] buffer = new byte[1024];
        ZipEntry zipEntry;
        try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                String entryName = zipEntry.getName();
                if (!zipEntry.isDirectory()) {
                    String fileName;
                    if (entryName.contains(RenovationDocNameEnum.CB.getName())) {
                        fileName = pdfPath + RenovationDocNameEnum.CB.getName() + ".pdf";
                    } else if (entryName.contains(RenovationDocNameEnum.JC.getName())) {
                        fileName = pdfPath + RenovationDocNameEnum.JC.getName() + ".pdf";
                    } else if (entryName.contains(RenovationDocNameEnum.SG.getName())) {
                        fileName = pdfPath + RenovationDocNameEnum.SG.getName() + ".pdf";
                    } else {
                        fileName = pdfPath + UUID.randomUUID() + ".pdf";
                    }
                    list.add(fileName);
                    File subFile = new File(fileName);
                    subFile.createNewFile();
                    readSubFile(subFile, zipInputStream, buffer);

                }
                zipInputStream.closeEntry();
            }
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    private static void readSubFile(File subFile, ZipInputStream zipInputStream, byte[] buffer) {
        int len;
        try (FileOutputStream fileOut = new FileOutputStream(subFile)) {
            while ((len = zipInputStream.read(buffer)) > 0) {
                fileOut.write(buffer, 0, len);
            }
        } catch (IOException e) {
            throw new ExceptionConverter(e);
        }
    }

6. itext5进行pdf合并:

  • 本人文件合并后要删除之前的文件,这时需要注意new PdfReader(inputStream)时使用inputStream
  • 不然可能出现:别的程序正在使用而无法删除情况

    /* 合并pdf文件
     * @param files 要合并文件数组(绝对路劲{ "D:\\a.pdf", "D:\\b.pdf" })
     * @param savePath 合并后新产生的文件绝对路径如D:\\temp.pdf
     */
    public static Integer mergePdfFiles(String[] files, String savePath) {
        Document document = null;
        PdfCopy copy = null;
        try (FileOutputStream outputStream = new FileOutputStream(savePath)) {
            // 创建一个与a.pdf相同纸张大小的document
            document = new Document(new PdfReader(files[0]).getPageSize(1));
            copy = new PdfCopy(document, outputStream);
            document.open();
            for (int i = 0; i < files.length; i++) {
                // 一个一个的遍历现有的PDF
                dealFile(files[i], copy, document);
            }
            copy.close();
            document.close();
            return 1;
        } catch (IOException | DocumentException e) {
            LOGGER.info("合并pdf失败,异常信息:{}", e);
        } finally {
            if (document != null) {
                document.close();
            }
            if (copy != null) {
                copy.close();
            }
        }
        return 0;
    }

    private static void dealFile(String file, PdfCopy copy, Document document) {
        PdfReader reader = null;
        try (FileInputStream inputStream = new FileInputStream(file)) {
            reader = new PdfReader(inputStream);
            int n = reader.getNumberOfPages();// PDF文件总共页数
            for (int j = 1; j <= n; j++) {
                document.newPage();
                PdfImportedPage page = copy.getImportedPage(reader, j);
                copy.addPage(page);
            }
            reader.close();
        } catch (IOException | BadPdfFormatException e) {
            throw new ExceptionConverter(e);
        } finally {
            if (reader != null) {
                reader.close();
            }
        }

    }

你可能感兴趣的:(pdf,java,itext,itextpdf,itext5)