iText7 HTML转PDF(页眉、页码、水印) + 浏览器在线预览

背景:

表单上点击打印按钮,把表单转为pdf并在浏览器预览。PDF设置固定页眉,页眉(含图片)、页码和水印。

 实现:

项目用的是Springboot + VUE2.x。

生成HTML:         FreeMarker或者Velocity

HTML转PDF:      iText7

实现步骤:

关键依赖

<!-- itext7 html转pdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>3.0.3</version>
</dependency>

<!-- pdf中文字体 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>font-asian</artifactId>
    <version>7.1.11</version>
</dependency>

1. 生成HTML(这里略过...) 

2. HTML转PDF

Service代码片段

// 生成html
RenderTemplateReq renderTemplateReq = new RenderTemplateReq();
renderTemplateReq.setTemplatePath("vm/pdf/iqc.vm");
renderTemplateReq.setData(JSON.parseObject(JSON.toJSONString(vo)));
Result<String> htmlResult = generatorRemote.renderTemplate(renderTemplateReq);
ResultCode.FEIGN_ERROR.assertTrue(Result.isSuccess(htmlResult), "生成html失败");
String html = htmlResult.getData();
// html转pdf
PdfGenerator.generatePdf(html, outputPath);

生成PDF

package com.multek.sqm.itext;

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.impl.OutlineHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.WriterProperties;
import com.itextpdf.layout.font.FontProvider;
import com.multek.sqm.itext.event.PageHeader;
import com.multek.sqm.itext.event.PageMarker;
import com.multek.sqm.itext.event.WaterMarker;

/**
 * PDF工具类
 *
 * @author: mcnlshen
 * @date: 2021-09-17 23:50
 */
public class PdfGenerator {

    private PdfGenerator() {
        // 构造器私有化
    }

    /**
     * 生成PDF
     *
     * @param html HTML
     * @param outputFile 输出路径
     */
    public static void generatePdf(String html, String outputFile) {
        try {
            //outputFile也可以是输出流
            PdfWriter writer = new PdfWriter(outputFile, new WriterProperties().setFullCompressionMode(Boolean.TRUE));
            PdfDocument doc = new PdfDocument(writer);
            doc.setDefaultPageSize(PageSize.A4);
            doc.getDefaultPageSize().applyMargins(20, 20, 20, 20, true);

            // 设置中文字体
            FontProvider fontProvider = new FontProvider();
            PdfFont pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);
            fontProvider.addFont(pdfFont.getFontProgram(), "UniGB-UCS2-H");

            // 水印
            doc.addEventHandler(PdfDocumentEvent.END_PAGE, new WaterMarker());
            // 页眉
            doc.addEventHandler(PdfDocumentEvent.END_PAGE, new PageHeader());
            // 页码
            doc.addEventHandler(PdfDocumentEvent.END_PAGE, new PageMarker(pdfFont));

            ConverterProperties properties = new ConverterProperties();
            properties.setFontProvider(fontProvider);
            //PDF目录
            properties.setOutlineHandler(OutlineHandler.createStandardHandler());
            HtmlConverter.convertToPdf(html, doc, properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
package com.multek.sqm.itext.event;

import cn.hutool.core.io.IoUtil;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.borders.SolidBorder;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.HorizontalAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;
import lombok.SneakyThrows;

import java.io.InputStream;

/**
 * PDF页眉
 *
 * @author: mcnlshen
 * @date: 2021-09-18 00:36
 */
public class PageHeader implements IEventHandler {


    @SneakyThrows
    @Override
    public void handleEvent(Event event) {
        final PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        final PdfDocument pdfDoc = docEvent.getDocument();
        final Document doc = new Document(pdfDoc);
        final PdfPage page = docEvent.getPage();
        final Rectangle pageSize = page.getPageSize();
        final float pdfWidth = pageSize.getWidth();
        final float pdfHeight = pageSize.getHeight();
//        final PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
//        final Color lineColor = new DeviceRgb(57, 123, 198);
//        pdfCanvas.setLineWidth(1.5f).setStrokeColor(lineColor);
        final float tableWidth = pdfWidth - doc.getRightMargin() - doc.getLeftMargin();
        // 页眉
//        final float x0 = doc.getRightMargin(), y0 = pdfHeight - doc.getTopMargin();
//        pdfCanvas.moveTo(x0, y0).lineTo(pdfWidth - doc.getRightMargin(), y0).stroke();
        final Table headerTable = new Table(2);
        headerTable.setFixedLayout();
        headerTable.setWidth(tableWidth);
        headerTable.setHorizontalAlignment(HorizontalAlignment.CENTER);

        // 设置图片
        InputStream is = this.getClass().getResourceAsStream("/img/multeklogo.jpg");
        byte[] bytes = IoUtil.readBytes(is);
        Image img = new Image(ImageDataFactory.create(bytes));
        img.setHeight(30);
        Paragraph imgParagraph = new Paragraph().add(img);
        imgParagraph.setMarginLeft(-20);
        Cell imgCell = new Cell();
        imgCell.setBorder(Border.NO_BORDER);
        imgCell.setHorizontalAlignment(HorizontalAlignment.LEFT);
        imgCell.setVerticalAlignment(VerticalAlignment.BOTTOM);
        imgCell.setTextAlignment(TextAlignment.LEFT);
        imgCell.add(imgParagraph);
        headerTable.addCell(imgCell);

        final Paragraph righText = new Paragraph();
        righText.setVerticalAlignment(VerticalAlignment.BOTTOM);
//        righText.add(new Tab()).addTabStops(new TabStop(800, TabAlignment.RIGHT));
        righText.setBorder(new SolidBorder(1));
        righText.add("F/SQM-IQC-001-BX-01E");
        righText.setMarginRight(19);
        righText.setPadding(1);
        final Cell rightCell = new Cell();
        rightCell.add(righText);
        rightCell.setWidth(140);
        rightCell.setFontSize(9f);
        rightCell.setBorder(Border.NO_BORDER);
        rightCell.setTextAlignment(TextAlignment.CENTER);
//        realnameCell.setFontColor(lineColor);
        rightCell.setVerticalAlignment(VerticalAlignment.BOTTOM);
        rightCell.setHorizontalAlignment(HorizontalAlignment.RIGHT);
        headerTable.addCell(rightCell);

        headerTable.setFixedPosition(doc.getLeftMargin(), pdfHeight - 85, tableWidth);
        doc.add(headerTable);
    }




}
package com.multek.sqm.itext.event;

import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import lombok.AllArgsConstructor;

/**
 * PDF页码
 *
 * @author mcnlshen
 * @date 2021-09-18
 */
@AllArgsConstructor
public class PageMarker implements IEventHandler {

    private PdfFont pdfFont;

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdf = docEvent.getDocument();
        PdfPage page = docEvent.getPage();
        Rectangle pageSize = page.getPageSize();
        PdfCanvas pdfCanvas = new PdfCanvas(
                page.getLastContentStream(), page.getResources(), pdf);
        Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
        float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
        float y = pageSize.getBottom() + 15;
        Paragraph p = new Paragraph("第" + pdf.getPageNumber(page) + "页")
                .setFontSize(12)
                .setFont(pdfFont);
        canvas.showTextAligned(p, x, y, TextAlignment.CENTER);
        canvas.close();
    }
}
package com.multek.sqm.itext.event;

import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;

/**
 * Itext7 实现水印
 *
 * @author mcnlshen
 * @date 2021-09-18
 */
public class WaterMarker implements IEventHandler {

    @Override
    public void handleEvent(Event event) {

        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdf = docEvent.getDocument();
        PdfPage page = docEvent.getPage();
        Rectangle pageSize = page.getPageSize();
        PdfCanvas pdfCanvas = new PdfCanvas(
                page.getLastContentStream(), page.getResources(), pdf);
        Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
        Paragraph waterMarker = new Paragraph("MULTEK IQC")
                .setOpacity(0.05f)
                .setFontSize(40);

        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                canvas.showTextAligned(waterMarker, (150 + i * 300), (160 + j * 150), pdf.getNumberOfPages(), TextAlignment.CENTER, VerticalAlignment.BOTTOM, .6f);
            }
        }
        canvas.close();
    }
}

前端预览核心代码

// 预览pdf
export function previewOnline(query) {
  axios({
    method: 'get',
    url: process.env.VUE_APP_BASE_API + '/sftp/download',
    params: query,
    responseType: 'blob',
    headers: { 'Authorization': 'Bearer ' + getToken() }
  }).then(res => {
    let blob = new Blob([res.data], { type: 'application/pdf' })
    // 适配
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(blob)
    } else {
      const fileURL = URL.createObjectURL(blob)
      window.open(fileURL)
    }
  })
}

最终效果图

 

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值