如何使用 Apache POI 中的文本替换所有 docx 控件

问题描述 投票:0回答:1

我有带控件的 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 的名称和内容,但它是只读的,我无法更改任何内容。我找不到以我可以管理的方式获取这些控件的方法。

有什么帮助吗?非常感谢

java ms-word apache-poi openxml export-to-pdf
1个回答
0
投票

确实,

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
应仅包含文本而不包含控件。

© www.soinside.com 2019 - 2024. All rights reserved.