Java Itextpdf 导出pdf

27 篇文章 0 订阅

Java Itextpdf 导出pdf

需求

  • 公司新需求:导出前端页面上的某个部分为pdf,由于涉及领导签名的图片以及只是页面的一部分而非整个页面 这个需求交给后端完成
  • 接下去我将以Java为基础讲述在服务端如何导出pdf

思路

  • 使用itextpdf+原生Document编写
    • 过于麻烦
    • 需要掌握大量元素,在后端写前端页面
  • 使用itextpdf + pdf模板
    • 对基本字段替换支持较好
    • 样式不好看
    • 需要自己绘制模板,工具有限
  • 使用html模板+html转为pdf
    • 可以与前端同学配合完成
    • css样式支持不完善 部分不支持,现在探测表格样式较为完善
    • 自带的分页若表格跨页支持不完善,一行被分为两段分布在两页,需特殊处理

方案

  • 最终选定使用html模板+html转为pdf

  • 使用mustache模板引擎+itextpdf进行html渲染以及转为pdf,使用pdfbox将pdf转为图片

  • 依赖

  •         <dependency>
                <groupId>com.samskivert</groupId>
                <artifactId>jmustache</artifactId>
                <version>1.15</version>
            </dependency>
    
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itextpdf</artifactId>
                <version>5.5.6</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext-asian</artifactId>
                <version>5.2.0</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>com.itextpdf.tool</groupId>
                <artifactId>xmlworker</artifactId>
                <version>5.5.13.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>fontbox</artifactId>
                <version>2.0.15</version>
            </dependency>
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>pdfbox</artifactId>
                <version>2.0.15</version>
            </dependency>
    

主要代码

		// html 模板
		String template = "template/mustache_template.html";
		// 随便构造数据
		Map<String, Object> dataMap = new HashMap<>();
		// 组件替换
		String htmlContent = Mustache.compiler().compile(templateString).execute(dataMap);
		String newPDFPath = "D:\\Learning\\export\\testM12.pdf";

		FileOutputStream fileOutputStream = new FileOutputStream(newPDFPath);
		// 自定义转换 修复base64图片转换 table分页出错
		html2pdf1(htmlContent, fileOutputStream);
	/**
	 * 自定义内容转换
	 * @param html html模板替换后的文本
	 * @param fileOutputStream 输出流
	 * @throws RuntimeException
	 * @throws DocumentException
	 * @throws IOException
	 */
	public static void html2pdf1(String html, FileOutputStream fileOutputStream) throws RuntimeException, DocumentException, IOException {
		Document doc = new Document();
		PdfWriter writer = PdfWriter.getInstance(doc, fileOutputStream);
		// 添加页眉/页脚
//		PDFPageHeadFootHelper headFooter = new PDFPageHeadFootHelper(doc);
//		writer.setPageEvent(headFooter);
		doc.open();

		// 主要自定义img标签处理机制,html中的img标签图片存放分两种:uri/base64的
		final TagProcessorFactory tagProcessorFactory = Tags.getHtmlTagProcessorFactory();
		tagProcessorFactory.removeProcessor(HTML.Tag.IMG);
		tagProcessorFactory.removeProcessor(HTML.Tag.TABLE);
		tagProcessorFactory.addProcessor(new ImageTagProcessor(), HTML.Tag.IMG);
		tagProcessorFactory.addProcessor(new TableTagProcessor(), HTML.Tag.TABLE);
		// 自定义
		InputStream cssInputStream = XMLWorkerHelper.class.getResourceAsStream("/default.css");
		// 可通过ClassPathResource 加载资源路径下的文件
//		ClassPathResource classPathResource = new ClassPathResource("template/my.css");
		CssFile css = getCSS(cssInputStream);
		XMLWorkerFontProvider xmlWorkerFontProvider = new XMLWorkerFontProvider();

		CssFilesImpl cssFiles = new CssFilesImpl();
		cssFiles.add(css);
		StyleAttrCSSResolver cssResolver = new StyleAttrCSSResolver(cssFiles);
		HtmlPipelineContext hpc = new HtmlPipelineContext(new CssAppliersImpl(xmlWorkerFontProvider));
		// 特殊tag处理工厂
		hpc.setAcceptUnknown(true).autoBookmark(true).setTagFactory(tagProcessorFactory).setResourcesRootPath(null);
		HtmlPipeline htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(doc, writer));
		Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
		XMLWorker worker = new XMLWorker(pipeline, true);
		XMLParser p = new XMLParser(true, worker, StandardCharsets.UTF_8);
		ByteArrayInputStream in = new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8));
		p.parse(in, StandardCharsets.UTF_8);
		doc.close();
		in.close();

	}
	/**
	 * pdf转成一张图片
	 *
	 * @param is           pdf输入流
	 * @param outputStream 图片输出流
	 */
	private static void pdf2multiImage(InputStream is, OutputStream outputStream) {
		try {
			// 读取文件
			PDDocument pdf = PDDocument.load(is);
			int actSize = pdf.getNumberOfPages();
			// 转为image对象
			List<BufferedImage> piclist = new ArrayList<BufferedImage>();
			for (int i = 0; i < actSize; i++) {
				BufferedImage image = new PDFRenderer(pdf).renderImageWithDPI(i, 130, ImageType.RGB);
				piclist.add(image);
			}
			// 图片输出
			yPic(piclist, outputStream);
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

踩坑

Java BIO input/outputStream转换

  • 可通过ByteArrayOutputStream/ByteArrayOutputStream进行流的方向转换
  • ByteArray底层用字节数组承载全量二进制数据,因此这个方案要注意不能载入太大文件到内存
  • Java自带的一些stream使用缓冲区解决把内存撑爆的问题,例如FileInputStream每次读取缓冲区大小的数据

Base64图片处理不支持

  • 自定义ImageTagProcessor 处理base64格式
public class ImageTagProcessor extends com.itextpdf.tool.xml.html.Image {

	@Override
	public List<Element> end(final WorkerContext ctx, final Tag tag, final List<Element> currentContent) {
		final Map<String, String> attributes = tag.getAttributes();
		String src = attributes.get(HTML.Attribute.SRC);
		List<Element> elements = new ArrayList<Element>(1);
		if (null != src && src.length() > 0) {
			Image img = null;
			if (src.startsWith("data:image/")) {
				final String base64Data = src.substring(src.indexOf(",") + 1);
				try {
					img = Image.getInstance(Base64.decode(base64Data));
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
				if (img != null) {
					try {
						final HtmlPipelineContext htmlPipelineContext = getHtmlPipelineContext(ctx);
						elements.add(getCssAppliers().apply(new Chunk((Image) getCssAppliers().apply(img, tag, htmlPipelineContext), 0, 0, true), tag,
								htmlPipelineContext));
					} catch (NoCustomContextException e) {
						throw new RuntimeWorkerException(e);
					}
				}
			}

			if (img == null) {
				elements = super.end(ctx, tag, currentContent);
			}
		}
		return elements;
	}
}

table自动分页错误

  • 自定义TableTagProcessor 处理分页问题
public class TableTagProcessor extends com.itextpdf.tool.xml.html.table.Table {

	@Override
	protected PdfPTable intPdfPTable(int numberOfColumn) {
		PdfPTable table = new PdfPTable(numberOfColumn);

		table.setHorizontalAlignment(Element.ALIGN_LEFT);
		table.setSplitLate(true);
//		table.setSplitLate(false);
//		table.setSplitRows(false);

		return table;
	}
}

服务器导出中文字体不支持

yum -y install fontconfig
fc-list :lang=zh
mkdir -p /usr/share/fonts/chinese
chmod -R 755 /usr/share/fonts/chinese
cd /usr/share/fonts/chinese/
# 上传字体
yum -y install mkfontscale
mkfontscale
fc-list :lang=zh

详细代码

https://github.com/luoziling/Design_Pattern/tree/master/src/priv/wzb/itext

参考

SpringBoot读取Resource下文件的几种方式

CentOS7安装中文字体

利用iTextPDF将Html转成PDF中图片img中包含base64的处理

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值