文章目錄
-
- 前置博文
- 準備工作
- XML分析
- 讀取公式
- 寫入公式
- 最後
前置博文
請先閱讀前期博文以便更好了解本篇博文
- java使用poi讀取word(一)
- java使用poi讀寫word中的圖檔(二)
準備工作
需要安裝一個第三方公式插件建
- AxMath官網
- MathType中文官網(國内某公司代理)
XML分析
先來分析一下
xml
- 正常文本:
- <w:r> 标簽對應一個
對象XWPFRun
- <w:t xml:space=“preserve”> 标簽對應一段在 Word中的字元(也可以是一個字元) 公式:
- <w:object> 标簽對應一個公式(當然我們這裡隻講公式,此标簽中也可以是一個
也可以是一個Excel
等等)PPT
- <v:shape> 标簽中有個
屬性,這裡style
就是圖檔在 Word中顯示的寬高style
- <v:imagedata> 标簽關聯着顯示的圖檔( <v:imagedata>為 <v:shape>子标簽)
- <o:OLEObject>标簽關聯着圖檔顯示公式對應的二進制檔案(二進制檔案也是最重要的檔案,沒有這個檔案當你在word中輕按兩下時,是打不開第三方公式插件的)
注意:
xml中
ProgID="Equation.AxMath"
屬性标記着是使用的什麼第三方插件(我用的是 AxMath, MathType的此屬性為
ProgID="Equation.DSMT4"
)
說了這麼多估計還是迷糊的,下面上代碼跟着代碼的思路再去好好的捋一捋。
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:noProof/>
</w:rPr>
<w:t xml:space="preserve">公式前的文字 </w:t>
</w:r>
<w:r w:rsidR="00D23F9D" w:rsidRPr="0051191A">
<w:rPr>
<w:noProof/>
<w:position w:val="-10"/>
</w:rPr>
<w:object w:dxaOrig="912" w:dyaOrig="318" w14:anchorId="3B58962C">
<v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="[email protected]@[email protected]@[email protected]@[email protected]@5xe" filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype>
<v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:45.75pt;height:15.75pt" o:ole="">
<v:imagedata r:id="rId6" o:title=""/>
</v:shape>
<o:OLEObject Type="Embed" ProgID="Equation.AxMath" ShapeID="_x0000_i1025" DrawAspect="Content" ObjectID="_1626856759" r:id="rId7"/>
</w:object>
</w:r>
讀取公式
- 首先看一下Word内容
- 再來看一下讀取出來是的結果 讀取出來的檔案
@Test
public void test4() throws Exception {
XWPFDocument word = new XWPFDocument(new FileInputStream("D:\\Test\\word\\test1.docx"));
try {
List<XWPFParagraph> paragraphs = word.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
StringBuffer text = new StringBuffer();
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
Node runNode = run.getCTR().getDomNode();
text.append(getText(runNode));
String math = getMath(run, runNode);
text.append(math);
}
System.out.println("段落内容:".concat(text.toString()));
}
} finally {
word.close();
}
}
/**
* 擷取字元串
*
* @param runNode
* @return
*/
private String getText(Node runNode) {
Node textNode = getChildNode(runNode, "w:t");
if (textNode == null) {
return "";
}
return textNode.getFirstChild().getNodeValue();
}
private String getMath(XWPFRun run, Node runNode) throws Exception {
Node objectNode = getChildNode(runNode, "w:object");
if (objectNode == null) {
return "";
}
Node shapeNode = getChildNode(objectNode, "v:shape");
if (shapeNode == null) {
return "";
}
Node imageNode = getChildNode(shapeNode, "v:imagedata");
if (imageNode == null) {
return "";
}
Node binNode = getChildNode(objectNode, "o:OLEObject");
if (binNode == null) {
return "";
}
XWPFDocument word = run.getDocument();
NamedNodeMap shapeAttrs = shapeNode.getAttributes();
// 圖檔在Word中顯示的寬高
String style = shapeAttrs.getNamedItem("style").getNodeValue();
System.out.println("圖檔寬高:".concat(style));
System.out.println("--------------");
NamedNodeMap imageAttrs = imageNode.getAttributes();
// 圖檔在Word中的ID
String imageRid = imageAttrs.getNamedItem("r:id").getNodeValue();
// 擷取圖檔資訊
PackagePart imgPart = word.getPartById(imageRid);
System.out.println("圖檔名稱".concat(imgPart.getPartName().getName()));
System.out.println(imgPart.getInputStream());
System.out.println("--------------");
NamedNodeMap binAttrs = binNode.getAttributes();
// 公式二進制檔案在Word中的ID
String binRid = binAttrs.getNamedItem("r:id").getNodeValue();
// 擷取二進制檔案
PackagePart binPart = word.getPartById(binRid);
System.out.println("二進制檔案名稱:".concat(binPart.getPartName().getName()));
System.out.println(binPart.getInputStream());
System.out.println("--------------");
return "{公式#}";
}
private Node getChildNode(Node node, String nodeName) {
if (!node.hasChildNodes()) {
return null;
}
NodeList childNodes = node.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node childNode = childNodes.item(i);
if (nodeName.equals(childNode.getNodeName())) {
return childNode;
}
childNode = getChildNode(childNode, nodeName);
if (childNode != null) {
return childNode;
}
}
return null;
}
寫入公式
- <v:shapetype>标簽可以不用寫入,公式正常顯示,當然如果想寫入進去也隻需要第一個公式寫就可以,其他公式直接飲用就好
- <o:OLEObject>标簽中
屬性需要與<v:shape>标簽中的ShapeID
一緻id
- 寫入公式後的Word
@Test
public void test5() throws Exception {
XWPFDocument word = new XWPFDocument();
OutputStream stream = null;
try {
XWPFParagraph paragraph = word.createParagraph();
XWPFRun run = paragraph.createRun();
XWPFDocument doc = run.getDocument();
InputStream imgIs = new FileInputStream("D:\\Test\\word\\image1.wmf");
InputStream binIs = new FileInputStream("D:\\Test\\word\\oleObject1.bin");
org.w3c.dom.Document document = createMathType(doc, imgIs, binIs, "width:45.75pt;height:15.75pt");
// 将公式寫入run中
run.setEmbossed(true);
run.getCTR().set(XmlObject.Factory.parse(document.getDocumentElement(), POIXMLTypeLoader.DEFAULT_XML_OPTIONS));
stream = new FileOutputStream("D:\\Test\\word\\test5.docx");
word.write(stream);
} finally {
word.close();
if (stream != null) {
stream.close();
}
}
}
private org.w3c.dom.Document createMathType(XWPFDocument doc, InputStream imgIs, InputStream binIs, String style)
throws Exception {
// 添加圖檔擷取rid
String imgRid = doc.addPictureData(imgIs, Document.PICTURE_TYPE_WMF);
int rid = Integer.parseInt(imgRid.replaceAll("[a-zA-Z]", ""));
// 添加二進制檔案
final PackagePartName partName = PackagingURIHelper.createPartName("/word/embeddings/oleMath" + rid + ".bin");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(IOUtils.toByteArray(binIs));
doc.getPackage().createPart(partName, "application/vnd.openxmlformats-officedocument.oleObject", bos);
PackageRelationship prOle = doc.getPackagePart().addRelationship(partName, TargetMode.INTERNAL,
POIXMLDocument.OLE_OBJECT_REL_TYPE);
// 建立xml
String xml = createObjectXml(imgRid, prOle.getId(), rid, style);
InputSource is1 = new InputSource(new StringReader(xml));
return DocumentHelper.readDocument(is1);
}
private String createObjectXml(String imgRid, String binRid, int num, String style) {
String shapeId = "math".concat(String.valueOf(num));
StringBuffer xml = new StringBuffer();
xml.append("<w:object\n\t");
xml.append("xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"\n\t");
xml.append("xmlns:o=\"urn:schemas-microsoft-com:office:office\"\n\t");
xml.append("xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"\n\t");
xml.append("xmlns:v=\"urn:schemas-microsoft-com:vml\">\n\t");
xml.append("<v:shape id=\"").append(shapeId);
xml.append("\" o:ole=\"\" style=\"").append(style).append("\" type=\"\">\n\t");
xml.append("<v:imagedata r:id=\"").append(imgRid).append("\" o:title=\"\" />\n\t");
xml.append("</v:shape>\n\t");
xml.append("<o:OLEObject DrawAspect=\"Content\" ObjectID=\"\" ProgID=\"Equation.DSMT4\" ShapeID=\"");
xml.append(shapeId);
xml.append("\" Type=\"Embed\" r:id=\"").append(binRid).append("\"/>");
xml.append("</w:object>");
return xml.toString();
}
最後
如果有什麼不明白的可以留言。
歡迎大家留言讨論。