1

Good day.

I must generete docx or odt file with many math formulas inside. i try to find solution in Apashe POI & ODFtoolkit but i am not was able. google doesn't help. (

May be anybody can help me with solution in this task? (any example?)

Thanks.

2 Answers2

3

You could use the docx4j library. Here is the documentation for Getting started.

I recommend that you create a template file first, with all the formatting, and as much structure as you can. Then you should put in content-controls where you want your text to go. The content controls could contain other content, that you could use to repeat some common structure.

In Word, the Content Controls can be found in the Developer tab on the main ribbon. They are named Rich Text Content Control and Plain Text Content Control. You might need to enable the Developer Tab first though. In the options, under Customize ribbon, check Developer.

To change the tag of a Content Control, click its handle on the page to select it, and then press the Control Properties button on the ribbon. You will then get a dialog where you can set the title and the tag.

In Java, the content controls will be represented in the object model as SdtBlock or SdtRun. When you are processing the template, you should replace those with the content you want.

The org.docx4j.math package contains the classes for creating math formulas.


Here is an example:

import java.*;
import java.io.*;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.namespace.*;
import org.docx4j.wml.*;
import org.docx4j.math.*;
import org.docx4j.openpackaging.packages.*;
import org.docx4j.openpackaging.parts.WordprocessingML.*;

public class Processor
{
    public static void main(String[] args) throws Exception
    {
        File inFile = new File(args[0]);
        File outFile = new File(args[1]);

        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inFile);
        MainDocumentPart mdp = wordMLPackage.getMainDocumentPart();

        Processor processor = new Processor();
        processor.processContent(mdp.getContent());

        wordMLPackage.save(outFile);
    }

    private Stack<String> tags = new Stack<String>();
    private void pushTag(String tag)
    {
        tags.push(tag);
    }
    private String getTag()
    {
        return tags.peek();
    }
    private void popTag()
    {
        tags.pop();
    }

    private static final org.docx4j.wml.ObjectFactory wmlFactory = new org.docx4j.wml.ObjectFactory();
    private static final org.docx4j.math.ObjectFactory mathFactory = new org.docx4j.math.ObjectFactory();

    public void processContent(List<Object> content)
    {
        for (Object child : content)
        {
            if (child instanceof SdtBlock)
            {
                processBlock((SdtBlock) child);
            }
            else if (child instanceof P)
            {
                processP((P) child);
            }
            else if (child instanceof JAXBElement)
            {
                JAXBElement<?> elem = (JAXBElement<?>) child;
                Class<?> elemType = elem.getDeclaredType();
                if (elemType.equals(CTOMath.class))
                {
                    processOMath((CTOMath) elem.getValue());
                }
            }
        }
    }

    public void processP(P p)
    {
        processContent(p.getContent());
    }

    public void processBlock(SdtBlock block)
    {
        String tag = block.getSdtPr().getTag().getVal();
        pushTag(tag);
        processContent(block.getSdtContent().getContent());
        popTag();
    }

    public void processOMath(CTOMath oMath)
    {
        String tag = getTag(); // tag of innermost <w:sdt>
        if (getTag().equals("MyEquation"))
        {
            List<Object> content = oMath.getEGOMathElements();
            content.clear();

            content.add(makeRun("A=\u03c0"));

            content.add(makeSSup(makeRun("r"), makeRun("2")));
        }
    }

    private JAXBElement<CTR> makeRun(String text)
    {
        // <m:r>
        CTR run = mathFactory.createCTR();
        List<Object> content = run.getContent();

        // <w:rPr><w:rFonts>
        RPr pr = wmlFactory.createRPr();
        RFonts rFonts = wmlFactory.createRFonts();
        rFonts.setAscii("Cambria Math");
        rFonts.setHAnsi("Cambria Math");
        pr.setRFonts(rFonts);
        content.add(wmlFactory.createSdtPrRPr(pr));

        // <m:t>
        CTText ctText = mathFactory.createCTText();
        ctText.setValue(text);
        content.add(mathFactory.createCTRTMath(ctText));

        return mathFactory.createCTOMathArgR(run);
    }

    private JAXBElement<CTSSup> makeSSup(Object expr, Object exp)
    {
        // <m:ssup>
        CTSSup ssup = mathFactory.createCTSSup();

        // <m:e>
        CTOMathArg eArg = mathFactory.createCTOMathArg();
        eArg.getEGOMathElements().add(expr);
        ssup.setE(eArg);

        // <m:sup>
        CTOMathArg supArg = mathFactory.createCTOMathArg();
        supArg.getEGOMathElements().add(exp);
        ssup.setSup(supArg);

        return mathFactory.createCTOMathArgSSup(ssup);
    }
}

It will look for block-level content-controls named "MyEquation", and replace the math-expressions in them with A=πr2.

Specifically, it will look for

<w:sdt>
    <w:sdtPr>
        <w:tag w:val="MyEquation"/>
    </w:sdtPr>
    <w:sdtContent>
        <w:p>
            <m:oMath>
            </m:oMath>
        </w:p>
    </w:sdtContent>
</w:sdt>

and replace it with

<w:p>
    <m:oMath>
        <m:r>
            <w:rPr>
                <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
            </w:rPr>
            <m:t>A=π</m:t>
        </m:r>
        <m:sSup>
            <m:sSupPr>
                <m:ctrlPr>
                    <w:rPr>
                        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
                    </w:rPr>
                </m:ctrlPr>
            </m:sSupPr>
            <m:e>
                <m:r>
                    <w:rPr>
                        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
                    </w:rPr>
                    <m:t>r</m:t>
                </m:r>
            </m:e>
            <m:sup>
                <m:r>
                    <w:rPr>
                        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
                    </w:rPr>
                    <m:t>2</m:t>
                </m:r>
            </m:sup>
        </m:sSup>
    </m:oMath>
</w:p>

You can make the equation in Word, and look inside the docx-file. They are stored as zip-files, containing a lot of XML. Specifically, you want to look in the word/document.xml file.

Community
  • 1
  • 1
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
  • i'm read docx4j manuals. Adding text and tables it's so ease. but i can't fint any example how add docx object to document. I experimented with org.docx4j.math class, but i didn't cucceed. May be you can give me some examlpe of adding simple formula object to document? – Aleksandr Yudin May 30 '13 at 08:46
  • thanks for your example! can you say how i can add to docx template file content-controls like in your example? in the word ribbon i see only specified content-controls (picrture, text, formated text...) but i can find such that i can setup name. ( – Aleksandr Yudin May 31 '13 at 12:20
  • Thanks for your help! I create docx file with one content-controls and give it to your code. After execute i got same file without any formulas. This is a code of document.xml file: [link](http://pastebin.ru/Q4JCsTBn). This is link to my test docx file: [link](https://www.dropbox.com/s/kx5pbu7wd4k6izr/test.docx) – Aleksandr Yudin Jun 01 '13 at 06:55
  • My test document had an empty math-equation already in there. The code just changes what it says. ``. You could change the code to actually create the `` and `` if you want. But I recommend creating as much structure in the template file as possible. – Markus Jarderot Jun 01 '13 at 09:11
  • I'm understand my mistake. I should add to word document Rich Text Content Control (not plane text), set specify tag name and add to this content control any math formula object. In that case your code work fine. Thanks for good example and coments!!! You are great!!! :) – Aleksandr Yudin Jun 01 '13 at 12:01
0

Also you can use Fmath library to generate OOXML code and then push it to docx files by doc4j library.

Here is some good examples.

http://www.fmath.info/java/latex-mathml-converter/

MJBZA
  • 4,796
  • 8
  • 48
  • 97