我有带控件的 Word docx 文档。我无法使用 Microsoft Word。我使用 POI 来执行 Word 文档中的某些任务,并且还使用 POI 将 docx 转换为 PDF(fr.opensagres.poi.xwpf.converter.pdf.2.0.1 和 poi 3.17 - 它们可以很好地协同工作)。我知道这不是 POI 的最后一个版本,但 3.17 版本与我发现的最后一个版本 2.0.1 配合得很好。必须是java开源免费软件。
除了控件内的文本之外,转换效果很好。它忽略了它们。因此,我想在进行转换之前删除控件并将它们替换为它们内容的文本。使用 Visual Basic 很容易,但我无法使用 Word。在 Visual Basic 中,这是通过检查文档的所有 ContentControl 并删除它们来完成的。
ForAll c In wordDoc.ContentControls
c.Delete
End ForAll
控件被删除并替换为其内容。但我迷失在 POI 中。
它不是找到一些文本并替换它。它查找所有控件并用其中的文本替换它们(删除控件),保持它们的格式。我发现了这个 使用 Apache POI 从 Word 文档中读取部分 我可以按控件查看所有控件的列表使用 AbstractXWPFSDT 的名称和内容,但它是只读的,我无法更改任何内容。我找不到以我可以管理的方式获取这些控件的方法。
有什么帮助吗?非常感谢
确实,
XWPFAbstractSDT
以及XWPFSDT
和XWPFSDTCell
使用起来不太舒服。因此,在处理 Microsot Word 内容控件时,我决定放弃这些。我使用来自 XmlObject
的 XML 的纯 XML 方法将所有这些内容控件设为 document.xml
。
代码:
List<XmlObject> extractSDTsFromBody(XWPFDocument document) {
XmlCursor xmlcursor = document.getDocument().getBody().newCursor();
QName qnameSdt = new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "sdt", "w");
List<XmlObject> allsdts = new ArrayList<XmlObject>();
while (xmlcursor.hasNextToken()) {
XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
if (tokentype.isStart()) {
if (qnameSdt.equals(xmlcursor.getName())) {
if (xmlcursor.getObject() instanceof XmlObject) {
allsdts.add(xmlcursor.getObject());
}
}
}
}
return allsdts;
}
有了
List<XmlObject> allsdts
,就可以得到 CTSdtBlock
、CTSdtRun
和 CTSdtCell
,具体取决于它是块内容控件、内联内容控件还是表格单元格内容控件。
内联内容控件
sdt
-XML-元素看起来像这样:
<w:sdt> <!-- structured document tags -->
<w:sdtPr> ... </w:sdtPr> <!-- sdt properties -->
<w:sdtContent> <!-- sdt content-->
<w:r> <!-- text run-->
...
<w:t>Text content</w:t> <!-- text -->
</w:r>
... <!-- further text runs-->
</w:sdtContent>
</w:sdt>
要仅获取 sdt 内容中的文本,可以取消设置 sdt 属性,然后将 XML
<w:r>...</w:r>
部分移出 <w:sdt><w:sdtContent> ... </w:sdtContent></w:sdt>
XML。之后 <w:sdt><w:sdtContent> ... </w:sdtContent></w:sdt>
为空,可以删除。
代码:
void replaceContentControls(List<XmlObject> allsdts) {
try {
for (XmlObject object : allsdts) {
if (object instanceof CTSdtBlock) {
CTSdtBlock ctSdtBlock = (CTSdtBlock)object;
ctSdtBlock.unsetSdtPr();
XmlCursor toHere = ctSdtBlock.newCursor();
int count = ctSdtBlock.getSdtContent().getPArray().length;
for (int i = 0; i < count; i++) {
CTP par = ctSdtBlock.getSdtContent().getPArray(0);
XmlCursor runCursor = par.newCursor();
runCursor.moveXml(toHere);
}
XmlCursor cursor = ctSdtBlock.newCursor();
cursor.removeXml();
} else if (object instanceof CTSdtRun) {
CTSdtRun ctSdtRun = (CTSdtRun)object;
ctSdtRun.unsetSdtPr();
XmlCursor toHere = ctSdtRun.newCursor();
int count = ctSdtRun.getSdtContent().getRArray().length;
for (int i = 0; i < count; i++) {
CTR run = ctSdtRun.getSdtContent().getRArray(0);
XmlCursor runCursor = run.newCursor();
runCursor.moveXml(toHere);
}
XmlCursor cursor = ctSdtRun.newCursor();
cursor.removeXml();
} else if (object instanceof CTSdtCell) {
CTSdtCell ctSdtCell = (CTSdtCell)object;
ctSdtCell.unsetSdtPr();
XmlCursor toHere = ctSdtCell.newCursor();
int count = ctSdtCell.getSdtContent().getTcArray().length;
for (int i = 0; i < count; i++) {
CTTc cell = ctSdtCell.getSdtContent().getTcArray(0);
XmlCursor runCursor = cell.newCursor();
runCursor.moveXml(toHere);
}
XmlCursor cursor = ctSdtCell.newCursor();
cursor.removeXml();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
完整示例:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.ArrayList;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import javax.xml.namespace.QName;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
public class WordReplaceContentControls {
private static List<XmlObject> extractSDTsFromBody(XWPFDocument document) {
XmlCursor xmlcursor = document.getDocument().getBody().newCursor();
QName qnameSdt = new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "sdt", "w");
List<XmlObject> allsdts = new ArrayList<XmlObject>();
while (xmlcursor.hasNextToken()) {
XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
if (tokentype.isStart()) {
if (qnameSdt.equals(xmlcursor.getName())) {
if (xmlcursor.getObject() instanceof XmlObject) {
allsdts.add(xmlcursor.getObject());
}
}
}
}
return allsdts;
}
static void replaceContentControls(List<XmlObject> allsdts) {
try {
for (XmlObject object : allsdts) {
if (object instanceof CTSdtBlock) {
CTSdtBlock ctSdtBlock = (CTSdtBlock)object;
ctSdtBlock.unsetSdtPr();
XmlCursor toHere = ctSdtBlock.newCursor();
int count = ctSdtBlock.getSdtContent().getPArray().length;
for (int i = 0; i < count; i++) {
CTP par = ctSdtBlock.getSdtContent().getPArray(0);
XmlCursor runCursor = par.newCursor();
runCursor.moveXml(toHere);
}
XmlCursor cursor = ctSdtBlock.newCursor();
cursor.removeXml();
} else if (object instanceof CTSdtRun) {
CTSdtRun ctSdtRun = (CTSdtRun)object;
ctSdtRun.unsetSdtPr();
XmlCursor toHere = ctSdtRun.newCursor();
int count = ctSdtRun.getSdtContent().getRArray().length;
for (int i = 0; i < count; i++) {
CTR run = ctSdtRun.getSdtContent().getRArray(0);
XmlCursor runCursor = run.newCursor();
runCursor.moveXml(toHere);
}
XmlCursor cursor = ctSdtRun.newCursor();
cursor.removeXml();
} else if (object instanceof CTSdtCell) {
CTSdtCell ctSdtCell = (CTSdtCell)object;
ctSdtCell.unsetSdtPr();
XmlCursor toHere = ctSdtCell.newCursor();
int count = ctSdtCell.getSdtContent().getTcArray().length;
for (int i = 0; i < count; i++) {
CTTc cell = ctSdtCell.getSdtContent().getTcArray(0);
XmlCursor runCursor = cell.newCursor();
runCursor.moveXml(toHere);
}
XmlCursor cursor = ctSdtCell.newCursor();
cursor.removeXml();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
try (XWPFDocument document = new XWPFDocument(new FileInputStream("./WordFormContentControls.docx"));
FileOutputStream out = new FileOutputStream("./WordFormContentControlsResult.docx"); ) {
List<XmlObject> allsdts = extractSDTsFromBody(document);
replaceContentControls(allsdts);
document.write(out);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
如果
./WordFormContentControls.docx
包含内容控件,则运行代码后,./WordFormContentControlsResult.docx
应仅包含文本而不包含控件。