Tutorial Apache POI: scriviamo e leggiamo un documento Word

Tutorial Apache POI: scriviamo e leggiamo un documento Word

In questo articolo vedremo come creare un documento Word e come successivamente possiamo leggerlo, tramite la libreria Apache POI. Purtroppo le API non sono complete come per la versione Excel e non esistono interfacce Wrapper per entrambe le versioni HWPF e XWPF, in poche parole dovremo usare due classi distinte se vogliamo creare e/o leggere un file di tipo .doc o di tipo .docx e, ancora peggio, il loro utilizzo è piuttosto differente.

Apache Commons: componenti riusabili per Java
Apache MyFaces: un’implementazione Open Source delle specifiche JSF
Apache POI: manipolare documenti Microsoft in Java

In questo articolo vedremo come creare un documento Word e come successivamente possiamo leggerlo, tramite la libreria Apache POI. Purtroppo le API non sono complete come per la versione Excel e non esistono interfacce Wrapper per entrambe le versioni HWPF e XWPF, in poche parole dovremo usare due classi distinte se vogliamo creare  e/o leggere un file di tipo .doc o di tipo .docx e, ancora peggio, il loro utilizzo è piuttosto differente.

Prima di vedere qualche esempio, elenchiamo le dipendenze della libreria POI:

ComponenteTipo ApplicazioneId Artefatto Maven
POIFSOLE2 Filesystempoi
HPSFOLE2 Property Setspoi
HSSFExcel XLSpoi
HSLFPowerPoint PPTpoi-scratchpad
HWPFWord DOCpoi-scratchpad
HDGFVisio VSDpoi-scratchpad
HPBFPublisher PUBpoi-scratchpad
HSMFOutlook MSGpoi-scratchpad
XSSFExcel XLSXpoi-ooxml
XSLFPowerPoint PPTXpoi-ooxml
XWPFWord DOCXpoi-ooxml
OpenXML4JOOXMLpoi-ooxml-schemas, ooxml-schemas

Nella tabella precedente troviamo nella prima colonna il nome del Componente della libreria Apche POI, nella seconda colonna il Tipo di Applicazione per cui viene utilizzato ed infine nella terza colonna l’Id dell’Artefatto Maven che lo implementa.

Id Artefatto MavenPrerequisitiJAR
poicommons-logging, log4jpoi-version-yyyymmdd.jar
poi-scratchpadpoipoi-scratchpad-version-yyyymmdd.jar
poi-ooxmlpoi, poi-ooxml-schemas, dom4jpoi-ooxml-version-yyyymmdd.jar
poi-ooxml-schemas (*)xmlbeans, stax-api-1.0.1poi-ooxml-schemas-version-yyyymmdd.jar
poi-examples (*)poi, poi-scratchpad, poi-ooxmlpoi-examples-version-yyyymmdd.jar
ooxml-schemasxmlbeans, stax-api-1.0.1ooxml-schemas-1.1.jar

(*) a partire dalla 3.6-beta1-20091124.

In questa ultima tabella vediamo nella seconda colonna i prerequisiti per i vari Artifatti Maven ed il corrispondente jar (che troviamo incluso nel download della libreria Apache POI).

Facciamo un esempio: se dovessimo usare il componente HWPF (documenti .doc) dovremmo includere nel nostro classpath i jar  commons-logging e log4j ed utilizzare la libreria poi-scratchpad-version-yyyymmdd.jar e poi-version-yyyymmdd.jar; se invece volessimo utilizzare il componente XWPF (documenti .docx) dovremmo includere oltre alle librerie precedenti, i jar xmlbeans, stax-api-1.0.1, dom4j, poi-ooxml-version-yyyymmdd.jarpoi-ooxml-schemas-version-yyyymmdd.jar.

Nel caso in cui usiamo Eclipse come IDE di sviluppo dobbiamo includere tali librerie nel Build Path del Progetto Java.

Facciamo ora qualche esempio prima con le API del componente HWPF. La prima cosa da fare è aprire un file .doc in quanto non è possibile crearne uno da programma (il costruttore HWPFDocument() è di tipo protected:

String nomefile = "test.doc";
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(nomefile));
HWPFDocument doc = new HWPFDocument(fs);

per poter modificare il testo contenuto nel documento word dobbiamo avere una istanza della classe Range che rappresenta l’insieme di testo all’interno del file .doc, possiamo poi prendere i vari elementi quali i Paragrafi direttamente da esso. Per ottenere il Range utilizziamo il metodo getRange():

Range range = doc.getRange();
range.insertAfter("Hello World!!");

con i metodi insertBefore e insertAfter possiamo aggiungere del testo prima e dopo del Range. Dal Range possiamo poi ottenere i Paragrafi e i CharacterRuns (insieme di testo che condivide le stesse proprietà).

Un metodo più efficace di estrarre il testo è istanziando una calsse di tipo WordExtractor:


WordExtractor we = new WordExtractor(doc);
String[] paragrafi = we.getParagraphText();
System.out.println( "Il documento Word ha " + paragrafi.length + " paragrafi" );
for( int i=0; iparagrafi[i] = paragrafi[i].replaceAll("\cM?r?n","");
System.out.println( "Testo: "+paragrafi[ i ]);
System.out.println( "Lunghezza: "+paragrafi[ i ].length());
}

Con il codice su riportato estraiamo i paragrafi dal doc e li inseriamo in un array di stringhe (una stringa per ogni paragrafo) e poi iteriamo stampando a video lunghezza e contenuto. Possiamo anche utilizzare più semplicemente il metodo getText() di WordExtractor per ottenere tutto il testo del doc.

Utilizzando invece la classe XWPFDocument (per file .docx) non abbiamo bisogno di un file preesistente, ma possiamo scriverne uno alla fine delle nostre manipolazioni.

Proviamo a creare dei paragrafi, utilizzando stili e allineamenti diversi, creiamo inoltre all’interno dei paragrafi, degli XWPFRun che sono porzioni di testo che condividono lo stesso stile e formato:


package it.appuntisoftware;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.xwpf.usermodel.Borders;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.VerticalAlign;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

public class TestWordPOI {

public static void main(String[] args) {

String filename = "text.docx";
XWPFDocument document = new XWPFDocument();

XWPFParagraph paragrafoUno = document.createParagraph();
paragrafoUno.setAlignment(ParagraphAlignment.CENTER);
paragrafoUno.setBorderBottom(Borders.SINGLE);
paragrafoUno.setBorderTop(Borders.SINGLE);
paragrafoUno.setBorderRight(Borders.SINGLE);
paragrafoUno.setBorderLeft(Borders.SINGLE);
paragrafoUno.setBorderBetween(Borders.SINGLE);

XWPFRun paragrafoUnoRunUno = paragrafoUno.createRun();
paragrafoUnoRunUno.setBold(true);
paragrafoUnoRunUno.setItalic(true);
paragrafoUnoRunUno.setText("Hello world! Questo è il paragrafo Uno, Run Uno!");
paragrafoUnoRunUno.addBreak();

XWPFRun paragrafoUnoRunDue = paragrafoUno.createRun();
paragrafoUnoRunDue.setText("Secondo Run!");
paragrafoUnoRunDue.setTextPosition(100);

XWPFRun paragrafoUnoRunTre = paragrafoUno.createRun();
paragrafoUnoRunTre.setStrike(true);
paragrafoUnoRunTre.setFontSize(20);
paragrafoUnoRunTre.setSubscript(VerticalAlign.SUBSCRIPT);
paragrafoUnoRunTre.setText(" Ancora paragrafo Uno...");

XWPFParagraph paragrafoDue = document.createParagraph();
paragrafoDue.setAlignment(ParagraphAlignment.DISTRIBUTE);
paragrafoDue.setIndentationRight(200);

XWPFRun paragraphTwoRunOne = paragrafoDue.createRun();
paragraphTwoRunOne.setText("Questo è il paragrafo due!");
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(filename);
document.write(outStream);
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

Possiamo inoltre creare e riempire delle tabelle di testo:

XWPFDocument document = new XWPFDocument();
int righe = 5, colonne = 5;
XWPFTable table = document.createTable(righe , colonne);
for(int i=0; i< righe; i++)
for(int j=0; j< colonne; j++)
table.getRow(i).getCell(j).addParagraph().createRun()
.setText("cella: ["+i+"]["+j+"]" );

COMMENTS

WORDPRESS: 9
  • comment-avatar
    antonio 12 anni ago

    l’esempio è chiaro ma per renderlo realmente utile va corredato delle opportune “import”.
    secondariamente, mi chiedo come mai un sito che rende disponibili tante informazioni non disponga di una pur minima interfaccia di ricerca.

    • comment-avatar

      ho aggiunto gli import all’esempio, era uno dei miei primi post quindi leggermente incompleto, per quanto riguarda la ricerca la trovi in alto a destra sotto i tasti di condivisione di facebook, twitter, ect…
      grazie per la segnalazione

  • comment-avatar

    Quando cerco di eseguire il suo esempio mi esce questo errore

    Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/xmlbeans/XmlException
    at EstraiFileDoc.metodoXWPF(EstraiFileDoc.java:43)
    at EstraiFileDoc.main(EstraiFileDoc.java:37)
    Caused by: java.lang.ClassNotFoundException: org.apache.xmlbeans.XmlException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    … 2 more

    Il file .doc l’ho creato in una cartella “files”

    Non riesco proprio a capire il problema

    Ringrazio anticpatamente

    • comment-avatar

      ciao, l’errore è risolvibile se aggiungi al classpath il jar xmlbeans-version.jar, che dovresti trovare nel pacchetto zip dei binari della versione della libreria POI che hai scaricato.
      Se usi Eclipse devi aggiungere la suddetta libreria al Build Path.
      Vi sono poi altre dipendenze in base alla classe specifica che stai utilizzando per i tuoi esempi, a questo punto aggiungo all’articolo l’elenco delle dipendenze in base alle classi che si utilizzano.

      ciao e grazie per aver letto l’articolo.

  • comment-avatar
    Andrea| 12 anni ago

    Ciao sto provando a compilare la prima parte del programma ma mi dice che non trova il metodo insertAfter della riga range.insertAfter(“Hello World!!”);
    Ho aggiunto tutti i jar che ho scaricato…magari sono sbagliati…sai dirmi quello giusto???

    • comment-avatar

      Ciao Andrea,
      per compilare un progetto Java che fa uso del componente HWPF di Apache POI devi avere necessariamente incluso nel Build Path del tuo progetto i seguenti jar:
      commons-logging
      log4j
      poi-version-yyyymmdd.jar
      poi-scratchpad-version-yyyymmdd.jar

      proprio in questo ultimo jar è definita la classe Range e probabilmente è questo jar che ti manca.

  • comment-avatar
    Sylvyn 12 anni ago

    Ciao. So che è una domanda banalissima, sono però nuova di java e netbeans.
    Volevo chiederti come faccio a far sì che il codice che hai postato funzioni sul mio computer, cioè non so aggiungere librerie esterne. Ho scaricato i due file .zip, quello bin e src, e messi nella cartella del mio progetto, ma ora cosa devo fare affinchè Netbeans li veda?
    Grazie e scusa la banalità.

    • comment-avatar

      Ciao, nel tutorial utilizzo Eclipse e per aggiungere librerie esterne basta fare click col tasto destro sul progetto e scegliere Configure Build Path…
      Per quanto riguarda Netbeans devi cliccare col destro sul progetto e selezionare la voce Properties, da questa poi devi andare su Libriaries e qui trovi la lista delle librerie disponibili, prima però devi importare la libreria in Netbeans, potresti vedere su questo sito come si fa:
      http://www.oracledistilled.com/netbeans/adding-java-libraries-to-netbeans-6-9/

      grazie per aver letto il tutorial

  • comment-avatar

    Ciao
    bello il tutorial funziona tutto ma ho due domande da fare.
    come faccio a posizionarmi in una certa posizione del documento word e poi inserire un testo o una tabella, va bene sia un doc che un docx
    In pratica in un modello di partenza metto una parola chiave e dopo di essa devo inserire un testo.
    In alternativa posso fare un cerca sul documento e rimanere posizionato
    in quella posizione per fare operazioni diverse.
    grazie
    salvatore