天天看點

java使用poi讀寫word中的公式(三)

文章目錄

    • 前置博文
    • 準備工作
    • XML分析
    • 讀取公式
    • 寫入公式
    • 最後

前置博文

請先閱讀前期博文以便更好了解本篇博文

  • java使用poi讀取word(一)
  • java使用poi讀寫word中的圖檔(二)

準備工作

需要安裝一個第三方公式插件建

  1. AxMath官網
  2. MathType中文官網(國内某公司代理)

XML分析

先來分析一下

xml

正常文本:
<w:r> 标簽對應一個

XWPFRun

對象
<w:t xml:space=“preserve”> 标簽對應一段在 Word中的字元(也可以是一個字元)
公式:
<w:object> 标簽對應一個公式(當然我們這裡隻講公式,此标簽中也可以是一個

Excel

也可以是一個

PPT

等等)
<v:shape> 标簽中有個

style

屬性,這裡

style

就是圖檔在 Word中顯示的寬高
<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内容
    java使用poi讀寫word中的公式(三)
  • 再來看一下讀取出來是的結果
    java使用poi讀寫word中的公式(三)
    讀取出來的檔案
    java使用poi讀寫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>标簽中

    ShapeID

    屬性需要與<v:shape>标簽中的

    id

    一緻
  • 寫入公式後的Word
    java使用poi讀寫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();
	}
           

最後

如果有什麼不明白的可以留言。

歡迎大家留言讨論。