最近需要用docx4j來對docx進行一些操作,用到的技術是docx4j,這個技術在國內其實用的不是很多,看了一些博主的文章,有些感悟,做了一些總結,如果有疑問或錯誤之處歡迎交流。
創建包:
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
保存包:
wordMLPackage.save(new java.io.File("C://xxx.docx"));
得到主段落,並且輸出/帶樣式輸出:
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!"); wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello Word!"); wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle"," a subtitle!");
創建表格並添加內容:
ObjectFactory factory=Context.getWmlObjectFactory(); Tbl table = factory.createTbl(); Tr tableRow = factory.createTr(); Tc tableCell = factory.createTc(); tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText("Field 1")); tableRow.getContent().add(tableCell); table.getContent().add(tableRow); wordMLPackage.getMainDocumentPart().addObject(table);
先創建一個工廠,(需要導入的包是org.docx4j.wml,導錯的的話下面全錯)創建表格,在創建行和單元格(tableCell),在單元格里添加你想要的內容,因為返回值是Object,只能通過這種方式傳入數據,最后層層退回去,用add添加,最后在主段落添加。
編輯表格樣式:
table.setTblPr(new TblPr()); CTBorder border = new CTBorder(); border.setColor("auto"); border.setSz(new BigInteger("4")); TblBorders borders = new TblBorders(); borders.setBottom(border); borders.setLeft(border); borders.setInsideV(border); table.getTblPr().setTblBorders(borders);
先創建table樣式對象,在用CTBorder對象規定樣式規范,用TblBorders對象將樣式規范應用進去。
創建 段落/運行塊/運行塊屬性/文本 對象:
ObjectFactory factory=Context.getWmlObjectFactory(); P paragraph = factory.createP(); Text text = factory.createText(); text.setValue(content); R run = factory.createR(); run.getContent().add(text); paragraph.getContent().add(run); RPr runProperties = factory.createRPr(); run.setRPr(runProperties); tableCell.getContent().add(paragraph);
P是一個段落,Text是文本的值對象,R是一個運行塊,負責便於將多個屬性相同的Text對象統一操作,RPr是運行塊的屬性,可以對R對象進行操作。簡單的說幾個對象之間的關系可以這么理解:Tc tableCell > P paragraph > R run > Text text。其中,run.setRPr(RPr runProperties)可以設置塊中屬性。個人認為用開始的方法輸入內容,在某種程度上是和上述代碼做了一樣的工作,效果相同。
加粗字體和調整字體大小:
HpsMeasure size = new HpsMeasure(); size.setVal(new BigInteger("40")); runProperties.setSz(size); runProperties.setSzCs(size); BooleanDefaultTrue b = new BooleanDefaultTrue(); b.setVal(true); runProperties.setB(b);
思路是先創建各自的對象,設置對象的值為自己想要的情況,再用RPr的對象來set相應的屬性。其中注意setVal中的值最后會被現實一半,所以只有字體20大小。
縱向合並單元格:
Tc tableCell = factory.createTc(); TcPr tableCellProperties = new TcPr(); VMerge merge = new VMerge(); merge.setVal("restart"); tableCellProperties.setVMerge(merge); tableCell.setTcPr(tableCellProperties); tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(content)); row.getContent().add(tableCell);
先創建單元格屬性,再創建VMerge對象,如果設置merge則為向上合並,如果將merge屬性設為restart則重新開始新的單元格。
設置單元格寬度:
TcPr tableCellProperties = new TcPr(); TblWidth tableWidth = new TblWidth(); tableWidth.setW(BigInteger.valueOf("50")); tableCellProperties.setTcW(tableWidth); tableCell.setTcPr(tableCellProperties);
先創建單元格屬性對象,創建Tblwidth對象並且設置寬度,用單元格屬性對象通過方法調用Tblwidth對象。
添加圖片:
File file = new File("c:\\a.jpg"); BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, file); int docPrId = 1; int cNvPrId = 2; Inline inline = imagePart.createImageInline("Filename hint","Alternative text", docPrId, cNvPrId, false); ObjectFactory factory = new ObjectFactory(); P paragraph = factory.createP(); R run = factory.createR(); paragraph.getContent().add(run); Drawing drawing = factory.createDrawing(); run.getContent().add(drawing); drawing.getAnchorOrInline().add(inline); wordMLPackage.getMainDocumentPart().addObject(paragraph);
打開文件,通過imagePart將圖片讀進去,現在圖片被轉換成二進制,為了能在文件中內聯中顯示出圖片,調用函數將圖片存在inline中。之后paragraph,run,drawing,用drawing讀inline,方法同上。
加載讀入docx文件:
WordprocessingMLPackage template = WordprocessingMLPackage.load(new File("c:\\a.docx"));
獲取文檔中所有內容(方法):
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; }
通過對類型的判斷,將obj的內容分類讀到List中,最后將內容按照列表的順序貯存,如果obj是JAXB的一個實例就將他轉型獲取值,如果是和第二個參數的類型相同就添加,如果是ContentAccessor的一個對象,就將對象中的內容存到另外的一個列表中,再次調用自己將全部元素添加到原來的List中,返回一個List。