最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java使用PDFBox渲染生成pdf文檔的代碼詳解

 更新時間:2025年04月07日 10:23:45   作者:SYKMI  
使用PDFBox可以渲染生成pdf文檔,并且自定義程度高,只是比較麻煩,這篇文章將為大家詳細介紹一下具體的實現方法,感興趣的小伙伴可以參考一下

使用PDFBox可以渲染生成pdf文檔,并且自定義程度高,只是比較麻煩,pdf的內容位置都需要手動設置x(橫向)和y(縱向)絕對位置,但是每個企業(yè)的單據都是不一樣的,一般來說都會設置一個模板,然后內容再填充到適當位置,所以這個功能還是有用的

實際效果

填充數據后效果

實現代碼

以下代碼基于PDFBox依賴版本-2.0.23

public class Demo01 {
    public static void main(String[] args) throws Exception{
        // 設定中文字體
        File fontFile = new File("C:\\Windows\\Fonts\\simHei.ttf");

        try (PDDocument document = new PDDocument()) {
            PDType0Font load = PDType0Font.load(document, fontFile);

            PDPage page;
            for (int i = 0; i < 1; i++) {
                page = new PDPage();
                document.addPage(page);

                // 對具體PDPage設定內容
                try(PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
                    contentStream.setFont(load, 25);
                    contentStream.beginText();
                    // newLineAtOffset方法
                    contentStream.newLineAtOffset(220, 750);
                    contentStream.showText("借用出庫打印單");
                    contentStream.setFont(load, 12);
                    contentStream.endText();
                    // 倉庫和會員渲染位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 700); // 80,700
                    contentStream.showText("倉庫:");
                    contentStream.newLineAtOffset(300, 0); //380,700
                    contentStream.showText("會員:");
                    contentStream.endText();
                    // 銷售員和操作人渲染位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 675); // 80,675
                    contentStream.showText("銷售員:");
                    contentStream.newLineAtOffset(300, 0); //380,675
                    contentStream.showText("操作人:");
                    contentStream.endText();
                    // 操作時間位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 650); // 80,650
                    contentStream.showText("操作時間:");
                    contentStream.endText();

                    // ----------------實際內容-----------------------
                    // 表頭
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 625); //80,625
                    contentStream.showText("序號");
                    contentStream.newLineAtOffset(40, 0); //120,625
                    contentStream.showText("商品編號");
                    contentStream.newLineAtOffset(80, 0); //200,625
                    contentStream.showText("商品名稱");
                    contentStream.newLineAtOffset(70, 0); //270,625
                    contentStream.showText("單位");
                    contentStream.newLineAtOffset(40, 0); //310,625
                    contentStream.showText("借出數量");
                    contentStream.newLineAtOffset(70, 0); //380,625
                    contentStream.showText("備注");
                    contentStream.newLineAtOffset(100, 0); //480,625
                    contentStream.showText("零售價");
                    contentStream.endText();

                    Map<String, String> contentMap = new HashMap<>();
                    contentMap.put("序號", "1");
                    contentMap.put("商品編號", "000212130023");
                    contentMap.put("商品名稱", "洗地機124123");
                    contentMap.put("單位", "個");
                    contentMap.put("借出數量", "13");
                    contentMap.put("備注", "我是備注我是備注");
                    contentMap.put("零售價", "1123300.34");
                    fillContent(contentStream, contentMap, load);

                    // 結尾結構渲染
                    // 合計位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 150); // 80,150
                    contentStream.showText("合計");
                    contentStream.endText();
                    // 出庫數量和總金額位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(110, 125); // 110,125
                    contentStream.showText("出庫數量:");
                    contentStream.newLineAtOffset(270, 0); // 380,125
                    contentStream.showText("總金額:");
                    contentStream.endText();
                    // 簽名位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 50); // 110,125
                    contentStream.showText("簽名:_______");
                    contentStream.endText();

                    // 模擬填充模板
                    Map<String, String> map = new HashMap<>();
                    map.put("倉庫", "上海倉");
                    map.put("會員", "小明");
                    map.put("銷售員", "銷售員01");
                    map.put("操作人", "系統(tǒng)管理員");
                    map.put("操作時間", "2025年4月1日23點07分");
                    map.put("出庫數量", "1455");
                    map.put("總金額", "285743835.45");
                    fillTemplate(contentStream, map);
                }
            }

            document.save("demo01.pdf");
            System.out.println("PDF created successfully!");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    // 填充固定模板方法 該方法不填充中間詳細內容
    public static void fillTemplate(PDPageContentStream contentStream, Map<String, String> map) {
        try {
            contentStream.beginText();
            contentStream.newLineAtOffset(130, 700);
            contentStream.showText(map.get("倉庫"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(430, 700);
            contentStream.showText(map.get("會員"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(130, 675);
            contentStream.showText(map.get("銷售員"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(430, 675);
            contentStream.showText(map.get("操作人"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(150, 650);
            contentStream.showText(map.get("操作時間"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(180, 125);
            contentStream.showText(map.get("出庫數量"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(430, 125);
            contentStream.showText(map.get("總金額"));
            contentStream.endText();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void fillContent(PDPageContentStream contentStream, Map<String, String> map, PDType0Font font) {
        try {
            contentStream.setFont(font, 10);

            contentStream.beginText();
            contentStream.newLineAtOffset(80, 600);
            contentStream.showText(map.get("序號"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(120, 600);
            contentStream.showText(map.get("商品編號"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(200, 600);
            contentStream.showText(map.get("商品名稱"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(270, 600);
            contentStream.showText(map.get("單位"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(310, 600);
            contentStream.showText(map.get("借出數量"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(380, 600);
            contentStream.showText(map.get("備注"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(480, 600);
            contentStream.showText(map.get("零售價"));
            contentStream.endText();

            contentStream.setFont(font, 12);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

上述代碼看著確實是挺繁瑣,每個內容的位置都需要設置x和y值,但是沒辦法

PDF文件本質是「坐標畫布」‌

  • PDF的渲染模型基于‌絕對坐標系‌(原點在頁面左下角),所有元素(文字、圖形)必須明確指定位置(x,y)。
  • 無布局引擎‌:PDF規(guī)范未定義“自動換行”或“文檔流”等高級排版概念,開發(fā)者需自行計算坐標。

但是這樣設計的好處就是自定義程度高,你可以任意設計一個PDF文檔的模板應該是什么樣子,內容該如何填充全部由你自由設定,就像低代碼平臺一樣,市面上成熟開源的低代碼平臺有許多,但是邏輯都是一開始就定好的,如果你想加上許多符合自己公司需求的功能但是平臺沒有那么都得自行開發(fā),并且自行開發(fā)的代碼融合進已有的系統(tǒng)不是一件容易的事情,甚至比自行開發(fā)一套系統(tǒng)都麻煩。

所以如果你有這樣的需求可以看下上述代碼實現,上述代碼只是一個簡單的demo,我只是進行記錄方便自己以后用到。

tips:關于一些方法的解釋

                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 700); // 80,700  --絕對定位
                    contentStream.showText("倉庫:");
                    contentStream.newLineAtOffset(300, 0); //380,700  --相對定位(以'倉庫:'的位置為準)
                    contentStream.showText("會員:");
                    contentStream.endText();
                    
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 675); // 80,675  --絕對定位
                    contentStream.showText("銷售員:");
                    contentStream.newLineAtOffset(300, 0); //380,675  --相對定位(以'銷售員'的位置為準)
                    contentStream.showText("操作人:");
                    contentStream.endText();

上述代碼可以看到在渲染內容時是被包裹在beginText()和endText()方法中間的,這樣當你調用newLineAtOffset(x, y)方法時參數中的x和y才從坐標系的絕對位置(絕對位置為畫布的左下角0,0)進行定位。如果你在定位時沒有重新開啟beginText()和endText()時,調用newLineAtOffset(x, y)方法則是參照上一個文本的位置進行相對定位的,相對定位對于需要在同一行的不同位置渲染內容會比較方便。

newLineAtOffset(x, y)方法的官方注釋有問題,官方說法是移動到下一行的開頭,從當前行的開頭進行偏移 (x, y),實測不對,并不會移動到下一行的開頭,并且在相對定位時參考的位置也是你上一次的位置的起始點。

如果你需要像寫文章那樣一段一段的文字進行渲染,那么可以考慮使用另外一個方法

                     contentStream.beginText();
                     contentStream.newLineAtOffset(80, 500); // 設定絕對位置的起點
                    contentStream.setLeading(20); // 文本行距
                    contentStream.showText("XXXXX"); //渲染內容
                    contentStream.newLine(); //開啟新行
                    contentStream.showText("XXXXX"); //渲染內容
                    contentStream.newLine(); //開啟新行
                    contentStream.showText("XXXXX"); //渲染內容
                    contentStream.newLine(); //開啟新行
                    contentStream.endText();

這個方法更適合大段連貫的文字渲染,你只要設定好固定行距之后就可以直接開啟新行,新行的位置會成功進入到下一行的開頭并且行距就是你設定的值,這樣你就不用每次都自行定位了,效果如下

到此這篇關于Java使用PDFBox渲染生成pdf文檔的代碼詳解的文章就介紹到這了,更多相關Java PDFBox渲染pdf內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 如何寫好一個Spring組件的實現步驟

    如何寫好一個Spring組件的實現步驟

    這篇文章主要介紹了如何寫好一個Spring組件的實現步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • SpringBoot與docker的結合的示例

    SpringBoot與docker的結合的示例

    本篇文章主要介紹了SpringBoot與docker的結合的示例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 關于Java語法糖以及語法糖的原理和用法

    關于Java語法糖以及語法糖的原理和用法

    這篇文章主要介紹了關于Java什么是語法糖以及語法糖的種類,也稱糖衣語法,是由英國計算機學家?Peter.J.Landin?發(fā)明的一個術語,指在計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用,需要的朋友可以參考下
    2023-05-05
  • Java動態(tài)循環(huán)隊列是如何實現的

    Java動態(tài)循環(huán)隊列是如何實現的

    今天帶大家學習java隊列的相關知識,文章圍繞著如何實現Java動態(tài)循環(huán)隊列展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • java多線程join()方法的作用和實現原理解析(應用場景)

    java多線程join()方法的作用和實現原理解析(應用場景)

    join方法主要是用于將當前線程掛起,等待其他線程結束后在執(zhí)行當前線程,本文通過應用場景分析代碼示例講解java多線程join()方法的作用和實現原理,感興趣的朋友一起看看吧
    2021-07-07
  • Spring Boot + Jpa(Hibernate) 架構基本配置詳解

    Spring Boot + Jpa(Hibernate) 架構基本配置詳解

    本篇文章主要介紹了Spring Boot + Jpa(Hibernate) 架構基本配置詳解,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • lombok @Accessors用法詳解

    lombok @Accessors用法詳解

    這篇文章主要介紹了lombok @Accessors用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • Java中==與equals的區(qū)別小結

    Java中==與equals的區(qū)別小結

    這篇文章主要介紹了Java中==與equals的區(qū)別小結,本文總結結論:== 與 equals()比較的內容是不同的,equals()方式是String類中的方法,它用于比較兩個對象引用所指的內容是否相等,而 == 比較的是兩個對象引用的地址是否相等,需要的朋友可以參考下
    2015-06-06
  • 淺談Java中幾種常見的比較器的實現方法

    淺談Java中幾種常見的比較器的實現方法

    下面小編就為大家?guī)硪黄獪\談Java中幾種常見的比較器的實現方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Java虛擬機(JVM)的自帶工具使用詳解

    Java虛擬機(JVM)的自帶工具使用詳解

    文章介紹了Java開發(fā)中常用的幾個工具及其基本用法,包括jps、jstat、jinfo、jmap、jhat和jstack,這些工具幫助開發(fā)者監(jiān)控和分析Java應用程序的性能和內存使用情況,對于排查問題和優(yōu)化性能非常有幫助
    2026-01-01

最新評論

遂平县| 怀化市| 高密市| 和田县| 灵川县| 太康县| 绍兴市| 深圳市| 喀喇| 会昌县| 南昌县| 运城市| 谷城县| 张家港市| 龙胜| 庆元县| 枣阳市| 中牟县| 五台县| 江油市| 普洱| 石景山区| 塔城市| 嘉荫县| 临洮县| 博客| 伊通| 龙口市| 仁化县| 化隆| 涞源县| 伊春市| 灵石县| 乐都县| 常山县| 新安县| 淅川县| 安平县| 乡宁县| 兴宁市| 祥云县|