Documenti DOCX con Apache POI
In questo articolo vedremo come sia possibile realizzare dei documenti DOCX utilizzando la libreria Java opensource Apache POI.
Il progetto Apache POI è stato creato per poter gestire attraverso un API Java i documenti Microsoft basati su OLE2 e sugli standard Office Open XML (OOXML). Utilizzando questa libreria è possibile quindi gestire diversi formati di file come quelli riportati nel seguente elenco, per i quali esistono dei veri e propri componenti separati
- Excel (SS=HSSF+XSSF)
- Word (HWPF+XWPF)
- PowerPoint (HSLF+XSLF)
- OpenXML4J (OOXML)
- OLE2 Filesystem (POIFS)
- OLE2 Document Props (HPSF)
- Outlook (HSMF)
- Visio (HDGF)
- TNEF (HMEF)
- Publisher (HPBF)
Negli esempi dei prossimi paragrafi vedremo come questa interessante libreria ci permette di interagire con i documenti DOCX. Tutti gli esempi presenti condivideranno le seguenti dipendenze Maven
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.13</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.13</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.13</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.13</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>ooxml-schemas</artifactId> <version>1.1</version> </dependency> </dependencies> |
Hello World
Per iniziare ad utilizzare XWPF, che è il componente di Apache POI che gestisce il formato DOCX, dobbiamo vedere le principali classi che dovranno essere gestite. Il documento vero e proprio viene mappato dalla classe org.apache.poi.xwpf.usermodel.XWPFDocument. A partire dal documento possiamo aggiungere dei paragrafi, mappati dalla classe XWPFParagraph, che a loro volta contengono il vero e proprio testo gestito dalla classe XWPFRun.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.javastaff.wordexample; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.ParagraphAlignment; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; public class HelloWorld { public static void main(String[] args) throws Exception { XWPFDocument document = new XWPFDocument(); XWPFParagraph bodyParagraph = document.createParagraph(); bodyParagraph.setAlignment(ParagraphAlignment.CENTER); XWPFRun r = bodyParagraph.createRun(); r.setBold(true); r.setText("Il mio primo documento DOCX con Apache POI"); //Aggiungo un logo String imgFile = "logo.png"; FileInputStream is = new FileInputStream(imgFile); r.addBreak(); r.addPicture(is, XWPFDocument.PICTURE_TYPE_PNG, imgFile, Units.toEMU(400), Units.toEMU(100)); // 400x100 pixel is.close(); //Crea il documento FileOutputStream out = new FileOutputStream(new File("helloworld.docx")); document.write(out); out.close(); } } |
Oltre al semplice testo abbiamo aggiunto un’immagine utilizzando il metodo addPicture di XWPFRun, dove viene specificato il tipo di immagine e anche le dimensioni in pixel. Nella seguente immagine è riportato il documento generato con questo codice
Header e Footer
Passiamo ora ad inserire header e footer all’interno del nostro documento. Per accedere a header e footer dobbiamo creare un oggetto di tipo XWPFHeaderFooterPolicy a partire dal nostro documento. Sulla base di questa policy, utilizzando i metodi createHeader e createFooter possiamo personalizzare le bande del documento con dei paragrafi creati da noi. Nel seguente esempio questi paragrafi hanno semplicemente un testo allineato al centro.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package com.javastaff.wordexample; import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.apache.poi.xwpf.usermodel.ParagraphAlignment; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText; public class HeaderFooter { public static void main(String a[]) throws IOException { XWPFDocument docx = new XWPFDocument(); CTSectPr sectPr = docx.getDocument().getBody().addNewSectPr(); XWPFHeaderFooterPolicy policy = new XWPFHeaderFooterPolicy(docx, sectPr); //HEADER CTP ctpHeader = CTP.Factory.newInstance(); CTR ctrHeader = ctpHeader.addNewR(); CTText ctHeader = ctrHeader.addNewT(); String headerText = "HEADER BLA BLA BLA"; ctHeader.setStringValue(headerText); XWPFParagraph headerParagraph = new XWPFParagraph(ctpHeader, docx); headerParagraph.setAlignment(ParagraphAlignment.CENTER); XWPFParagraph[] parsHeader = new XWPFParagraph[1]; parsHeader[0] = headerParagraph; policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, parsHeader); //FOOTER CTP ctpFooter = CTP.Factory.newInstance(); CTR ctrFooter = ctpFooter.addNewR(); CTText ctFooter = ctrFooter.addNewT(); String footerText = "FOOTER BLA BLA BLA"; ctFooter.setStringValue(footerText); XWPFParagraph footerParagraph = new XWPFParagraph(ctpFooter, docx); footerParagraph.setAlignment(ParagraphAlignment.CENTER); XWPFParagraph[] parsFooter = new XWPFParagraph[1]; parsFooter[0] = footerParagraph; policy.createFooter(XWPFHeaderFooterPolicy.DEFAULT, parsFooter); //DOCUMENTO XWPFParagraph bodyParagraph = docx.createParagraph(); bodyParagraph.setAlignment(ParagraphAlignment.CENTER); XWPFRun r = bodyParagraph.createRun(); r.setBold(true); r.setText("Documento con header e footer"); FileOutputStream out = new FileOutputStream("header-footer.docx"); docx.write(out); out.close(); } } |
Abbiamo visto come scrivere header e footer, se invece volessimo accedere al testo contenuto in queste bande potremmo farlo utilizzando rispettivamente i metodi getHeaderList e getFooterList di XWPFDocument.
Replace
Una cosa che sicuramente può tornare molto utile in diverse situazioni è quella di parametrizzare i documenti. Come viene fatto con altre strumenti, come ad esempio JasperReport, possiamo includere in un documento DOCX delle variabili e poi utilizzare Apache POI per valorizzarle a runtime. Il testo viene quasi sempre inserito in degli oggetti contenitori chiamati XWPFRun, che quindi dovremo ricercare all’interno del documento per verificare se è presente una determinata stringa e sostituirla.
XWPFRun è possibile trovarli sia all’interno di un XWPFParagraph che all’interno di una tabella XWPFTable che a sua volta contiene XWPFParagraph. Nel seguente esempio possiamo vedere un semplice esempio di quello che abbiamo detto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package com.javastaff.wordexample; import java.io.File; import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.List; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; public class Replace { public static void main(String a[]) throws Exception { XWPFDocument doc = new XWPFDocument(OPCPackage.open("toreplace.docx")); SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy"); replace(doc, "$libreria", "Apache POI"); replace(doc, "$chiave1", "Pizza"); replace(doc, "$chiave2", "Fichi"); replace(doc, "$data", format.format(Calendar.getInstance().getTime())); FileOutputStream out = new FileOutputStream(new File("replaced.docx")); doc.write(out); out.close(); } public static void replace(XWPFDocument document, String oldtext, String newtext) { String text = null; List<XWPFParagraph> paragraphs = document.getParagraphs(); for (XWPFParagraph xwpfParagraph : paragraphs) { for (XWPFRun run : xwpfParagraph.getRuns()) { text = run.getText(0); text = text.replace(oldtext, newtext); run.setText(text, 0); } } for (XWPFTable tbl : document.getTables()) { for (XWPFTableRow row : tbl.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph p : cell.getParagraphs()) { for (XWPFRun run : p.getRuns()) { text = run.getText(0); text = text.replace(oldtext, newtext); run.setText(text, 0); } } } } } } } |
Tabella
Vediamo infine come creare una tabella. L’esempio che viene riportato crea prima un oggetto XWPFTable all’interno del documento e successivamente si popola la tabella, creando prima la riga attraverso il metodo createRow di XWPFTable. La riga, mappata dalla classe XWPFTableRow, ci servira poi per popolare le singole celle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.javastaff.wordexample; import java.io.File; import java.io.FileOutputStream; import java.math.BigInteger; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth; public class Table { public static void main(String[] args) throws Exception { XWPFDocument document = new XWPFDocument(); //TABELLA XWPFTable table = document.createTable(); XWPFTableRow riga1 = table.getRow(0); riga1.getCell(0).setText("*"); riga1.addNewTableCell().setText("0"); riga1.addNewTableCell().setText("1"); riga1.addNewTableCell().setText("2"); XWPFTableRow riga2 = table.createRow(); riga2.getCell(0).setText("0"); riga2.getCell(1).setText("0"); riga2.getCell(2).setText("0"); riga2.getCell(3).setText("0"); XWPFTableRow riga3 = table.createRow(); riga3.getCell(0).setText("1"); riga3.getCell(1).setText("0"); riga3.getCell(2).setText("1"); riga3.getCell(3).setText("2"); XWPFTableRow riga4 = table.createRow(); riga4.getCell(0).setText("2"); riga4.getCell(1).setText("0"); riga4.getCell(2).setText("2"); riga4.getCell(3).setText("4"); CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW(); width.setType(STTblWidth.DXA); width.setW(BigInteger.valueOf(9072)); //Per rimuovere i bordi //table.getCTTbl().getTblPr().unsetTblBorders(); FileOutputStream out = new FileOutputStream(new File("tabella.docx")); document.write(out); out.close(); } } |
All’interno di una cella è anche possibile aggiungere un XWPFParagraph, potendo quindi andare ad inserire un testo strutturato in diversi XWPFRun o comunque con formattazioni particolari.