Page 1 of 1

How to create style definitions in a document.

PostPosted: Thu Feb 25, 2010 3:14 am
by Animal
I've got as far as

Code: Select all
        wordDocumentPackage =  new WordprocessingMLPackage();
        wordDocumentPart = new MainDocumentPart();
        wordDocumentPackage.addTargetPart(wordDocumentPart);


I am programmatically processing an XML input file, using xpath to grab the input data I need.

I need to create styled paragraphs in the output Word document, and I need to create a style definition to paragraphs on the fly.

Now, for some reason, you have to have styles defined before you add a Paragraph which references it.

I have been through the docs, but they are "flat". They are the default generated skeleton that you get if the developer doesn't bother with comments!

So given a document, how do you simply add a style definition to it?

Re: How to create style definitions in a document.

PostPosted: Thu Feb 25, 2010 9:50 am
by jason
Here is some example code, for adding a style to an existing StyleDefinitionsPart sdp:
Code: Select all
      Style myNewStyle = getDefaultParagraphStyle();
      myNewStyle = Context.getWmlObjectFactory().createStyle();
      myNewStyle.setType("paragraph");
      myNewStyle.setStyleId("myNewStyle");
      
      org.docx4j.wml.Style.Name n = Context.getWmlObjectFactory().createStyleName();
      n.setVal("myNewStyle");
      myNewStyle.setName(n);
      // Finally, add it to styles
      sdp.jaxbElement.getStyle().add(myNewStyle);         
      
      BasedOn based = Context.getWmlObjectFactory().createStyleBasedOn();
      based.setVal("Normal");      
      myNewStyle.setBasedOn(based);


An alternative to that is to create the style from a string (which you might have copied from OpenXML generated by Word). See the Getting Started Guide for how to do that.

Did you see the method createPackage()?:
Code: Select all
   public static WordprocessingMLPackage createPackage() throws InvalidFormatException {
      
            
      // Create a package
      WordprocessingMLPackage wmlPack = new WordprocessingMLPackage();

      // Create main document part
      MainDocumentPart wordDocumentPart = new MainDocumentPart();      
      
      // Create main document part content
      org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
      org.docx4j.wml.Body  body = factory.createBody();      
      org.docx4j.wml.Document wmlDocumentEl = factory.createDocument();
      
      wmlDocumentEl.setBody(body);
      
      // Create a basic sectPr using our Page model
      PageDimensions page = new PageDimensions();
      SectPr sectPr = factory.createSectPr();
      body.setSectPr(sectPr);
      sectPr.setPgSz(page.createPgSize() );
      sectPr.setPgMar(page.createPgMar());
            
      // Put the content in the part
      wordDocumentPart.setJaxbElement(wmlDocumentEl);
                  
      // Add the main document part to the package relationships
      // (creating it if necessary)
      wmlPack.addTargetPart(wordDocumentPart);
            
      // Create a styles part
      Part stylesPart = new org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart();
      try {
         ((org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart) stylesPart)
               .unmarshalDefaultStyles();
         
         // Add the styles part to the main document part relationships
         // (creating it if necessary)
         wordDocumentPart.addTargetPart(stylesPart); // NB - add it to main doc part, not package!         
         
      } catch (Exception e) {
         // TODO: handle exception
         e.printStackTrace();         
      }
      // Return the new package
      return wmlPack;
      
   }


If you know what your styles are going to be in advance, you could use an entire pre-defined styles part, which you keep say in your filesystem (similar to the above).

I'd encourage you to download the source code and look at it. There are often comments, some may be helpful others obscure. The code in which there is no comments is likely generated from the OpenXML schemas.

ps from MainDocumentPart you can getStyleDefinitionsPart();

ps2 http://dev.plutext.org/trac/docx4j/brow ... nding.java may help with your use case.

Hope this helps.

Re: How to create style definitions in a document.

PostPosted: Thu Feb 25, 2010 8:35 pm
by Animal
Thanks very much for the example code snippets.

But I can create s Style object!

It's levering it into the document that is difficult to figure out.

Your code makes reference to a mysterious "sdp" variable in order to add your new style. What's that?

I must say I find some of that code worryingly like "incantations". I don't like it when I get code which must be "just so". I hate actually asking for help in forums, but I'd never have found "Context.getWmlObjectFactory()" to get an object which creates empty WML elements!

I have actually linked through and through the docs inventing ways to use the unexplained objects in ways that might create and add styles!

I have

Code: Select all
        styleTree = wordDocumentPart.getStyleTree();


then

Code: Select all
        Tree<AugmentedStyle> pStyles = styleTree.getParagraphStylesTree();
        StyleTree.AugmentedStyle as = styleTree.new AugmentedStyle(s.getStyle());
        org.docx4j.model.styles.Node<StyleTree.AugmentedStyle> n = new org.docx4j.model.styles.Node(pStyles, id, as);
        pStyles.getRootElement().addChild(n);


Where "s" is my own encapsulation of a style definition parsed from my input XML. Its getStyle method returns an org.docx4j.wml.Style

So I'm halfway there, I just need to add that to this mysterious "sdp.jaxbElement.getStyle()"

Where do I get that?

Thanks a lot for your help BTW. I know it can be irritating when people dive into an API without reading and use a forum as a first line of help. But I'm really not doing that. I've been hacking and examining the source!

Re: How to create style definitions in a document.

PostPosted: Thu Feb 25, 2010 8:46 pm
by Animal
Actually, it's OK, sdp is "obviously" a StylesDefinitionPart. That's something I'm already accessing.

So I'm there. I have converted

Code: Select all
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://www.forwardcomputers.co.uk/schemas/page" font="sans-serif" font-size="8px" height="21cm" margin-bottom="1cm" margin-left="1cm" margin-right="1cm" margin-top="1cm" width="29.7cm">
   <body>
      <block font="Arial" font-size="48" color="red" background-color="lightblue" text-align="center">Some large, red letters on a lightblue background</block>
   </body>
</page>


And out came a landscape oriented A4 page with block of large, red text centered at the top on a light blue background.

OK, I'm on the road now!

As you can see we have a very simple kind of page description XML (a VERY much simplified version of XSL-FO... transformation through XSL-FO to PDF is very easy!).

Re: How to create style definitions in a document.

PostPosted: Thu Feb 25, 2010 9:42 pm
by jason
Glad to hear you are making progress!

So your use case is to convert documents in your page description format, to docx?

Another approach (possibly the last thing you want to hear!), would be to use XSLT to transform your XML docs into "Flat OPC XML". Word 2007 can read Flat OPC XML, but other programs can only read docx. So you might want to convert Flat OPC XML to docx, which docx4j can do for you.

Re: How to create style definitions in a document.

PostPosted: Thu Feb 25, 2010 10:10 pm
by Animal
So there's an XML format which can encapsulate a whole Word document in a single file without the need for a zip of seperate files, and "rels" files?

Sounds interesting.

But XSLT is just such a pain! It shoehorns procedural processing into XML style declarations. Everything is a pain! I wrote a transformation to transform from our page description XML into XSL-FO and it was horrible. Just horrible. And when is a string that you use as an attribute value of one of your XSLT commands a selector or the result of a selector, or the string, or a function call, or the result of the function call?

What I have done is implement a Java class which encapsulates a lot of the powerful concepts of XSL. So it parses an input document into a DOM tree, and then allows you do select and process elements of it using XPath selectors.

You can also create your output document based on calls which mirror XSLT's element and attribute creation tags.

It's based on a stack at the top of which is your "current" input node. And there are built in functions which interrogate and select from the current node.

So I took my XSLT code to create FO from this

Code: Select all
  <xsl:template match="page">

    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
      <fo:layout-master-set>
        <fo:simple-page-master master-name="document">
          <xsl:attribute name="margin-top"><xsl:value-of select="@margin-top"/></xsl:attribute>
          <xsl:attribute name="margin-right"><xsl:value-of select="@margin-right"/></xsl:attribute>
          <xsl:attribute name="margin-bottom"><xsl:value-of select="@margin-bottom"/></xsl:attribute>
          <xsl:attribute name="margin-left"><xsl:value-of select="@margin-left"/></xsl:attribute>
          <xsl:attribute name="page-width"><xsl:value-of select="@width"/></xsl:attribute>
          <xsl:attribute name="page-height"><xsl:value-of select="@height"/></xsl:attribute>
          <fo:region-body>
            <xsl:attribute name="margin-top"><xsl:value-of select="header/@height"/></xsl:attribute>
            <xsl:attribute name="margin-bottom"><xsl:value-of select="footer/@height"/></xsl:attribute>
          </fo:region-body>
          <fo:region-before region-name="header">
            <xsl:attribute name="extent"><xsl:value-of select="header/@height"/></xsl:attribute>
          </fo:region-before>
          <fo:region-after region-name="footer">
            <xsl:attribute name="extent"><xsl:value-of select="footer/@height"/></xsl:attribute>
          </fo:region-after>
        </fo:simple-page-master>
      </fo:layout-master-set>

      <fo:page-sequence master-reference="document">

<!--    Subset of page attributes applied to page-sequence -->
        <xsl:call-template name="copy-attributes">
          <xsl:with-param name="attributes" select="@font|@font-family|@font-size|@font-weight|@colour|@color"/>
        </xsl:call-template>

<!--    Only process header if there are header children, or a background image. -->
        <xsl:if test="count(header/*) + count(background-image) != 0">
          <xsl:apply-templates select="header[1]" />
        </xsl:if>

<!--    Only process footer if there are footer children. -->
        <xsl:if test="count(footer/*) != 0">
          <xsl:apply-templates select="footer[1]" />
        </xsl:if>

<!--    Process the body -->
        <xsl:apply-templates select="body" />       

      </fo:page-sequence>

    </fo:root>

  </xsl:template>


to this

Code: Select all
    @Template(match="page")
    public void transformPage() throws Exception {

        element("fo:root");
            xmlns("fo", "http://www.w3.org/1999/XSL/Format");
            element("fo:layout-master-set");
                element("fo:simple-page-master", "master-name", "document");
                    attribute("margin-top", select("@margin-top"));
                    attribute("margin-right", select("@margin-right"));
                    attribute("margin-bottom", select("@margin-bottom"));
                    attribute("margin-left", select("@margin-left"));
                    attribute("page-width", select("@width"));
                    attribute("page-height", select("@height"));
                    element("fo:region-body");
                        attribute("margin-top", select("header/@height"));
                        attribute("margin-bottom", select("footer/@height"));
                    closeElement("fo:region-body");
                    element("fo:region-before", "region-name", "header");
                        attribute("extent", select("header/@height"));
                    closeElement("fo:region-before");
                    element("fo:region-after", "region-name", "footer");
                        attribute("extent", select("footer/@height"));
                    closeElement("fo:region-after");
                closeElement("fo:simple-page-master");
            closeElement("fo:layout-master-set");

            element("fo:page-sequence", "master-reference", "document");

//              Subset of page attributes applied to page-sequence
                copyAttributes(select("@font|@font-family|@font-size|@font-weight|@colour|@color"));

//              Only process header if there are header children, or a background image. -->
                if (count("header/*") + count("background-image") != 0) {
                    applyTemplates("header[1]");
                }

//              Only process footer if there are footer children. -->
                if (count("footer/*") != 0) {
                    applyTemplates("footer[1]");
                }

//              Process the body -->
                applyTemplates("body");       
   
            closeElement("fo:page-sequence");

        closeElement("fo:root");
    }


You can see the mapping. But I'm in code! Real code in which I can easily DO STUFF!

The selector which is automatically attached to the template is attached by a Java annotation, so to kick off processing, you just call

Code: Select all
        applyTemplates();


And it applies matching methods.

Re: How to create style definitions in a document.

PostPosted: Sat Feb 27, 2010 4:05 am
by Animal
So, it's going great. I've been picking up the intricacies of creating nested "bits" and adding nested "bits" into them etc etc. All being driven by this psuedo xslt type processing.

Our report markup language only has two data-bearing elements. <block> and <table> (which obviously only allows data in the <cell> element which must be in a row)

I can convert <block> elements to paragraphs, and <table>s to <tbl>s, and I can get the main data content of a report out. (They are mainly tables)

So now I need to go a little deeper and create header and footer sections by processing our <header> and <footer> elements. No complexity here and different odd/even/first page footers. Just a <header> and <footer> which may contain text (I wrap naked text elements in paragraphs if they are not in a <block>) or <block>s or <table>s

Again, I've been scouring MainDocumentPart, Document, Part, WordprocessingMLPackage etc etc etc... I just can't find the incantation!

How do I add a header and footer please?

Re: How to create style definitions in a document.

PostPosted: Sat Feb 27, 2010 8:54 am
by jason
See this old post: viewtopic.php?f=6&t=38

I've dug up and added that old file HeaderFooter.java to the samples dir. I haven't tested it, but it looks ok, except that it will only work as it stands if image its refers to is present in your file system, or the path altered.

I'm locking this topic now, since it has wandered. If you have more questions about headers/footers, please post to that old thread, or create a new topic, thanks!