Skip to content

值得您信賴的旅遊品牌 | 團體旅遊、自由行的專家‎

機場接送

Menu
  • 首頁
  • 裝潢設計
  • 旅遊天地
  • 環保清潔
Menu

itext7史上最全實戰總結_台中搬家

Posted on 2021-05-182021-05-18 by admin

※台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

1. itext7史上最全實戰總結

1.1. 前言

最近有個需求需要我用Java手動寫一份PDF報告,經過考察幾種pdf開源代碼,最終選取了itext7,此版本為7.1.11,由於發現網上關於該工具的博文比較少,特別是實戰博文幾乎沒有,在我踩完各種坑,最終把PDF成型后,打算把經驗分享出來,本文通過摘錄解釋來說明,內容來自本人GitHub itext-pdf

1.2. 配置文件

項目採用了Spring Cloud config所以配置在git上,僅僅研究itext7不需要用到數據庫等功能,請直接運行PdfMain類的main方法,即可生成模擬的PDF報告

1.3. 版本POM

itext7相關pom

<properties>
    <itext.version>7.1.11</itext.version>
</properties>
<dependencies>
    <!-- itext7 -->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>kernel</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>io</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>layout</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>forms</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>pdfa</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>pdftest</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>font-asian</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.18</version>
    </dependency>

    <!--itext7 html轉pdf用到的包-->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>html2pdf</artifactId>
        <version>3.0.0</version>
    </dependency>
</dependencies>

1.4. 乾貨

itext7語義本身和前端css很像,所以有點前端基礎還是比較容易掌握的

1.4.1. 添加圖片

  1. 讀取項目中圖片文件
  2. 設置邊距
  3. 設置寬高擴大縮小
Image indexImage = new Image(ImageDataFactory.create(GenoReportBuilder.class.getClassLoader().getResource("image/gene.png")));
indexImage.setMargins(-50, -60, -60, -60);
indexImage.scale(1, 1.05f);

1.4.2. 添加指定空白頁

  1. 添加第2頁為空白頁,立即刷新后再繼續添加
pdf.addNewPage(2).flush();

1.4.3. Div、Paragraph

    Div div = new Div();
    div.setWidth(UnitValue.createPercentValue(100));
    div.setHeight(UnitValue.createPercentValue(100));
    div.setHorizontalAlignment(HorizontalAlignment.CENTER);
    Paragraph p1 = new Paragraph();
    p1.setHorizontalAlignment(HorizontalAlignment.CENTER);
    p1.setMaxWidth(UnitValue.createPercentValue(75));
    p1.setMarginTop(180f);
    p1.setCharacterSpacing(0.4f);
    Style large = new Style();
    large.setFontSize(22);
    large.setFontColor(GenoColor.getThemeColor());
    p1.add(new Text("尊敬的 ").addStyle(large));
    ...
    Paragraph p2 = new Paragraph();
    ...
    div.add(p1);
    div.add(p2);
  1. 整塊的內容用Div包裹,這裏整塊包裹的好處是什麼?一方面排版分明成體系,另一方面若需求是整塊的內容必須在同一個版面,你可以對Div設置div.setKeepTogether(true);,盡量保證若整塊的內容超出了一頁,那這塊內容會自動整塊出現在下一頁,上一頁剩下的就留白了
  2. 可以看到Div,Paragraph可以設置很多屬性,實際上我們常用的組件除了這兩種,還有Table,Cell,List,他們大部分的屬性都是一樣的,只是部分屬性只在部分組件起效果,所以當你設置某個屬性沒起效果也不用奇怪
  3. Paragraph需要特別注意的一點,想要段落文字居中,不要用setHorizontalAlignment(HorizontalAlignment.CENTER);這是組件的居中對段落無效,甚至對段落里你放Text也無效,需要改用setTextAlignment(TextAlignment.CENTER);
  4. Paragraph段落的行距也是個高頻問題,這裏給出官方我看到的解釋,參考https://itextpdf.com/en/resources/books/itext-7-building-blocks/chapter-4-adding-abstractelement-objects-part-1,搜關鍵字setFixedLeading,我的理解該方法設值行高絕對值,官方解釋是兩行文字中間基線之間的距離
  5. 如果想了解詳細的什麼屬性哪裡能起作用哪裡不行,請訪問該地址

1.4.4. Table

  1. useAllAvailableWidth表示頁面有多寬,我就有多寬
  2. table.startNewRow();表示新起一行,table每畫一行都要新起一行
  3. 同樣table內容需要居中,和段落一樣,請設置new Cell().setTextAlignment(TextAlignment.CENTER)
  4. 每個table中cell都有默認高度,會比實際輸入字體高些,此時設置setHeight,若更大沒有問題,若高度小於或接近字體大小文字可能就消失了,若想讓Cell高度更接近文字高度,請設置Cell的padding,即cell.setPadding(-2),設置負值即可

1.4.5. Tab,\t

  1. itext7中如果要表示段落前的空格,不能使用\t,但換行可以使用\n

  2. 若要實現Tab效果可以有多個方法

    1. \u00a0符號,大概7、8個該符號可表示tab,可能不是很準確
    p1.add(new Text("\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0壹基因衷心祝願您身體健康、享受品質生活!"));
    
    1. p1.setFirstLineIndent(24),表示段落前留多少空,需要知道一個字多大,設置成兩倍就行
    2. Tab也是集成AbstractElement的組件,通過以下方式也可實現相同的效果
      p2.add(new Tab());
      p2.addTabStops(new TabStop(20, TabAlignment.LEFT));
    

1.4.6. 換頁

我常用的換頁方法為如下,該方法可保證立即換頁

doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));

當然PdfDocument有addNewPage其實也可以用,但有時候你沒把握好刷新時間可能導致某些混亂

1.4.7. 畫圖或畫文字

能畫出多麼複雜的圖形看是誰畫了,在我的PDF中,我畫的最複雜的圖形如下

該圖形由多個弧形區域加線段加文字組成,包括数字上的小箭頭也是畫出來的,畫這個的代碼過多,想要了解詳細的可以自行下載研究,這裏介紹API功能

  1. lineTo畫線段
  2. roundRectangle可用來畫角是弧形的方形,也可以用來畫圓
  3. showText用來畫文字

以上幾種結合填充即可把三角形,多邊形畫出來了

    PdfPage page = pdf.getPage(pdf.getNumberOfPages());
    pageSize = pdf.getDefaultPageSize();
    PdfCanvas pdfCanvas = new PdfCanvas(page);

    pdfCanvas.saveState().moveTo(pageSize.getWidth() / 2 - 100 + i * 40, yOffset - 203)
                    .lineTo(pageSize.getWidth() / 2 - 100 + i * 40, yOffset - 208)
                    .stroke().restoreState();

    pdfCanvas.setLineWidth(2);
        pdfCanvas.setStrokeColor(color);
        pdfCanvas.roundRectangle(pageSize.getWidth() / 2 - 3 + posXOffset, yOffset - 188, 6, 6, 3)
                .stroke();

    pdfCanvas.beginText()
                .setFontAndSize(font, 12)
                .moveText(pageSize.getWidth() / 2 - text.length() * 12 / 2, yOffset - 45);
        pdfCanvas.showText(text);
        pdfCanvas.endText();

1.4.8. Html段落轉Pdf段落

我們可能遇到把一段Html文本轉換成itext7的段落放進來,此時需要用到它的htmlToPdf模塊,該模塊對應POM

    <!--itext7 html轉pdf用到的包-->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>html2pdf</artifactId>
        <version>3.0.0</version>
    </dependency>

至於使用,設置好配置屬性,使用也很簡單,通常我們需要支持中文,所有配置如下,字體可以自己換

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

    ConverterProperties proper = new ConverterProperties();
    //字體設置,解決中文不显示問題
    FontSet fontSet = new FontSet();
    fontSet.addFont(GenoReportBuilder.class.getClassLoader().getResource("font/SourceHanSansCN-Regular.ttf").getPath(), PdfEncodings.IDENTITY_H);

    FontProvider fontProvider = new FontProvider(fontSet);
    proper.setFontProvider(fontProvider);

    String content = "html內容";
    List<IElement> elements = HtmlConverter.convertToElements(content, proper);

轉換的內容是IElement集合,而IElement是什麼呢?給張圖就了解了

也就是說只要你的html內容是<div></div>包裹的,你直接把元素轉成itext7的Div然後add到document就可以實現html內容的添加了,當然你也可以用instanceof判斷不同內容不同處理

如下是我的處理例子供參考,我把輸入html內容樣式進行了一定修改後轉成itext7組件,這裏特別提心,html轉過來的itext7組件可能會不支持部分樣式的修改,所以需要在html中進行css樣式的添加,這裏我就把字體和高度統一用css設值了

    Div overall = new Div();
    java.util.List<IElement> iElements = getFixContent(value);
    for (IElement iElement : iElements) {
        Style style = new Style();
        style.setFontSize(10);
        style.setCharacterSpacing(0.7f);
        if (iElement instanceof Div) {
            Div div = (Div) iElement;
            java.util.List<IElement> children = div.getChildren();
            // 全部段落改成相同樣式
            this.addParagraphStyleCircle(style, children);
            overall.add(div);
        } else if (iElement instanceof Paragraph) {
            Paragraph element = (Paragraph) iElement;
            overall.add(element.addStyle(style));
        }
    }
    doc.add(overall);
  • getFixContent
    private java.util.List<IElement> getFixContent(String content) {
        if (content.startsWith("<div>")) {
            content = content.replaceAll("<div>", "<div style='line-height:18pt;font-size:16px;'>");
        } else {
            content = "<div style='line-height:18pt;font-size:16px;'>" + content + "</div>";
        }
        return HtmlConverter.convertToElements(content, proper);
    }
  • addParagraphStyleCircle
    private void addParagraphStyleCircle(Style style, java.util.List<IElement> children) {
        for (IElement child : children) {
            if (child instanceof Paragraph) {
                Paragraph element = (Paragraph) child;
                element.addStyle(style);
                java.util.List<IElement> children1 = element.getChildren();
                this.addParagraphStyleCircle(style, children1);
            }
            if (child instanceof Div) {
                Div div = (Div) child;
                java.util.List<IElement> children1 = div.getChildren();
                this.addParagraphStyleCircle(style, children1);
            }
            if (child instanceof Text) {
                Text text = (Text) child;
                text.addStyle(style);
            }
        }
    }

1.4.9. 監聽事件

在編寫pdf的時候,比如一篇整體的文章,我們需要在頁眉位置添加關於這篇文章的固定文本或者圖形,類似於打個標籤,表示你翻了這麼多頁一直在看這篇文章,當第二篇文章的時候就換一個,舉個例子

  • 第一頁
  • 第二頁

這種需求我們如何實現呢?思路分析發現,我們需要知道什麼時候文章內容一頁寫不起了,換了一頁的時候我們需要添加一個同樣的頁眉。這樣我們就需要知道頁是何時添加的,監聽事件就是處理這種問題的

  • pdf是PdfDocument,可添加的事件有START_PAGE,INSERT_PAGE,REMOVE_PAGE,END_PAGE共四個,如上需求我們需要監聽START_PAGE事件,在事件處理中做相應的處理,我在事件中使用PdfCanvas畫了頭部內容
HeaderTextEvent headerTextEvent = new HeaderTextEvent(title, font);
pdf.addEventHandler(PdfDocumentEvent.START_PAGE, headerTextEvent);
  • HeaderTextEvent類,Painting僅僅是封裝了PdfCanvas
public class HeaderTextEvent implements IEventHandler {

    private String text;
    private PdfFont font;

    public HeaderTextEvent(String text,PdfFont font) {
        this.text = text;
        this.font = font;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        Painting painting = new Painting(pdfDoc, font);
        painting.drawHeader();
        painting.drawHeaderText(text);
        painting.close();
    }
}

在添加內容前添加相應事件,同時需要記得在不需要的時候移除

// 移除監聽器
pdf.removeEventHandler(PdfDocumentEvent.START_PAGE, headerTextEvent);

1.4.10. 添加目錄

我沒有找到itext7原生是否有目錄添加,根據我自己的需求,我用Table組件來實現了自定義目錄,由於我的PDF是用來打印的,所以我並沒有給目錄添加Link,也就是頁面跳轉,不過當你徹底理解了我的項目,我想這個需求實現也不難

  • 實現效果如下,隨着內容的增長,目錄自動增長

先說下遇到的困難,目錄顧明思意,必須要有內容才會有目錄,所以實際上目錄是最後添加的,但如果我們添加內容到最後再跳轉到前面的頁面來添加目錄,有三個問題:

  1. 目錄有幾頁如何知道?
  2. 目錄有幾頁不知道,如何知道內容在第幾頁?
  3. 由於目錄不確定,所以後續內容的頁碼其實也是不確定的,也就是說頁碼也不是一頁頁可以添加過去的

而經過實踐你會發現,我們不能夠回到前幾頁去修改已存在的頁面,因為會提示你已經flush了,不能修改。

這時我看到了movePage這個方法,也就是可以通過移動頁面,把目錄在內容之後生成,后再移動到前幾頁,但是頁碼還是不能修改,發現腦袋不夠想了只能用上屁股,靈光一閃,不能一遍生成為什麼不能二次渲染呢?於是研究讀取原pdf在原pdf上修改,二次渲染的時候填上頁碼及移動頁面,主要代碼如下,包括了讀取中間文件,移動目錄,添加每頁頁碼

PdfReader reader = null;
PdfWriter writer = null;
String inPath = getInPath();
try {
    reader = new PdfReader(new File(inPath));
    writer = new PdfWriter(new File(outPath));
} catch (IOException e) {
    e.printStackTrace();
}
PdfDocument pdf = new PdfDocument(reader, writer);
Document doc = new Document(pdf);
int startPage = 7;
int numberOfPages = pdf.getNumberOfPages();
for (int i = 0; i < catalogSize; i++) {
    pdf.movePage(numberOfPages, startPage);
}
String forbidPage = properties.getProperty("forbidPage");
for (int pageNumber = 1; pageNumber < numberOfPages + 1; pageNumber++) {

    if (pageNumber > 6 + catalogSize && pageNumber != 8 + catalogSize) {
        if (forbidPage != null && (pageNumber - catalogSize) >= Integer.parseInt(forbidPage)) {
            continue;
        }
        PageSize pageSize = pdf.getDefaultPageSize();
        doc.showTextAligned(new Paragraph(String.format("- %d -", pageNumber)), pageSize.getWidth() / 2, 30, pageNumber, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
    }
}

1.5. 總結

經過上述總結,我基本上把項目中的大多基本點和難點都概括進去了,初次用itext7寫PDF的同學基本會遇到的問題基本都在上述這些,不理解的就把項目下下來運行Main方法慢慢調試,理解透我這個項目,還有其它問題那基本只能翻官網了

項目Github: https://github.com/tzxylao/onegeno-itext-pdf
itext7官網:https://itextpdf.com/

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

好站推薦

  • 健康醫療 減重知識專區
  • 婚紗世界 婚紗攝影寫真網
  • 成人話題 未滿18請勿進入
  • 流行時尚 時下流行愛美情報
  • 理財資訊 當舖借貸信用卡各式理財方法
  • 生活情報 各行各業情報資訊
  • 科技資訊 工業電子3C產品
  • 網路資訊 新奇趣味爆笑內容
  • 美食分享 全台各式名產 伴手禮
  • 裝潢設計 買屋賣屋裝修一羅框
  • 視覺設計 T恤、團體服、制服、polo衫

近期文章

  • 水磨石、弧形元素,打造30坪質感混搭風格居所
  • 25坪獨享開放式中島!
  • 住宅大翻修,提升機能收納、創造明亮通透感
  • 你家浴缸的設計關乎財運
  • 如何用玻璃隔間化解風水煞氣?

標籤

USB CONNECTOR  到府月嫂 南投搬家公司費用 古典家具推薦 台中室內設計 台中搬家 台中搬家公司 台中電動車 台北網頁設計 台東伴手禮 台東名產 地板施工 大圖輸出 如何寫文案 婚禮錄影 宜蘭民宿 家具工廠推薦 家具訂製工廠推薦 家具訂製推薦 實木地板 床墊 復刻家具推薦 新竹婚宴會館 木地板 木質地板 柚木地板 桃園機場接送 桃園自助婚紗 沙發修理 沙發換皮 海島型木地板 潭子電動車 牛軋糖 租車 網站設計 網頁設計 網頁設計公司 貨運 超耐磨木地板 銷售文案 隱形鐵窗 電動車 馬賽克拼貼 馬賽克磁磚 馬賽克磚

彙整

  • 2022 年 5 月
  • 2022 年 4 月
  • 2022 年 3 月
  • 2022 年 2 月
  • 2022 年 1 月
  • 2021 年 12 月
  • 2021 年 11 月
  • 2021 年 10 月
  • 2021 年 9 月
  • 2021 年 8 月
  • 2021 年 7 月
  • 2021 年 6 月
  • 2021 年 5 月
  • 2021 年 4 月
  • 2021 年 3 月
  • 2021 年 2 月
  • 2021 年 1 月
  • 2020 年 12 月
  • 2020 年 11 月
  • 2020 年 10 月
  • 2020 年 9 月
  • 2020 年 8 月
  • 2020 年 7 月
  • 2020 年 6 月
  • 2020 年 5 月
  • 2020 年 4 月
  • 2020 年 3 月
  • 2020 年 2 月
  • 2020 年 1 月
  • 2019 年 12 月
  • 2019 年 11 月
  • 2019 年 10 月
  • 2019 年 9 月
  • 2019 年 8 月
  • 2019 年 7 月
  • 2019 年 6 月
  • 2019 年 5 月
  • 2019 年 4 月
  • 2019 年 3 月
  • 2019 年 2 月
  • 2019 年 1 月
  • 2018 年 12 月
©2022 值得您信賴的旅遊品牌 | 團體旅遊、自由行的專家‎ | Built using WordPress and Responsive Blogily theme by Superb