帶有docx4j的Java Word(.docx)文檔

幾個月前,我需要創建一個包含許多表和段落的動態Word文檔。 過去,我曾使用POI來實現此目的,但是我發現它很難使用,并且在創建更復雜的文檔時對我來說效果不佳。 因此,對于這個項目,經過一番搜索,我決定使用docx4j 。 Docx4j,根據他們的網站是:

“ docx4j是一個Java庫,用于創建和處理Microsoft Open XML(Word docx,Powerpoint pptx和Excel xlsx)文件。
它類似于Microsoft的OpenXML SDK,但適用于Java。

在本文中,我將向您展示幾個示例,您可以使用這些示例來生成Word文檔的內容。 更具體地說,我們將看以下兩個示例:

  • 加載模板Word文檔以添加內容并另存為新文檔
  • 將段落添加到此模板文檔
  • 將表添加到此模板文檔

這里的一般方法是首先創建一個Word文檔,其中包含最終文檔的布局和主要樣式。 在本文檔中,您將需要添加占位符(簡單字符串),我們將使用這些占位符來搜索并替換為真實內容。

例如,一個非常基本的模板如下所示:

在本文中,我們將向您展示如何填寫此內容,以便獲得:

加載模板Word文檔以添加內容并另存為新文檔

首先是第一件事。 讓我們創建一個簡單的Word文檔,將其用作模板。 為此,只需打開Word,創建一個新文檔并將其另存為template.docx。 這是我們用來向其添加內容的單詞模板。 我們需要做的第一件事是用docx4j加載該文檔。 您可以使用以下一段Java代碼:

private WordprocessingMLPackage getTemplate(String name) throws Docx4JException, FileNotFoundException {WordprocessingMLPackage template = WordprocessingMLPackage.load(new FileInputStream(new File(name)));return template;}

這將返回一個Java對象,該對象表示完整的(此時)空文檔。 現在,我們可以使用Docx4J API在此Word文檔中添加,刪除和修改內容。 Docx4J有許多幫助程序類,可用于遍歷此文檔。 我確實寫了一些幫助程序,盡管它們確實使查找特定的占位符并將其替換為實際內容變得非常容易。 讓我們看看其中之一。 該操作是對幾個JAXB操作的包裝,使您可以搜索特定元素及其所有子元素來查找某個類。 例如,您可以使用它來獲取文檔中的所有表,表中的所有行等等。

private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {List<Object> result = new ArrayList<Object>();if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();if (obj.getClass().equals(toSearch))result.add(obj);else if (obj instanceof ContentAccessor) {List<?> children = ((ContentAccessor) obj).getContent();for (Object child : children) {result.addAll(getAllElementFromObject(child, toSearch));}}return result;}

沒什么復雜的,但真的很有幫助。 讓我們看看如何使用此操作。 對于此示例,我們將使用不同的值替換一個簡單的文本占位符。 例如,這是您用來動態設置文檔標題的內容。 不過,首先,在您創建的Word模板中添加一個自定義占位符。 我將為此使用SJ_EX1。 我們將用我們的名字替換這個值。 docx4j中的基本文本元素由org.docx4j.wml.Text類表示。 要替換此簡單的占位符,我們要做的就是調用此方法:

private void replacePlaceholder(WordprocessingMLPackage template, String name, String placeholder ) {List<Object> texts = getAllElementFromObject(template.getMainDocumentPart(), Text.class);for (Object text : texts) {Text textElement = (Text) text;if (textElement.getValue().equals(placeholder)) {textElement.setValue(name);}}}

這將查找文檔中的所有Text元素,并將匹配的元素替換為我們指定的值。 現在,我們要做的就是將文檔寫回到文件中。

private void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException {File f = new File(target);template.save(f);}

如您所見,并不難。

通過此設置,我們還可以將更復雜的內容添加到Word文檔中。 確定如何添加特定內容的最簡單方法是查看word文檔的XML源代碼。 這將告訴您需要哪些包裝器以及Word如何編組XML。 對于下一個示例,我們將研究如何添加完整的段落。 ?

將段落添加到此模板文檔

您可能想知道為什么我們需要添加段落? 我們已經可以添加文本了,一個段落不只是一大段文本嗎? 好,是的,不是。 一段確實看起來像是一段很大的文本,但是您需要考慮的是換行符。 如果像我們之前那樣添加Text元素,并在文本中添加換行符,它們將不會顯示。 需要換行符時,需要創建一個新段落。 幸運的是,使用Docx4j也很容易做到這一點。
我們將通過以下步驟進行操作:

  1. 從模板中找到要替換的段落
  2. 將輸入文本分成單獨的行
  3. 對于每一行,根據模板中的段落創建一個新段落
  4. 刪除原始段落

我們應該已經擁有的輔助方法不應該太難了。

private void replaceParagraph(String placeholder, String textToAdd, WordprocessingMLPackage template, ContentAccessor addTo) {// 1. get the paragraphList<Object> paragraphs = getAllElementFromObject(template.getMainDocumentPart(), P.class);P toReplace = null;for (Object p : paragraphs) {List<Object> texts = getAllElementFromObject(p, Text.class);for (Object t : texts) {Text content = (Text) t;if (content.getValue().equals(placeholder)) {toReplace = (P) p;break;}}}// we now have the paragraph that contains our placeholder: toReplace// 2. split into seperate linesString as[] = StringUtils.splitPreserveAllTokens(textToAdd, '\n');for (int i = 0; i < as.length; i++) {String ptext = as[i];// 3. copy the found paragraph to keep styling correctP copy = (P) XmlUtils.deepCopy(toReplace);// replace the text elements from the copyList texts = getAllElementFromObject(copy, Text.class);if (texts.size() > 0) {Text textToReplace = (Text) texts.get(0);textToReplace.setValue(ptext);}// add the paragraph to the documentaddTo.getContent().add(copy);}// 4. remove the original one((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);}

在此方法中,我們用提供的文本替換段落的內容,然后將新段落替換為用addTo指定的參數。

String placeholder = "SJ_EX1";String toAdd = "jos\ndirksen";replaceParagraph(placeholder, toAdd, template, template.getMainDocumentPart());

如果您在Word模板中使用更多內容來運行此程序,則會注意到這些段落將出現在文檔的底部。 原因是將段落添加回了主文檔。 如果您希望將段落添加到文檔中的特定位置(通常需要這樣做),則可以將其包裝在1×1無邊界表格中。 該表被視為段落的父級,可以在此處添加新段落。

將表添加到此模板文檔

我想展示的最后一個示例是如何向單詞模板添加表格。 實際上,更好的描述是如何在Word模板中填充預定義的表格。 就像我們對簡單的文本和段落所做的一樣,我們將替換占位符。 對于此示例,向您的Word文檔中添加一個簡單的表格(您可以隨意設置樣式)。 向此表添加1個啞行,用作內容模板。 在代碼中,我們將查找該行,將其復制,并將內容替換為來自Java代碼的新行,如下所示:

  1. 查找包含我們的關鍵字之一的表
  2. 復制用作行模板的行
  3. 對于每行數據,根據行模板向表中添加一行
  4. 刪除原始模板行

與我們在段落中顯示的方法相同。 首先,讓我們看一下如何提供替換數據。 對于此示例,我僅提供了一組哈希圖,其中包含要替換的占位符的名稱和要替換為其的值。 我還提供了可在表格行中找到的替換令牌。

Map<String,String> repl1 = new HashMap<String, String>();repl1.put("SJ_FUNCTION", "function1");repl1.put("SJ_DESC", "desc1");repl1.put("SJ_PERIOD", "period1");Map<String,String> repl2 = new HashMap<String,String>();repl2.put("SJ_FUNCTION", "function2");repl2.put("SJ_DESC", "desc2");repl2.put("SJ_PERIOD", "period2");Map<String,String> repl3 = new HashMap<String,String>();repl3.put("SJ_FUNCTION", "function3");repl3.put("SJ_DESC", "desc3");repl3.put("SJ_PERIOD", "period3");replaceTable(new String[]{"SJ_FUNCTION","SJ_DESC","SJ_PERIOD"}, Arrays.asList(repl1,repl2,repl3), template);

現在,這個replaceTable方法是什么樣的。

private void replaceTable(String[] placeholders, List<Map<String, String>> textToAdd,WordprocessingMLPackage template) throws Docx4JException, JAXBException {List<Object> tables = getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);// 1. find the tableTbl tempTable = getTemplateTable(tables, placeholders[0]);List<Object> rows = getAllElementFromObject(tempTable, Tr.class);// first row is header, second row is contentif (rows.size() == 2) {// this is our template rowTr templateRow = (Tr) rows.get(1);for (Map<String, String> replacements : textToAdd) {// 2 and 3 are done in this methodaddRowToTable(tempTable, templateRow, replacements);}// 4. remove the template rowtempTable.getContent().remove(templateRow);}}

此方法查找表,獲取第一行,并為每個提供的地圖在表中添加新行。 返回之前,它將刪除模板行。 此方法使用兩個幫助器:addRowToTable和getTemplateTable。 我們首先來看最后一個:

private Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {for (Iterator<Object> iterator = tables.iterator(); iterator.hasNext();) {Object tbl = iterator.next();List<?> textElements = getAllElementFromObject(tbl, Text.class);for (Object text : textElements) {Text textElement = (Text) text;if (textElement.getValue() != null && textElement.getValue().equals(templateKey))return (Tbl) tbl;}}return null;}

此函數只是查看表是否包含我們的占位符之一。 如果是這樣,則返回該表。 addRowToTable操作也非常簡單。

private static void addRowToTable(Tbl reviewtable, Tr templateRow, Map<String, String> replacements) {Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);List textElements = getAllElementFromObject(workingRow, Text.class);for (Object object : textElements) {Text text = (Text) object;String replacementValue = (String) replacements.get(text.getValue());if (replacementValue != null)text.setValue(replacementValue);}reviewtable.getContent().add(workingRow);}

此方法復制我們的模板,并使用提供的值替換此模板行中的占位符。 該副本將添加到表中。 就是這樣。 通過這段代碼,我們可以在Word文檔中填寫套利表,同時保留表的布局和樣式。

到本文為止。 使用段落和表格,您可以創建許多不同類型的文檔,這與最常生成的文檔類型非常匹配。 但是,也可以使用這種方法將其他類型的內容添加到Word文檔中。

參考:來自Smart Java博客的JCG合作伙伴 Jos Dirksen 使用docx4j以編程方式創建復雜的Word(.docx)文檔 。


翻譯自: https://www.javacodegeeks.com/2012/07/java-word-docx-documents-with-docx4j.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372155.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372155.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372155.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql中distinct關鍵字,MySQL關鍵字Distinct的詳細介紹

DDLPrepare SQL&#xff1a;?Prepare Data&#xff1a;?查詢數據如下圖所示&#xff1a;第一種情況&#xff0c;使用Distinct關鍵字&#xff0c;查詢單列數據&#xff0c;如下圖所示&#xff1a;結果&#xff1a;對 name 字段進行去重處理&#xff0c;符合預期期望&#xff0…

#pragma 預處理指令

Linux C 編程一站式學習 #pragma 預處理指示供編譯器實現一些非標準的特性&#xff0c;C 標準沒有規定 #pragma 后面應該寫什么以及起什么作用&#xff0c;由編譯器自己規定。有的編譯器用 #pragma 定義一些特殊功能寄存器名&#xff0c;有的編譯器用 #pragma 定位鏈接地址&…

px ,em ,rem

做移動端或者響應式的頁面必然需要字體的變化的。這次我就自己的經驗來說說他們之間的關系&#xff0c;以及怎么用。 px (絕對單位)是我們常用的就不說了。 em&#xff08;相對單位&#xff0c;相對父級&#xff09; em 指字體高&#xff0c;任意瀏覽器的默認字體高都是16px。所…

使用JAnnocessor生成Java代碼

在本文中&#xff0c;我將向你展示如何生成的代碼JAnnocessor通過創建框架Nikolche Mihajlovski 。 在Nikolche的演講中&#xff0c;我第一次在GeeCON 2012大會上遇到JAnnocessor&#xff1a; “創新和實用的Java源代碼生成” &#xff08;幻燈片&#xff09; 。 之后&#xff…

Linq學習筆記(轉)

開始Linq前你要知道的 擴展方法 顧名思義就是對現有類進行擴展的的方法&#xff0c;擴展方法可以在不修改現有類的情況下&#xff0c;為現有類增加公共的接口&#xff08;不是C#中的interface&#xff09;。 擴展方法本質上是一個靜態方法&#xff0c;不同之處在于它的第一個參…

cass展點不在原位置,cass中打開一副圖后,通過繪圖處理-——展高程點,怎么之前的圖形就不顯示了,,只剩下高程點!!...

答&#xff1a;1、進入控制面板&#xff0c;選擇“卸載或更改程序”。 2、選中“AutoCAD2006”圖標。 3、右擊選擇“更改”。 4、進入“AutoCAD2006安裝程序對話框”&#xff0c;選擇“添加/刪除功能”單選按鈕&#xff0c;點擊下一步。 5、在“程序文件”列表中&#xff0c;選…

(二)windows下安裝PHPCMS V9

一、準備工作 搭建環境 &#xff1a;參考:Windows下搭建PHP開發環境及相關注意事項PHPCMS V9 &#xff1a;下載適合自己 PHPCMS V9 版本到本地或服務器&#xff0c;下載地址&#xff1a;http://www.phpcms.cn/html/download/說明&#xff1a;官方提供了 2 種不同的編碼。包括 G…

JavaFX 2.0布局窗格– HBox和VBox

如果要對JavaFX 2.0中所有不同的布局窗格進行概述&#xff0c;或者想了解有關它們的一些基本知識&#xff0c;請參閱我以前的文章《 JavaFX 2.0中的布局窗格》 。 布局窗格HBox和VBox絕對是JavaFX 2.0中最基本的布局容器。 如您所知&#xff0c;它們的用途是將所有子級布置在一…

flask mysql分頁,Flask分頁的實現方法

所需環境Flask-SQLAlchemy分頁使用Flask-SQLAlchemy提供的pagination()方法。頁數是pagination()方法的第一個參數&#xff0c;也是唯一必須的參數。可選參數per_page用來指定每頁顯示的記錄數。參考代碼&#xff1a;def index():# ...page request.args.get(page, 1, typeint…

Java中的生成器設計模式

Java 中的 Builder設計模式是一種創建模式&#xff0c;即用于創建對象&#xff0c;類似于 工廠方法設計模式 &#xff0c;這也是創建設計模式。 在學習任何設計模式之前&#xff0c;我建議先找出特定設計模式要解決的問題。 眾所周知&#xff0c; 必要性是發明的母親。 在沒有面…

驗證碼( 隨機數)

方式一&#xff08;變色版&#xff09;&#xff1a; <html> <head><meta charset"UTF-8"/><title></title><script src"jquery-2.0.2.min.js"></script> </head> <body> <?php header("co…

單片機串行通信全解析

1.什么是串行通信&#xff1f; 串行通信&#xff08;英語&#xff1a;Serial communication&#xff09;是指在計算機總線或其他數據通道上&#xff0c;每次傳輸一個位元數據&#xff0c;并連續進行以上單次過程的通信方式。與之對應的是并行通信&#xff0c;它在串行端口上通過…

java type 類型,java中的泛型類型與Type接口

假設我們定義了一個Room的類&#xff0c;表示一個房間public classRoom(){}由于我們建造好房間是&#xff0c;不知道房間以后的用途&#xff0c;他可能用來住人&#xff0c;也有可能用來放貨物&#xff0c;因此需要用到泛型。但是我們可能想獲取Room這個房間里面進來的的東西的…

centos7下操作防火墻

引言 最近使用centos7系統比較頻繁&#xff0c;在配置服務器的時候&#xff0c;總是遇到能夠ping通服務器&#xff0c;但是就是沒有辦法訪問80端口&#xff0c;這個時候我的直覺告訴我&#xff0c;肯定是防火墻的原因&#xff0c;但是使用iptables卻怎么都找不到命令&#xff0…

其他團隊對本團隊評價的總結

我們小組在看了其他小組的評價后&#xff0c;對自己的程序有了新的看法。轉載于:https://www.cnblogs.com/bk1246788/p/6879691.html

Java:使用Fork / Join框架的Mergesort

此項的目的是顯示一個Fork / Join RecursiveAction的簡單示例&#xff0c;而不是過多地研究合并合并的可能優化方法&#xff0c;或者比使用Exkutor / Join Pool優于現有的基于Java 6的現有實現&#xff08;例如ExecutorService&#xff09;的相對優勢。 以下是使用Java的自上而…

php的異常處理方式,php異常處理基本方法

當一個php腳本運行時&#xff0c;為了防止腳本運行崩潰&#xff0c;亦或是當php作為webserver&#xff0c;為了防止php程序出錯&#xff0c;拋出httpcode500錯誤&#xff0c;我們常常需要對php程序做異常處理。今天介紹的是最基本的異常處理方法&#xff1a;一般而言&#xff0…

關系型數據庫的三范式

第一范式:確保每列的原子性. 如果每列(或者每個屬性)都是不可再分的最小數據單元(也稱為最小的原子單元),則滿足第一范式. 例如:顧客表(姓名、編號、地址、……)其中"地址"列還可以細分為國家、省、市、區等。第二范式:在第一范式的基礎上更進一層,目標是確保表…

vray學習筆記(3)-多維子材質是個什么東西

多維子材質是個什么東西&#xff1f;為什么出現這個概念&#xff1f; 在3dsmax官方網站&#xff0c;我們可以看到它的定義&#xff1a; The Multi/Sub-Object material lets you assign different materials at the sub-object level of your geometry. 意思是多維子材質這個概…

Hello JavaFX 2.0:命令行介紹

我從博客文章Hello JavaFX 2.0&#xff1a;NetBeans 7.1 beta的介紹中&#xff0c;從NetBeans 7.1 beta的角度看了一個無處不在的Hello World示例的簡單JavaFX版本。 在本文中&#xff0c;我將介紹僅使用命令行工具通過JavaFX實現的Hello World版本。 JavaFX 2.0 API文檔包括ja…