Page 1 of 1

docx4j.properties

PostPosted: Tue Apr 10, 2012 5:53 am
by DerikGW
Hello. I have a question about page margins. I did google for a while, and searched this forum, but nothing helpful came up. I have went through the "Getting Started" and some of the examples in the docx4j jar (really only one example of creating a table in a docx).

My question is, how can I set page margin to "NARROW" (0.5" margins)? I have tried through the docx4j.properties file. I was able to change the default orientation to landscape via the properties file, but it seems to ignore docx4j.PageMargins=NARROW. I also noticed in the code that the margin methods the PgMar object only accept BigInteger as arguments for top, bottom, etc. Is that why my page seems to be stuck wigh 1" margins even when I specify "NARROW"?

I am in need of narrow margins because I am generating a report that is too wide to fit with a 1" margin.

Thanks in advance for any advice/help.


Derik

Re: docx4j.properties

PostPosted: Tue Apr 10, 2012 10:19 pm
by DerikGW
The code below is what I am using. If you notice in the createTable() method, I even tried to set the margins for the table cell (the commented out code in that method). That was code taken from a current document we generate via MS Access, and then save as docx. It had no effect either. I must be missing something. Neither setting docx4j.PageMargins=NARROW in docx4j.properties works, nor does setting the <w:tblCellMar> properties (top, bottom, left, right). What am I missing? I am using the last stable build of docx4j by the way. Also, the logic below is not production logic, it is my pitiful attempt to learn the docx4j API so that I can use it in the main project.

I have also uploaded the document that was created. As you can see, the landscape orientation worked but the margins=NARROW did not (or the lowest narrow is 1", but I need 0.5"). I did look at the document.xml and styles.xml and I see no mention of margins at all. It's like it totally ignores margins. Maybe I should use the current daily development release of docx4j? I hate to put untested software in my production logic though.

Thanks again in advance for any assistance.

Code: Select all
import java.io.FileNotFoundException;
import java.math.BigInteger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.docx4j.XmlUtils;
import org.docx4j.convert.out.flatOpcXml.FlatOpcXmlCreator;
import org.docx4j.jaxb.Context;
import org.docx4j.jaxb.NamespacePrefixMapperUtils;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.samples.AbstractSample;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.TblGrid;
import org.docx4j.wml.TblGridCol;
import org.docx4j.wml.TblPr;
import org.docx4j.wml.TblWidth;
import org.docx4j.wml.Tc;
import org.docx4j.wml.TcPr;
import org.docx4j.wml.Tr;

class DocxWriter extends AbstractSample {

   public void toDocx(String[] filePath) throws Docx4JException, JAXBException {

      getInputFilePath(filePath);

      boolean save = (inputfilepath == null ? false : true);

      System.out.println("Creating package..");

      WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
            .createPackage();

      wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title",
            "Table 42 Staging Report");

      Tbl tbl = createTable(wordMLPackage.getDocumentModel().getSections()
            .get(0).getPageDimensions().getWritableWidthTwips(), 20, 23);
      wordMLPackage.getMainDocumentPart().addObject(tbl);


      // Now save it
      if (save) {
         wordMLPackage.save(new java.io.File(inputfilepath));
         System.out.println("Saved " + inputfilepath);
      } else {
         // Create a org.docx4j.wml.Package object
         FlatOpcXmlCreator worker = new FlatOpcXmlCreator(wordMLPackage);
         org.docx4j.xmlPackage.Package pkg = worker.get();

         // Now marshall it
         JAXBContext jc = Context.jcXmlPackage;
         Marshaller marshaller = jc.createMarshaller();

         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
               Boolean.TRUE);
         NamespacePrefixMapperUtils.setProperty(marshaller,
               NamespacePrefixMapperUtils.getPrefixMapper());
         System.out.println("\n\n OUTPUT ");
         System.out.println("====== \n\n ");
         marshaller.marshal(pkg, System.out);

      }

      System.out.println("Done.");

   }

   public void injectDocPropsCustomPart(WordprocessingMLPackage wordMLPackage) throws FileNotFoundException, InvalidFormatException, JAXBException {

         org.docx4j.openpackaging.parts.DocPropsCustomPart docPropsCustomPart = new org.docx4j.openpackaging.parts.DocPropsCustomPart();

         java.io.InputStream is = new java.io.FileInputStream(
               "/tmp/custompart.xml");

         docPropsCustomPart.unmarshal(is);

         wordMLPackage.addTargetPart(docPropsCustomPart);
   }

   private Tbl createTable(int writableWidthTwips, int rows, int cols) {
      
      
      int cellWidthTwips = new Double(Math.floor((writableWidthTwips / cols)))
            .intValue();

      Tbl tbl = Context.getWmlObjectFactory().createTbl();

      // ///////////////////
      
      String strTblPr = "<w:tblPr " + Namespaces.W_NAMESPACE_DECLARATION
            + ">" + "<w:tblStyle w:val=\"TableGrid\"/>"
            + "<w:tblW w:w=\"0\" w:type=\"auto\"/>"
            + "<w:tblLook w:val=\"04A0\"/>"
            + "</w:tblPr>";

      /*
      String strTblPr = "<w:tblPr "
            + "<w:tblCellMar>"
            + "<w:top w:w=\"0\" w:type=\"dxa\" />"
                + "<w:left w:w=\"108\" w:type=\"dxa\" />"
                + "<w:bottom w:w=\"0\" w:type=\"dxa\" />"
                + "<w:right w:w=\"108\" w:type=\"dxa\" />"
                + "</w:tblCellMar>"
            + "</w:tblPr>";
      */
      
      TblPr tblPr = null;

      try {
         tblPr = (TblPr) XmlUtils.unmarshalString(strTblPr);
      }

      catch (JAXBException e) {
         // Shouldn't happen
         e.printStackTrace();
      }
      tbl.setTblPr(tblPr);

      TblGrid tblGrid = Context.getWmlObjectFactory().createTblGrid();
      tbl.setTblGrid(tblGrid);

      for (int i = 0; i < cols; i++) {
         TblGridCol gridCol = Context.getWmlObjectFactory()
               .createTblGridCol();
         gridCol.setW(BigInteger.valueOf(cellWidthTwips));
         tblGrid.getGridCol().add(gridCol);
      }

      Tc tc = null;
      Tr tr = null;

      for (int j = 0; j < rows; j++) {
         tr = Context.getWmlObjectFactory().createTr();
         tbl.getContent().add(tr);

         for (int i = 0; i < cols; i++) {
            tc = Context.getWmlObjectFactory().createTc();

            tr.getContent().add(tc);

            TcPr tcPr = Context.getWmlObjectFactory().createTcPr();
            tc.setTcPr(tcPr);
            TblWidth cellWidth = Context.getWmlObjectFactory()
                  .createTblWidth();
            tcPr.setTcW(cellWidth);
            cellWidth.setType("dxa");
            cellWidth.setW(BigInteger.valueOf(cellWidthTwips));
            
            org.docx4j.wml.ObjectFactory factory = Context
                  .getWmlObjectFactory();
            org.docx4j.wml.P p = factory.createP();
            org.docx4j.wml.Text t = factory.createText();
            t.setValue("hi");
            org.docx4j.wml.R run = factory.createR();
            run.getContent().add(t);

            p.getContent().add(run);
            tc.getContent().add(p);
         }
      }
      return tbl;
   }

}


This is my docx4j.properties file:

Code: Select all
# Page size: use a value from org.docx4j.model.structure.PageSizePaper enum
# eg A4, LETTER
docx4j.PageSize=LETTER
# Page size: use a value from org.docx4j.model.structure.MarginsWellKnown enum
docx4j.PageMargins=NARROW
docx4j.PageOrientationLandscape=true

# These will be injected into docProps/app.xml
# if App.Write=true
docx4j.App.write=true
docx4j.Application=docx4j
docx4j.AppVersion=2.7
# of the form XX.YYYY where X and Y represent numerical values

# These will be injected into docProps/core.xml
docx4j.dc.write=true
docx4j.dc.creator.value=docx4j
docx4j.dc.lastModifiedBy.value=docx4j

#
#docx4j.McPreprocessor=true

# If you haven't configured log4j yourself
# docx4j will autoconfigure it.  Set this to true to disable that
docx4j.Log4j.Configurator.disabled=false

Re: docx4j.properties

PostPosted: Wed Apr 11, 2012 12:16 am
by DerikGW
Ok, this is what I have tried since the previous posts. I have generated the correctly formatted word document (rtf) from MS Access. I saved it as docx. Then I reopen the file to ensure the styling wasn't changed by converting it from rtf to docx. It was fine, so I saved it as template.docx and removed the data content from the file, keeping the styles intact. I then modified my code to load the styles.docx from the template.docx file that I created.

Code: Select all
   WordprocessingMLPackage templateMLPackage = WordprocessingMLPackage.load(new File(currentDir + "/templates/template.docx"));
   StyleDefinitionsPart style = templateMLPackage.getMainDocumentPart().getStyleDefinitionsPart();


I then set the style of the output docx file to the style from the template:

Code: Select all
   WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
         .createPackage();
      
   MainDocumentPart outputMainDocPart = wordMLPackage.getMainDocumentPart();
   StyleDefinitionsPart outputStylesPart = outputMainDocPart.getStyleDefinitionsPart();

   outputStylesPart.setJaxbElement(style.getJaxbElement());


I then verified that my table margins are there like they were in the template:

Code: Select all
<w:style w:default="true" w:styleId="TableNormal" w:type="table">
    <w:name w:val="Normal Table" />
    <w:uiPriority w:val="99" />
    <w:semiHidden />
    <w:unhideWhenUsed />
    <w:qFormat />
    <w:tblPr>
        <w:tblInd w:type="dxa" w:w="0" />
        <w:tblCellMar>
            <w:top w:type="dxa" w:w="0" />
            <w:left w:type="dxa" w:w="108" />
            <w:bottom w:type="dxa" w:w="0" />
            <w:right w:type="dxa" w:w="108" />
        </w:tblCellMar>
    </w:tblPr>
</w:style>



It looks like I successfully loaded the styles.xml and wrote it to the new docx file; however, margins still aren't working. :( I wonder now if this is a MS Word 2007 thing, but it does work when exported from MS Access and then converted to docx from rtf.

I will look through the openXML documentation again in case I missed something.

Re: docx4j.properties

PostPosted: Wed Apr 11, 2012 12:30 am
by DerikGW
I forgot to set relationships I think.

Code: Select all
                RelationshipsPart templateRelatePart = templateMLPackage.getRelationshipsPart();
                Relationships templateRelate = templateRelatePart.getJaxbElement();

                RelationshipsPart outputRelatePart = wordMLPackage.getRelationshipsPart();
                outputRelatePart.setJaxbElement(templateRelate);


The restult:

Code: Select all
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml" />
</Relationships>


It didn't make a difference because they were both the same.

Re: docx4j.properties

PostPosted: Wed Apr 11, 2012 1:02 am
by DerikGW
I think the part I might be missing is the theme1.xml document? The old version had theme1.xml but the new document does not. I am not sure how to load themes using docx4j. It doesn't seem to have a method for that, but it could be called something else. I will look through the samples again. There really needs to be better documentation for this API.

Re: docx4j.properties

PostPosted: Wed Apr 11, 2012 1:23 am
by DerikGW
Ok, it seems that I have talked myself through this. It was much simpler than I was making it out to be. All I did was load my template:

Code: Select all
    WordprocessingMLPackage templateMLPackage = WordprocessingMLPackage.load(new File(currentDir + "/templates/template.docx"));


populate it ...
Code: Select all
    templateMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title",
      "Table 42 Staging Report");

    Tbl tbl = createTable(templateMLPackage.getDocumentModel().getSections()
      .get(0).getPageDimensions().getWritableWidthTwips(), 20, 23);

    templateMLPackage.getMainDocumentPart().addObject(tbl);



and then save it under another name ...


Code: Select all
    templateMLPackage.save(new java.io.File(inputfilepath));


Here's my new table code for this example. I am setting font properties, sizes etc.

Code: Select all
   private Tbl createTable(int writableWidthTwips, int rows, int cols) throws JAXBException {

      int cellWidthTwips = new Double(Math.floor((writableWidthTwips / cols)))
            .intValue();

      Tbl tbl = Context.getWmlObjectFactory().createTbl();

      TblGrid tblGrid = Context.getWmlObjectFactory().createTblGrid();
      tbl.setTblGrid(tblGrid);

      for (int i = 0; i < cols; i++) {
         TblGridCol gridCol = Context.getWmlObjectFactory()
               .createTblGridCol();
         gridCol.setW(BigInteger.valueOf(cellWidthTwips));
         tblGrid.getGridCol().add(gridCol);
      }

      Tc tc = null;
      Tr tr = null;

      for (int j = 0; j < rows; j++) {
         tr = Context.getWmlObjectFactory().createTr();
         tbl.getContent().add(tr);

         for (int i = 0; i < cols; i++) {
            tc = Context.getWmlObjectFactory().createTc();

            tr.getContent().add(tc);

            TcPr tcPr = Context.getWmlObjectFactory().createTcPr();
            tc.setTcPr(tcPr);
            
            TblWidth cellWidth = Context.getWmlObjectFactory()
                  .createTblWidth();
            tcPr.setTcW(cellWidth);
            cellWidth.setType("dxa");
            cellWidth.setW(BigInteger.valueOf(cellWidthTwips));

            org.docx4j.wml.ObjectFactory factory = Context
                  .getWmlObjectFactory();
            org.docx4j.wml.P p = factory.createP();
            org.docx4j.wml.Text t = factory.createText();
            
            t.setValue("Hi");
            org.docx4j.wml.R run = factory.createR();
            run.getContent().add(t);

            String textString = "<w:rPr " + Namespaces.W_NAMESPACE_DECLARATION + ">"
              + "<w:rFonts w:ascii=\"Arial\" w:hAnsi=\"Arial\" w:cs=\"Arial\" />"
              + "<w:b/>"
              + "<w:color w:val=\"000000\" />"
              + "<w:sz w:val=\"12\" />"
              + "<w:szCs w:val=\"12\" />"
              + "</w:rPr>";
            
                RPr rpr = (RPr)XmlUtils.unmarshalString(textString);
            run.setRPr(rpr);
            
            p.getContent().add(run);
            tc.getContent().add(p);
         }
      }
      return tbl;
   }


Thanks all for letting me use your forums to talk meself through this! lol. The result file is attached.

Re: docx4j.properties

PostPosted: Wed Apr 11, 2012 2:51 pm
by jason
Thanks for raising this topic.

Page margins are a section property (look for sectPr in document.xml).

docx4j also has a PageDimensions object which is/can be associated with sectPr via SectionWrapper.

When you are reading a docx, DocumentModel (which contains List<SectionWrapper>) will only be set up if required (eg for PDF output).

When you are creating a docx, you can set this stuff via the createPackage method, or for an additional sectPr:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting

                PageDimensions page = new PageDimensions();
               
                SectPr sectPr = factory.createSectPr();
                body.setSectPr(sectPr);
                sectPr.setPgMar( page.getPgMar() );
 
Parsed in 0.013 seconds, using GeSHi 1.0.8.4


I've changed PageDimensions so that it reads docx4j.properties. But in summary for your purposes, with 2.7.1, either alter sectPr directly, or if via PageDimensions, using method setMargins(MarginsWellKnown m ) where you pass a value explicitly.

Re: docx4j.properties

PostPosted: Wed Apr 11, 2012 10:23 pm
by DerikGW
Ah, good information. Thanks! For the moment, loading the template.docx and then adding content to it is working pretty well. I can set up styling and everything through MS word for my templates, and then add styling to individual sections as I write the data. The one thing I can't set using unmarshal is (if I remember correctly) TcPr. It blows up with the dreaded unrecognized tag error (I don't remember the actual error, but it's the same error that pops up when you use, for example, 2010 shcema when creating a 2007 document).

I will post that error if it pops up again, but for now I have modified the logic to set the object properties for that specific object directly rather than via unmarshalling ooxml strings.