Page 1 of 1

Creating TOC

PostPosted: Wed Sep 22, 2010 5:30 pm
by sharu1484
Also, I am not finding any API [methods] or samples to create TOC in the word file. Can you please help?

Re: Creating TOC

PostPosted: Thu Sep 23, 2010 3:49 pm
by desau
I'm also curious about how to do this.

Rather, in my case, I've got a TOC that was created (using Word), and I'd like to update that TOC with content that I've added via docx4j. Not sure what the TOC actually is .. is it just XML like any other P/R/Text? Do we just need to manually update it?

Re: Creating TOC

PostPosted: Fri Sep 24, 2010 12:49 am
by jason
Please see viewtopic.php?f=6&t=187

The relevant classes are org.docx4j.wml.FldChar and in ObjectFactory:

Code: Select all
    @XmlElementDecl(namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", name = "instrText", scope = R.class)
    public JAXBElement<Text> createRInstrText(Text value) {
        return new JAXBElement<Text>(_RInstrText_QNAME, Text.class, R.class, value);
    }

Re: Creating TOC

PostPosted: Fri Sep 24, 2010 5:06 am
by desau
BTW - on a side note:

Searching for "Table of Contents" did not reveal that forum topic -- I only found this one from a "TOC" search.

Searching for "Table of Contents" (with or without quotes) yields:

"The following words in your search query were ignored because they are too common words: contents table of.
You must specify at least one word to search for. Each word must consist of at least 3 characters and must not contain more than 14 characters excluding wildcards."


I know -- phpbb is a pain. I also administer a phpbb board -- although I've upgraded to 3.0, which is much better. If you haven't done a lot of custom modification, the upgrade process is very easy.. might be worth it.

Re: Creating TOC

PostPosted: Fri Sep 24, 2010 6:31 pm
by sharu1484
Jason,
Coming back to creating the TOC, as per your post you had given a link to another thread [which I had checked earlier], it is using the XML.
I wanted to know if there is a method available which would create TOC directly?
Thanks for your help once again.

Regards,
Sharad

Re: Creating TOC

PostPosted: Fri Sep 24, 2010 10:27 pm
by jason
If you are looking for a method CreateTOC("TOC \o \"1-3\" \h \z \u"), there isn't one, though it would be very easy to write one which creates the first XML snippet.

Here's how: You need to add JAXB objects which are equivalent to the XML.

You can do that by using the ObjectFactory - I've told you the relevant objects.

Or you can use XmlUtils.unmarshalString with the XML as an arg (see Getting Started for details - don't forget to add the namespace declaration)

Re: Creating TOC

PostPosted: Mon Sep 27, 2010 9:15 pm
by sharu1484
Thanks Jason,
I am trying with those classes, however FldChar.java is giving problem. I have attached the source code [AddTOC.zip ]which I have written.
It is asking for the @XmlRootElement annotation as below:
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "org.docx4j.wml.FldChar" as an element because it is missing an @XmlRootElement annotation]
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(Unknown Source)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(Unknown Source)
at org.docx4j.openpackaging.parts.JaxbXmlPart.marshal(JaxbXmlPart.java:176)
at org.docx4j.openpackaging.parts.JaxbXmlPart.marshal(JaxbXmlPart.java:153)
at org.docx4j.openpackaging.io.SaveToZipFile.saveRawXmlPart(SaveToZipFile.java:206)
at org.docx4j.openpackaging.io.SaveToZipFile.saveRawXmlPart(SaveToZipFile.java:194)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:402)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:365)
at org.docx4j.openpackaging.io.SaveToZipFile.save(SaveToZipFile.java:164)
at org.docx4j.openpackaging.io.SaveToZipFile.save(SaveToZipFile.java:101)
at org.docx4j.openpackaging.packages.WordprocessingMLPackage.save(WordprocessingMLPackage.java:202)
at com.apcc.reports.docx4j.AddTOC.main(AddTOC.java:81)
Caused by: com.sun.istack.internal.SAXException2: unable to marshal type "org.docx4j.wml.FldChar" as an element because it is missing an @XmlRootElement annotation
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayReferenceNodeProperty.serializeListBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayReferenceNodeProperty.serializeListBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayReferenceNodeProperty.serializeListBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(Unknown Source)
... 13 more

Re: Creating TOC

PostPosted: Mon Sep 27, 2010 11:36 pm
by jason
I'll look at adding the annotation in the next release (a beta of which I'm planning for the next week or so).

In the meantime, you can work around that with:

Code: Select all
   public static JAXBElement getWrappedFldChar(FldChar fldchar) {
      
      return new JAXBElement( new QName(Namespaces.NS_WORD12, "fldChar"),
            FldChar.class, fldchar);
      
   }


Adding this to your code, it looks something like:

Code: Select all
import java.io.File;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.wml.Body;
import org.docx4j.wml.FldChar;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.STFldCharType;
import org.docx4j.wml.Text;
import org.docx4j.wml.P.Hyperlink;

public class AddTOC {

   public static void main(String[] args) {
      // TODO Auto-generated method stub
      try {
         
              ObjectFactory factory = Context.getWmlObjectFactory();
             
              P p = factory.createP();             
              P.Hyperlink h = new Hyperlink();
              h.setAnchor("_Toc236597049");
              h.setHistory(true);
                 
              // You aren't adding your hyperlink to the P
              // You'll need something like
              p.getParagraphContent().add(h);
              // In what follows, you should be adding the content
              // to the h, not the p, but I'll leave that to you
             
              R r = factory.createR();
              r.setRsidRPr("TOC entry text");
              R.Tab tab = new R.Tab();
              r.getRunContent().add(tab);             
              p.getParagraphContent().add(r);
             
             
              FldChar fldchar = factory.createFldChar();
              fldchar.setFldCharType(STFldCharType.BEGIN);
              r.getRunContent().add(getWrappedFldChar(fldchar));
             
              Text txt = new Text();
              txt.setValue("PAGEREF _Toc236597049 \\h");
              txt.setSpace("preserve");
             
              // You aren't adding this anywhere, so ..
              r.getRunContent().add(
                    factory.createRInstrText(txt) );             
             
              FldChar fldcharSep = factory.createFldChar();
              fldcharSep.setFldCharType(STFldCharType.SEPARATE);
              R r1 = factory.createR();
              r1.getRunContent().add(getWrappedFldChar(fldcharSep));
             
             
              R.Tab pageTab = new R.Tab();
              r1.getRunContent().add(pageTab);
              p.getParagraphContent().add(r1);
             
             
              FldChar fldcharEnd = factory.createFldChar();
             
              fldcharEnd.setFldCharType(STFldCharType.SEPARATE);
              R r3 = factory.createR();
              r.getRunContent().add(getWrappedFldChar(fldcharEnd));
              p.getParagraphContent().add(r3);
             
                           
         WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
         MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
         
         org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document)documentPart.getJaxbElement();
         Body body =  wmlDocumentEl.getBody();
         body.getEGBlockLevelElts().add(p);
         wordMLPackage.save(new File("AddTOC.docx"));
         
         
      } catch (InvalidFormatException e) {
         e.printStackTrace();
      }catch (Exception e) {
         e.printStackTrace();
      }
      
      
   }
   
   public static JAXBElement getWrappedFldChar(FldChar fldchar) {
      
      return new JAXBElement( new QName(Namespaces.NS_WORD12, "fldChar"),
            FldChar.class, fldchar);
      
   }
   
   
}



Pls note the comments I added.

hope this helps .. Jason

Re: Creating TOC

PostPosted: Mon Sep 27, 2010 11:39 pm
by jason
desau wrote:BTW - on a side note:

Searching for "Table of Contents" did not reveal that forum topic -- I only found this one from a "TOC" search.

Searching for "Table of Contents" (with or without quotes) yields:

"The following words in your search query were ignored because they are too common words: contents table of.
You must specify at least one word to search for. Each word must consist of at least 3 characters and must not contain more than 14 characters excluding wildcards."


I know -- phpbb is a pain. I also administer a phpbb board -- although I've upgraded to 3.0, which is much better. If you haven't done a lot of custom modification, the upgrade process is very easy.. might be worth it.


Thanks for pointing this out.

When I searched the forum, I got the same message.

Workaround (for now) is to use the "Search Plutext & msdn" search option instead, which is powered by Google. I'll modify the page to include this hint.

cheers .. Jason

Re: Creating TOC

PostPosted: Tue Sep 28, 2010 4:36 pm
by sharu1484
Hello Jason,
Thanks once again for the workaround and also the comments.
1> When I run this program only a bookmark is coming and it is not a TOC. I am trying to understand what each class meant for but not able to achieve much.

2> I tried generating TOC using the XML and that one is generating TOC however not adding Headings etc. Also, If I right click and select 'update field' option then I get error:
"Error! Bookmark not defined"
Forgive me if I am asking too many things but TOC is giving me nice time.

I would prefer achieving it by approach 1>. Please do the needful.

Regards,
Sharad

Re: Creating TOC

PostPosted: Wed Sep 29, 2010 12:14 am
by jason
I've updated the other thread with a post which clarifies the minimal XML required. You can use either approach to create this.

If you use the object factory approach, you can use XmlUtils's
Code: Select all
   public static String marshaltoString(Object o, boolean suppressDeclaration )


to quickly see whether what you are generating is what you are expecting.

Re: Creating TOC

PostPosted: Wed Sep 29, 2010 10:16 pm
by sharu1484
Hello Jason,
No luck with approach 1> ie using Java classes with latest code you gave.

However,the TOC is coming up with minimal XML but not being updated with all the Headings etc.
I need to manually do 'update field'.
Is this happening because I am using the XML approach?
Please help.

Regards,
Sharad

Re: Creating TOC

PostPosted: Wed Oct 20, 2010 11:34 pm
by goldburg12345
hi Jason,
I tried following code but it didn't seems working.
Haven't got any idea why? I need to generate whole TOC from scratch. I manage to generate report but now its Contents didn't seems to generate.
Can you suggest what am I doing wrong?


public void createTOC() throws Exception {

P p = factory.createP();
R r = factory.createR();

FldChar fldchar = factory.createFldChar();
fldchar.setFldCharType(STFldCharType.BEGIN);
r.getRunContent().add(getWrappedFldChar(fldchar));
p.getParagraphContent().add(r);

R r1 = factory.createR();
Text txt = new Text();
txt.setSpace("preserve");
txt.setValue("TOC \\o \"1-3\" \\h \\z \\u \\h");
r.getRunContent().add(factory.createRInstrText(txt) );
p.getParagraphContent().add(r1);

FldChar fldcharend = factory.createFldChar();
fldcharend.setFldCharType(STFldCharType.END);
R r2 = factory.createR();
r2.getRunContent().add(getWrappedFldChar(fldcharend));
p.getParagraphContent().add(r2);
body.getEGBlockLevelElts().add(p);
}

@SuppressWarnings("unchecked")
public static JAXBElement getWrappedFldChar(FldChar fldchar) {
return new JAXBElement( new QName(Namespaces.NS_WORD12, "fldChar"), FldChar.class, fldchar);
}

Re: Creating TOC

PostPosted: Thu Oct 21, 2010 11:24 pm
by jason
You need the dirty flag.

I've committed a working example at http://dev.plutext.org/trac/docx4j/brow ... ddTOC.java

Re: Creating TOC

PostPosted: Wed Oct 16, 2013 9:20 am
by jason
Plutext now offers docx4j TOC Helper, a commercial extension for docx4j, which can generate or update a table of contents, including page numbers. Please email sales@plutext.com for more information.