亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

實(shí)際效果

填充數(shù)據(jù)后效果

實(shí)現(xiàn)代碼

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

public class Demo01 {
    public static void main(String[] args) throws Exception{
        // 設(shè)定中文字體
        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);

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

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

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

                    // 結(jié)尾結(jié)構(gòu)渲染
                    // 合計(jì)位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 150); // 80,150
                    contentStream.showText("合計(jì)");
                    contentStream.endText();
                    // 出庫(kù)數(shù)量和總金額位置
                    contentStream.beginText();
                    contentStream.newLineAtOffset(110, 125); // 110,125
                    contentStream.showText("出庫(kù)數(shù)量:");
                    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("倉(cāng)庫(kù)", "上海倉(cāng)");
                    map.put("會(huì)員", "小明");
                    map.put("銷售員", "銷售員01");
                    map.put("操作人", "系統(tǒng)管理員");
                    map.put("操作時(shí)間", "2025年4月1日23點(diǎn)07分");
                    map.put("出庫(kù)數(shù)量", "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);
        }
    }


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

            contentStream.beginText();
            contentStream.newLineAtOffset(430, 700);
            contentStream.showText(map.get("會(huì)員"));
            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("操作時(shí)間"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(180, 125);
            contentStream.showText(map.get("出庫(kù)數(shù)量"));
            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("序號(hào)"));
            contentStream.endText();

            contentStream.beginText();
            contentStream.newLineAtOffset(120, 600);
            contentStream.showText(map.get("商品編號(hào)"));
            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("借出數(shù)量"));
            contentStream.endText();

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

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

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

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

PDF文件本質(zhì)是「坐標(biāo)畫布」‌

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

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

所以如果你有這樣的需求可以看下上述代碼實(shí)現(xiàn),上述代碼只是一個(gè)簡(jiǎn)單的demo,我只是進(jìn)行記錄方便自己以后用到。

tips:關(guān)于一些方法的解釋

                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 700); // 80,700  --絕對(duì)定位
                    contentStream.showText("倉(cāng)庫(kù):");
                    contentStream.newLineAtOffset(300, 0); //380,700  --相對(duì)定位(以'倉(cāng)庫(kù):'的位置為準(zhǔn))
                    contentStream.showText("會(huì)員:");
                    contentStream.endText();
                    
                    contentStream.beginText();
                    contentStream.newLineAtOffset(80, 675); // 80,675  --絕對(duì)定位
                    contentStream.showText("銷售員:");
                    contentStream.newLineAtOffset(300, 0); //380,675  --相對(duì)定位(以'銷售員'的位置為準(zhǔn))
                    contentStream.showText("操作人:");
                    contentStream.endText();

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

newLineAtOffset(x, y)方法的官方注釋有問(wèn)題,官方說(shuō)法是移動(dòng)到下一行的開頭,從當(dāng)前行的開頭進(jìn)行偏移 (x, y),實(shí)測(cè)不對(duì),并不會(huì)移動(dòng)到下一行的開頭,并且在相對(duì)定位時(shí)參考的位置也是你上一次的位置的起始點(diǎn)。

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

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

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

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

相關(guān)文章

  • Java SSM框架如何配置靜態(tài)資源加載

    Java SSM框架如何配置靜態(tài)資源加載

    這篇文章主要介紹了Java SSM框架如何配置靜態(tài)資源加載,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 十五道tomcat面試題,為數(shù)不多的機(jī)會(huì)!

    十五道tomcat面試題,為數(shù)不多的機(jī)會(huì)!

    這篇文章主要介紹了十五道tomcat面試題,Tomcat的本質(zhì)是一個(gè)Servlet容器。一個(gè)Servlet能做的事情是:處理請(qǐng)求資源,并為客戶端填充response對(duì)象,需要的朋友可以參考下
    2021-08-08
  • 基于Java編寫一個(gè)實(shí)用的ExcelUtil工具類

    基于Java編寫一個(gè)實(shí)用的ExcelUtil工具類

    在項(xiàng)目中經(jīng)常遇到excel表格導(dǎo)入導(dǎo)出功能,每次都要重復(fù)寫有關(guān)excel 的邏輯,所以本文直接使用Java編寫一個(gè)實(shí)用的ExcelUtil工具類,希望對(duì)大家有所幫助
    2024-04-04
  • Java中Date、LocalDate、LocalDateTime、LocalTime、時(shí)間戳之間的相互轉(zhuǎn)換代碼

    Java中Date、LocalDate、LocalDateTime、LocalTime、時(shí)間戳之間的相互轉(zhuǎn)換代碼

    這篇文章主要介紹了Java中日期時(shí)間轉(zhuǎn)換的多種方法,包括將Date轉(zhuǎn)換為L(zhǎng)ocalDateTime、LocalDate等,以及將時(shí)間戳轉(zhuǎn)換為L(zhǎng)ocalDateTime,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • SpringBoot 使用 OpenAPI3 規(guī)范整合 knife4j的詳細(xì)過(guò)程

    SpringBoot 使用 OpenAPI3 規(guī)范整合 knife4j的詳細(xì)過(guò)程

    Swagger工具集使用OpenAPI規(guī)范,可以生成、展示和測(cè)試基于OpenAPI規(guī)范的API文檔,并提供了生成客戶端代碼的功能,本文給大家介紹SpringBoot使用OpenAPI3規(guī)范整合knife4j的詳細(xì)過(guò)程,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • Spring Boot優(yōu)雅使用RocketMQ的方法實(shí)例

    Spring Boot優(yōu)雅使用RocketMQ的方法實(shí)例

    這篇文章主要給大家介紹了關(guān)于Spring Boot優(yōu)雅使用RocketMQ的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 詳解Vue響應(yīng)式的部分實(shí)現(xiàn)

    詳解Vue響應(yīng)式的部分實(shí)現(xiàn)

    響應(yīng)式,簡(jiǎn)單來(lái)說(shuō)當(dāng)數(shù)據(jù)發(fā)生變化時(shí),對(duì)數(shù)據(jù)有依賴的代碼會(huì)重新執(zhí)行。這篇文章主要為大家介紹了Vue中響應(yīng)式的部分實(shí)現(xiàn),感興趣的可以了解一下
    2022-12-12
  • 教你用Java GUI實(shí)現(xiàn)文本文件的讀寫

    教你用Java GUI實(shí)現(xiàn)文本文件的讀寫

    今天帶大家來(lái)學(xué)習(xí)怎么用JavaSwing實(shí)現(xiàn)實(shí)現(xiàn)文本文件讀寫,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • java鍵盤錄入的方法舉例詳解

    java鍵盤錄入的方法舉例詳解

    這篇文章主要給大家介紹了關(guān)于java鍵盤錄入的相關(guān)資料,我們?cè)趯懗绦虻臅r(shí)候,數(shù)據(jù)值都是固定的,但是實(shí)際開發(fā)中,數(shù)據(jù)值肯定是變化的,所以,把數(shù)據(jù)改進(jìn)為鍵盤錄入,提高程序的靈活性,需要的朋友可以參考下
    2023-10-10
  • springboot?實(shí)現(xiàn)動(dòng)態(tài)刷新配置的詳細(xì)過(guò)程

    springboot?實(shí)現(xiàn)動(dòng)態(tài)刷新配置的詳細(xì)過(guò)程

    這篇文章主要介紹了springboot實(shí)現(xiàn)動(dòng)態(tài)刷新配置,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05

最新評(píng)論