Java基於Itext7實現Html轉PDF的方法,解決老版本缺陷。


寫在前面

以下路徑問題根據項目結構自己修改,以下是我使用spring boot打成jar包的寫法。

一、需求背景

在前端編輯器中輸入任意的文本,包括css樣式變化,保存為html文本。通過Java后台將html文本轉換為PDF文檔並加上頁眉、頁腳、水印等。因為網上開源的方案用的工具版本都比較老,也無法滿足要求。所以只能用目前比較新的Itext7,網上的資料不多,只能看文檔自己學習。

二、解決方案

1.開發工具Itext7 (https://itextpdf.com/itext7): 首先jar包一定要引對,要不Demo也運行不了。我項目使用的是maven,以下是pom.xml最新版jar可以通過官方文檔中尋找。

<!-- pdfHTML -->
<dependency>
 <groupId>com.itextpdf</groupId>
 <artifactId>html2pdf</artifactId>
 <version>1.0.2</version>
</dependency>
<!-- add all iText 7 Community modules -->
<dependency>
 <groupId>com.itextpdf</groupId>
 <artifactId>itext7-core</artifactId>
 <version>7.0.5</version>
 <type>pom</type>
</dependency>

itext7-core包含了9個jar包,直接都導入就好

2.最簡單的HTML轉PDF(包含中文字體、粗體、表格等基本,不支持PDF的頁眉、頁腳、頁邊距、水印等)方法的參數和返回值可以靈活變通

public class Html2PdfUtil {
    public static void main(String[] args) throws Exception {
        String html = "<p><span style=\"font-family: Microsoft YaHei;\">微軟雅黑: 粗體前A<strong>A粗體A</strong>A粗體后</span></p>\n" +
                "<p><span style=\"font-family: SimSun;\">宋體: 粗體前A<strong>A粗體A</strong>A粗體后</span></p>\n" +
                "<p><span style=\"font-family: STHeiti;\">黑體: 粗體前A<strong>A粗體A</strong>A粗體后</span></p>" +
                "<p><span style=\"font-family: Times New Roman;\">Times New Roman: pre bdA<strong>AbdA</strong>Aaft bd</span></p>\n";
        FileOutputStream fileOutputStream = new FileOutputStream("D:/Test/a.pdf");
        fileOutputStream.write(convert(html));
        fileOutputStream.close();
    }
    public static byte[] convert(String html) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ConverterProperties props = new ConverterProperties();
        FontProvider fp = new FontProvider(); // 提供解析用的字體
        fp.addStandardPdfFonts(); // 添加標准字體庫、無中文
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        fp.addDirectory(classLoader.getResource("fonts").getPath()); // 自定義字體路徑、解決中文,可先用絕對路徑測試。
        props.setFontProvider(fp);
        // props.setBaseUri(baseResource); // 設置html資源的相對路徑
        HtmlConverter.convertToPdf(html, outputStream, props); // 無法靈活設置頁邊距等
        byte[] result = outputStream.toByteArray();
        outputStream.close();
        return result;
    }
}

這段代碼添加了中文字體庫,否則中文無法顯示。設置了解析html時資源的相對路徑。還有就是如果html編輯器有加粗樣式類的需求時,需要把該字體和加粗字體都上傳,否則無法正常顯示,字體不太好找,大部分字體系統盤中有,附宋體加粗字體,和華文黑體加粗,黑體加粗沒找到。

https://files.cnblogs.com/files/Sigurd/STHeitibd.rar
https://files.cnblogs.com/files/Sigurd/simsunbd.rar

3.如何靈活設置生成pdf的頁邊距

首先要找到在哪里設置頁邊距,找了一圈發現只有Document類中有這個方法,所有剛開始我用了HtmlConvert.convertToDocument()方法,但是發現得到的Document類immediateFlush為true,沒辦法手動重新布局,只能換方法了。

然后看到了List HtmlConvert.convertToElements(), 之后就好辦了自己新建一個Document設置好頁邊距,然后foreach插入(IBlockElement)IElement就好。生成pdf之后確實有了邊距,但是段間距變得不正常了。

IBlockElement中沒有修改邊距的方法,看一下IBlockElement的實現類BlockElement中可以修改邊距,然后強轉成BlockElement就可以修改行句了。最后代碼如下。

public class Html2PdfUtil {
    public static final float topMargin = 114f;
    public static final float bottomMargin = 156f;
    public static final float leftMargin = 90f;
    public static final float rightMargin = 90f;
    public static byte[] convert(String html) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfWriter writer = new PdfWriter(outputStream);
        PdfDocument pdfDocument = new PdfDocument(writer);
        try {
            ConverterProperties props = new ConverterProperties();
            FontProvider fp = new FontProvider();
            fp.addStandardPdfFonts();
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            fp.addDirectory(classLoader.getResource("fonts").getPath());
            props.setFontProvider(fp);
            List<IElement> iElements = HtmlConverter.convertToElements(html, props);
            Document document = new Document(pdfDocument, PageSize.A4, true); // immediateFlush設置true和false都可以,false 可以使用 relayout
            document.setMargins(topMargin, rightMargin, bottomMargin, leftMargin);
            for (IElement iElement : iElements) {
                BlockElement blockElement = (BlockElement) iElement;
                blockElement.setMargins(1, 0, 1, 0);
                document.add(blockElement);
            }
            document.close();
            return outputStream.toByteArray();
        } catch (Exception e) {
            throw e;
        } finally {
            outputStream.close();
        }
    }
}

4.頁眉頁腳水印的寫法

頁眉頁腳水印都是在每頁插入相同內容,所以做法類似。都是實現IEventHandler接口,然后添加監聽。頁眉頁腳水印、基本都是圖片和文字調用的api也比較簡單,原點坐標在左下角,在第一象限內做圖,以下面的代碼為例吧。

/**
 * Description html轉pdf
 * Created by shuxiaogang 
 * date on 2017/11/22
 */
public class Html2PdfUtil {
    public static final float topMargin = 114f;
    public static final float bottomMargin = 156f;
    public static final float leftMargin = 90f;
    public static final float rightMargin = 90f;

    public static byte[] convert(String html) throws IOException {
        ...同代碼3
        Header headerHandler = new Header();
        Footer footerHandler = new Footer();
        WatermarkingEventHandler watermarkingEventHandler = new WatermarkingEventHandler();
        pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
        pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
        pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, watermarkingEventHandler);
        ...同代碼3
    }
    // 頁眉
    protected static class Header implements IEventHandler {
        protected float width = 102f;
        protected float height = 32f;
        protected float x = 42f;
        protected float y = 740f;
        @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);
            ImageData image = null;
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            InputStream logo = loader.getResourceAsStream("imgaes/logo.jpg");
            try {
                image = ImageDataFactory.create(toByteArray(logo));
            } catch (IOException e) {
                e.printStackTrace();
            }
            Image img = new Image(image);
            img.scaleAbsolute(width, height); // 圖片寬高
            img.setFixedPosition(x, y); // 圖片坐標 左下角(0,0)
            canvas.add(img);
        }
    }
    // 頁腳
    protected static class Footer implements IEventHandler {
        protected PdfFormXObject placeholder; // 相對坐標系
        protected float x = 82f;
        protected float y = 50f;
        protected float imageWidth = 6f;
        protected float imageHeight = 78f;
        protected float space = 10f;
        public Footer() {
            placeholder =
                    new PdfFormXObject(new Rectangle(0, 0, 500, 78));
        }
        @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);
            pdfCanvas.addXObject(placeholder, x + space, y);
            Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
            ImageData image = null;
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            InputStream buleRed = loader.getResourceAsStream("imgaes/bule_red.JPG");
            try {
                image = ImageDataFactory.create(toByteArray(buleRed));
            } catch (IOException e) {
                e.printStackTrace();
            }
            Image img = new Image(image);
            img.scaleAbsolute(imageWidth, imageHeight);
            img.setFixedPosition(x, y);
            canvas.add(img);
            writeInfo(pdf);
            pdfCanvas.release();
        }
        public void writeInfo(PdfDocument pdf) {
            Canvas canvas = new Canvas(placeholder, pdf);
            canvas.setFontSize(7.5f);
            PdfFont pdfFont = null;
            try {
                // 微軟雅黑
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                InputStream msyh = loader.getResourceAsStream("fonts/msyh.ttf");
                pdfFont = PdfFontFactory.createFont(toByteArray(msyh), PdfEncodings.IDENTITY_H, false);
            } catch (IOException e) {
                e.printStackTrace();
            }
            canvas.setFont(pdfFont); // 需要單獨設置一下字體才能使用中文
            canvas.showTextAligned("http://www.xxxx.com",
                    0, 65, TextAlignment.LEFT);
            canvas.showTextAligned("深圳市南山區學府路東xxxxx  xxxxxx",
                    0, 50, TextAlignment.LEFT);
            canvas.showTextAligned("xxxxx Ixxxxxx,Xuefu Road Ease,Nan Shan District, Shenzhen xxxxxx",
                    0, 35, TextAlignment.LEFT);
            canvas.showTextAligned("Tel:0755-xxxxx Fax:212-xxxxxx",
                    0, 20, TextAlignment.LEFT);
        }
    }
    // 水印
    protected static class WatermarkingEventHandler implements IEventHandler {
        protected float x = 298f;
        protected float y = 421f;
        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdfDoc = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            PdfFont font = null;
            try {
                font = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
            } catch (IOException e) {
                e.printStackTrace();
            }
            PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
            new Canvas(canvas, pdfDoc, page.getPageSize())
                    .setFontColor(Color.LIGHT_GRAY)
                    .setFontSize(60)
                    .setFont(font)
                    .showTextAligned(new Paragraph("W A T E R M A R K"), x, y, pdfDoc.getPageNumber(page),
                            TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45);
        }
    }

    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM