Solución:
Sí, puedes hacerlo usando Apache-POI. Los nombres de sus variables deben ser únicos. Ver el siguiente código
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.hwpf.usermodel.Section;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
public class HWPFTest {
public static void main(String[] args){
String filePath = "F:\Sample.doc";
POIFSFileSystem fs = null;
try {
fs = new POIFSFileSystem(new FileInputStream(filePath));
HWPFDocument doc = new HWPFDocument(fs);
doc = replaceText(doc, "$VAR", "MyValue1");
saveWord(filePath, doc);
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
}
private static HWPFDocument replaceText(HWPFDocument doc, String findText, String replaceText){
Range r1 = doc.getRange();
for (int i = 0; i < r1.numSections(); ++i ) {
Section s = r1.getSection(i);
for (int x = 0; x < s.numParagraphs(); x++) {
Paragraph p = s.getParagraph(x);
for (int z = 0; z < p.numCharacterRuns(); z++) {
CharacterRun run = p.getCharacterRun(z);
String text = run.text();
if(text.contains(findText)) {
run.replaceText(findText, replaceText);
}
}
}
}
return doc;
}
private static void saveWord(String filePath, HWPFDocument doc) throws FileNotFoundException, IOException{
FileOutputStream out = null;
try{
out = new FileOutputStream(filePath);
doc.write(out);
}
finally{
out.close();
}
}
}
Recientemente tuve que resolver el mismo problema pero con un .docx documento. Y probar el enfoque anterior resultó como el siguiente error (como se informó en esta publicación):
org.apache.poi.poifs.filesystem.OfficeXmlFileException: los datos proporcionados parecen estar en el XML de Office 2007+. Está llamando a la parte de PDI que se ocupa de los documentos de Office OLE2. Debe llamar a una parte diferente de POI para procesar estos datos (por ejemplo, XSSF en lugar de HSSF)
Finalmente, tuve que cambiar el código de la siguiente manera (en mi caso, el archivo .docx está dentro de la carpeta de recursos):
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
public class XWPFTest {
public static void main(String[] args) throws URISyntaxException, IOException {
String resourcePath = "template.docx";
Path templatePath = Paths.get(XWPFTest.class.getClassLoader().getResource(resourcePath).toURI());
XWPFDocument doc = new XWPFDocument(Files.newInputStream(templatePath));
doc = replaceTextFor(doc, "UNIQUE_VAR", "MyValue1");
saveWord("C:\document.docx", doc);
}
private static XWPFDocument replaceTextFor(XWPFDocument doc, String findText, String replaceText){
doc.getParagraphs().forEach(p ->{
p.getRuns().forEach(run -> {
String text = run.text();
if(text.contains(findText)) {
run.setText(text.replace(findText, replaceText), 0);
}
});
});
return doc;
}
private static void saveWord(String filePath, XWPFDocument doc) throws FileNotFoundException, IOException{
FileOutputStream out = null;
try{
out = new FileOutputStream(filePath);
doc.write(out);
}
catch(Exception e) {
e.printStackTrace();
}
finally{
out.close();
}
}
}
PD: tuve que eliminar el $ porque en .docx se administran ejecuciones separadas, así que tuve que optar por el enfoque de un nombre var único. Necesitaba las siguientes dependencias de Apache POI:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.17</version>
</dependency>