IBody.getParagraphs и IBody.getBodyElements- получают только те абзацы или элементы тела, которые находятся непосредственно в этом IBody
. Но ваши абзацы находятся не там, а в отдельном текстовом поле или текстовом фрейме. Вот почему их нельзя получить таким образом.
Поскольку *.docx
- это ZIP
архив, содержащий XML
файлов для документов, верхних и нижних колонтитулов, можно получить все текстовые прогоны одного IBody
, создав XmlCursor
, который выбирает все элементы w:r
XML
. Для XWPFHeader
это может выглядеть так:
private List<XmlObject> getAllCTRs(XWPFHeader header) {
CTHdrFtr ctHdrFtr = header._getHdrFtr();
XmlCursor cursor = ctHdrFtr.newCursor();
cursor.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:r");
List<XmlObject> ctrInHdrFtr = new ArrayList<XmlObject>();
while (cursor.hasNextSelection()) {
cursor.toNextSelection();
XmlObject obj = cursor.getObject();
ctrInHdrFtr.add(obj);
}
return ctrInHdrFtr;
}
Теперь у нас есть список всех элементов XML
в этом заголовке, которые являются элементами text-run в Word
.
У нас может быть более общий getAllCTRs
, который получает все CTR
элементов из любого вида IBody
, например, так:
private List<XmlObject> getAllCTRs(IBody iBody) {
XmlCursor cursor = null;
List<XmlObject> ctrInIBody = new ArrayList<XmlObject>();
if (iBody instanceof XWPFHeaderFooter) {
XWPFHeaderFooter headerFooter = (XWPFHeaderFooter)iBody;
CTHdrFtr ctHdrFtr = headerFooter._getHdrFtr();
cursor = ctHdrFtr.newCursor();
} else if (iBody instanceof XWPFDocument) {
XWPFDocument document = (XWPFDocument)iBody;
CTDocument1 ctDocument1 = document.getDocument();
cursor = ctDocument1.newCursor();
} else if (iBody instanceof XWPFAbstractFootnoteEndnote) {
XWPFAbstractFootnoteEndnote footEndnote = (XWPFAbstractFootnoteEndnote)iBody;
CTFtnEdn ctFtnEdn = footEndnote.getCTFtnEdn();
cursor = ctFtnEdn.newCursor();
}
if (cursor != null) {
cursor.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:r");
while (cursor.hasNextSelection()) {
cursor.toNextSelection();
XmlObject obj = cursor.getObject();
ctrInIBody.add(obj);
}
}
return ctrInIBody ;
}
Теперь у нас есть список всех XML
элементов в этом IBody
, которые являются элементами text-run в Word
.
Имея это, мы можем получить текст из них следующим образом:
private void printAllTextInTextRunsOfIBody(IBody iBody) throws Exception {
List<XmlObject> ctrInIBody = getAllCTRs(iBody);
for (XmlObject obj : ctrInIBody) {
CTR ctr = CTR.Factory.parse(obj.xmlText());
for (CTText ctText : ctr.getTList()) {
String text = ctText.getStringValue();
System.out.println(text);
}
}
}
Это, вероятно, показывает следующую проблему. Потому что Word
очень грязно при создании text-run-elements. Например, ваш заполнитель <<Firstname>>
может быть разбит на текстовые прогоны <<
+ Firstname
+ >>
. Причиной может быть другое форматирование или проверка орфографии или что-то еще. Даже это возможно: <<
+ Lastname
+ >>; <<
+ YearOfBirth
+ >>
. Или даже это: <<Firstname
+ >> <<
+ Lastname>>; <<
+ YearOfBirth>>
. Видите ли, замена заполнителей текстом практически невозможна, поскольку заполнители могут быть разбиты на несколько текстовых прогонов.
Чтобы избежать этого, необходимо создать template.dotx
от пользователей, которые знают, что они делают.
Сначала отключить проверку орфографии . Проверка грамматики также. Если нет, то все найденные возможные ошибки заклинаний или грамматические нарушения находятся в отдельных текстовых прогонах, чтобы пометить их соответствующим образом.
Во-вторых, убедитесь, что весь заполнитель отформатирован в равном порядке. Различный форматированный текст также должен быть в отдельных текстовых прогонах.
Я действительно скептично c, что это будет работать правильно. Но попробуйте сами.
Полный пример:
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlCursor;
import java.util.List;
import java.util.ArrayList;
public class WordEditAllIBodys {
private List<XmlObject> getAllCTRs(IBody iBody) {
XmlCursor cursor = null;
List<XmlObject> ctrInIBody = new ArrayList<XmlObject>();
if (iBody instanceof XWPFHeaderFooter) {
XWPFHeaderFooter headerFooter = (XWPFHeaderFooter)iBody;
CTHdrFtr ctHdrFtr = headerFooter._getHdrFtr();
cursor = ctHdrFtr.newCursor();
} else if (iBody instanceof XWPFDocument) {
XWPFDocument document = (XWPFDocument)iBody;
CTDocument1 ctDocument1 = document.getDocument();
cursor = ctDocument1.newCursor();
} else if (iBody instanceof XWPFAbstractFootnoteEndnote) {
XWPFAbstractFootnoteEndnote footEndnote = (XWPFAbstractFootnoteEndnote)iBody;
CTFtnEdn ctFtnEdn = footEndnote.getCTFtnEdn();
cursor = ctFtnEdn.newCursor();
}
if (cursor != null) {
cursor.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:r");
while (cursor.hasNextSelection()) {
cursor.toNextSelection();
XmlObject obj = cursor.getObject();
ctrInIBody.add(obj);
}
}
return ctrInIBody ;
}
private void printAllTextInTextRunsOfIBody(IBody iBody) throws Exception {
List<XmlObject> ctrInIBody = getAllCTRs(iBody);
for (XmlObject obj : ctrInIBody) {
CTR ctr = CTR.Factory.parse(obj.xmlText());
for (CTText ctText : ctr.getTList()) {
String text = ctText.getStringValue();
System.out.println(text);
}
}
}
private void replaceTextInTextRunsOfIBody(IBody iBody, String placeHolder, String textValue) throws Exception {
List<XmlObject> ctrInIBody = getAllCTRs(iBody);
for (XmlObject obj : ctrInIBody) {
CTR ctr = CTR.Factory.parse(obj.xmlText());
for (CTText ctText : ctr.getTList()) {
String text = ctText.getStringValue();
if (text != null && text.contains(placeHolder)) {
text = text.replace(placeHolder, textValue);
ctText.setStringValue(text);
obj.set(ctr);
}
}
}
}
public void generateDocX() throws Exception {
FileOutputStream outputStream = new FileOutputStream(new File("./" + 1234 + "header.docx"));
//Get the template for generating the new docx file
File template = new File("./template.dotx");
XWPFDocument document = new XWPFDocument(new FileInputStream(template));
//traverse all headers
for (XWPFHeader header : document.getHeaderList()) {
printAllTextInTextRunsOfIBody(header);
replaceTextInTextRunsOfIBody(header, "<<Firstname>>", "Axel");
replaceTextInTextRunsOfIBody(header, "<<Lastname>>", "Richter");
replaceTextInTextRunsOfIBody(header, "<<ProfessionalTitle>>", "Skeptic");
}
//traverse all footers
for (XWPFFooter footer : document.getFooterList()) {
printAllTextInTextRunsOfIBody(footer);
replaceTextInTextRunsOfIBody(footer, "<<Firstname>>", "Axel");
replaceTextInTextRunsOfIBody(footer, "<<Lastname>>", "Richter");
replaceTextInTextRunsOfIBody(footer, "<<ProfessionalTitle>>", "Skeptic");
}
//traverse document body; note: tables needs not be traversed separately because they are in document body
printAllTextInTextRunsOfIBody(document);
replaceTextInTextRunsOfIBody(document, "<<Firstname>>", "Axel");
replaceTextInTextRunsOfIBody(document, "<<Lastname>>", "Richter");
replaceTextInTextRunsOfIBody(document, "<<ProfessionalTitle>>", "Skeptic");
//traverse all footnotes
for (XWPFFootnote footnote : document.getFootnotes()) {
printAllTextInTextRunsOfIBody(footnote);
replaceTextInTextRunsOfIBody(footnote, "<<Firstname>>", "Axel");
replaceTextInTextRunsOfIBody(footnote, "<<Lastname>>", "Richter");
replaceTextInTextRunsOfIBody(footnote, "<<ProfessionalTitle>>", "Skeptic");
}
//traverse all endnotes
for (XWPFEndnote endnote : document.getEndnotes()) {
printAllTextInTextRunsOfIBody(endnote);
replaceTextInTextRunsOfIBody(endnote, "<<Firstname>>", "Axel");
replaceTextInTextRunsOfIBody(endnote, "<<Lastname>>", "Richter");
replaceTextInTextRunsOfIBody(endnote, "<<ProfessionalTitle>>", "Skeptic");
}
//since document was opened from *.dotx the content type needs to be changed
document.getPackage().replaceContentType(
"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");
//Write the changes to the new docx file and close the document
document.write(outputStream);
outputStream.close();
document.close();
}
public static void main(String[] args) throws Exception {
WordEditAllIBodys app = new WordEditAllIBodys();
app.generateDocX();
}
}
Кстати: поскольку ваш документ был открыт с *.dotx
, тип контента необходимо изменить с wordprocessingml.template
на wordprocessingml.document
, В противном случае Word не откроет полученный документ *.docx
. См. Преобразование файла с расширением ".dotx" (шаблон) в "docx" (файл Word) .
Поскольку я скептически настроен c по поводу замены-заполнителя-текста-подхода Мой предпочтительный способ заполнения форм. См. Проблема с обработкой текстового документа java. Конечно, такие поля формы нельзя использовать в верхнем или нижнем колонтитуле. Таким образом, верхние и нижние колонтитулы должны создаваться с нуля в целом.