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
<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.
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.
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
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.
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.

Looking for a right “about me”…
Commenti recenti