If somebody like the post and its helpful in your work then, add comments.

Sunday, August 9, 2009

WordProcessingML : How to update Table of Content in MS Word Document

To update the Table of content in the Microsoft word document first we have 
to create a table of content in first of document. Then write below code to
update the table of content updation.
class Program 
{
public static XNamespace ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

static void Main(string[] args)
{
using (WordprocessingDocument WorDocument = WordprocessingDocument.Create(@"C:\TestDocuments\TOCDocument.docx", WordprocessingDocumentType.Document))
{
// Add a new main document part.
MainDocumentPart mainPart = WorDocument.AddMainDocumentPart();
//Create Document tree for simple document.
mainPart.Document = new Document();
//Create Body (this element contains other elements that we want to include
Body body = new Body();

//Save changes to the main document part.
mainPart.Document.Append(body);

XDocument xmlXdocument = XDocument.Parse(mainPart.Document.InnerXml);
IEnumerable<XElement> xmlelement = xmlXdocument.Descendants(ns + "sdt");
XElement TOCRefNode = xmlelement.First();
GenerateTOC(xmlXdocument, TOCRefNode);

mainPart.Document.InnerXml = xmlXdocument.ToString();

mainPart.Document.Save();
WorDocument.Close();

}
}

#region TOC Creation

/// <summary>
///
returns title paragraphs of genereated doc fro creating toc hyperlink
/// </summary>
private static IEnumerable<XElement> TitleParagraphsElements(XDocument mainDocument)
{
IEnumerable<XElement> results = mainDocument.Descendants().Where
(
tag =>
tag.Name == ns + "p" &&
tag.Descendants(ns + "t").Count() > 0 &&
tag.Descendants().Where
(
tag2 =>
tag2.Name == ns + "pStyle" &&
(
tag2.Attribute(ns + "val").Value == "Head1" ||
tag2.Attribute(ns + "val").Value == "Head2" ||
tag2.Attribute(ns + "val").Value == "Head3" ||
tag2.Attribute(ns + "val").Value == "Head4" ||
tag2.Attribute(ns + "val").Value == "Head5" ||
tag2.Attribute(ns + "val").Value == "Head6"
)
).Count() > 0
);

return results;
}

private static void GenerateTOC(XDocument xmlMainDocument, XElement TOCRefNode)
{
int bookMarkIdCounter = 0;
int maxHeading = 1;
int tempheading = 1;

// sdtContent, will contain all the paragraphs used in the TOC
XElement sdtContent = new XElement(ns + "sdtContent");
String strContentHdr = "";
XElement xContentHdr = TOCRefNode.Elements(ns + "sdtContent").First().Descendants().Where(
tag =>
tag.Name == ns + "p" &&
tag.Descendants().Where
(
tag2 =>
tag2.Name == ns + "pStyle" &&
tag2.Attribute(ns + "val").Value == "TOCHeading"
).Count() > 0
).FirstOrDefault();

if (xContentHdr != null)
strContentHdr = xContentHdr.Descendants(ns + "t").FirstOrDefault().Value;

// some information regarding the attributes of the TOC
xContentHdr.Add(

new XElement(ns + "r",
new XElement(ns + "fldChar",
new XAttribute(ns + "fldCharType", "begin"))),
new XElement(ns + "r",
new XElement(ns + "instrText",
new XAttribute(XNamespace.Xml + "space", "preserve"),
"TOCLIMIT")),
new XElement(ns + "r",
new XElement(ns + "fldChar",
new XAttribute(ns + "fldCharType", "separate"))));
sdtContent.Add(new XElement(xContentHdr));


// for each title found it in the document, we have to wrap the run inside of it,
// with a bookmark, this bookmark will have an id which will work as an anchor,
// for link references in the TOC
foreach (XElement titleParagraph in TitleParagraphsElements(xmlMainDocument))
{
string bookmarkName = "_TOC" + bookMarkIdCounter;
XElement bookmarkStart =
new XElement(ns + "bookmarkStart",
new XAttribute(ns + "id", bookMarkIdCounter),
new XAttribute(ns + "name", bookmarkName));
XElement bookmarkEnd =
new XElement(ns + "bookmarkEnd",
new XAttribute(ns + "id", bookMarkIdCounter));

// wrap the run with bookmarkStart and bookmarkEnd
titleParagraph.AddFirst(bookmarkStart);
titleParagraph.Add(bookmarkEnd);

// get the name of the style of the parapgraph of the title, and for each one,
// choose a style to add in the paragraph inside the TOC
string referenceTitleStyle = "";
switch (titleParagraph.Descendants(ns + "pStyle").First().Attribute(ns + "val").Value)
{
case "Head1":
{
referenceTitleStyle = "TOC1";
tempheading = 1;
break;
}
case "Head2":
{
referenceTitleStyle = "TOC2";
tempheading = 2;
break;
}
case "Head3":
{
referenceTitleStyle = "TOC3";
tempheading = 3;
break;
}
case "Head4":
{
referenceTitleStyle = "TOC4";
tempheading = 4;
break;
}
case "Head5":
{
referenceTitleStyle = "TOC5";
tempheading = 5;
break;
}
case "Head6":
{
referenceTitleStyle = "TOC6";
tempheading = 6;
break;
}
}


string entryContent = "";
IEnumerable<XElement> owTList = titleParagraph.Descendants(ns + "t");
foreach (XElement entryElement in owTList)
{
entryContent += (entryElement == null ? string.Empty : entryElement.Value);
}

XElement TOCElement = null;
XElement tempTOCElement = TOCRefNode.Elements(ns + "sdtContent").First().Descendants().Where(
tag =>
tag.Name == ns + "p" &&
tag.Descendants().Where
(
tag2 =>
tag2.Name == ns + "pStyle" &&
tag2.Attribute(ns + "val").Value == referenceTitleStyle
).Count() > 0
).FirstOrDefault();

if (tempTOCElement != null)
{
if (maxHeading < tempheading)
maxHeading = tempheading;

TOCElement = new XElement(tempTOCElement);
//delete instrText which contains TOC 1-n
XElement instrTextTOC = TOCElement.Descendants().Where(
tag =>
tag.Name == ns + "r" &&
tag.Descendants().Where(
tag2 =>
tag2.Name == ns + "instrText" &&
tag2.Value.Contains(@"TOC \o ")
).Count() > 0

).FirstOrDefault();
if (instrTextTOC != null)
{
instrTextTOC.ElementsAfterSelf(ns + "r").Remove();
instrTextTOC.ElementsBeforeSelf(ns + "r").Remove();
instrTextTOC.Remove();
}

//get hyperlink node
XElement hyperlink = TOCElement.Descendants().Where(
tag =>
tag.Name == ns + "hyperlink"

).FirstOrDefault();
//update anchor attribute value
hyperlink.Attribute(ns + "anchor").Value = bookmarkName;

//get entry content node
XElement contentNode = hyperlink.Descendants().Where(
tag =>
tag.Name == ns + "r" &&
tag.Descendants().Where(
tag2 =>
tag2.Name == ns + "rStyle" &&
tag2.Attribute(ns + "val").Value == "Hyperlink"
).Count() > 0 &&
tag.Elements(ns + "t").Count() > 0

).FirstOrDefault().Elements(ns + "t").FirstOrDefault();

contentNode.Value = entryContent;

//update PAGEREF value
XElement instrText = TOCElement.Descendants().Where(
tag =>
tag.Name == ns + "instrText" &&
tag.Value.Contains("PAGEREF ")
).FirstOrDefault();
if (instrText != null)
{
instrText.Value = " PAGEREF " + bookmarkName + @" \h ";
}

sdtContent.Add(TOCElement);
bookMarkIdCounter++;
}


}

sdtContent.Descendants().Where(
tag =>
tag.Name == ns + "instrText"
&&
tag.Value.Contains("TOCLIMIT")
).FirstOrDefault().Value = String.Format(@"TOC \o ""1-{0}"" \h \z \u ", maxHeading);

sdtContent.Add(
new XElement(ns + "p",
new XElement(ns + "r",
new XElement(ns + "fldChar",
new XAttribute(ns + "fldCharType", "end")))));

// Finish the xml construction of the TOC
XElement TOC =
new XElement(ns + "sdt",
new XElement(ns + "sdtPr",
new XElement(ns + "docPartObj",
new XElement(ns + "docPartGallery",
new XAttribute(ns + "val", "Table of Contents")),
new XElement(ns + "docPartUnique"))),
sdtContent);

// add it to the original document
IEnumerable<XElement> tocNodes = xmlMainDocument.Descendants().Where
(
tag =>
tag.Name == ns + "sdt" &&
tag.Descendants(ns + "sdtContent").Count() > 0 &&
tag.Descendants().Where
(
tag2 =>
tag2.Name == ns + "p" &&
tag2.Descendants().Where
(
tag3 =>
tag3.Name == ns + "pStyle" &&
(
tag3.Attribute(ns + "val").Value == "TOCHeading"
)
).Count() > 0

).Count() > 0
);

TOCRefNode.ReplaceWith(TOC);

}

#endregion
Solution is provided by open xmldeveloper.org at following
link

Download Solution

2 comments:

  1. I think the code is missing a close "}" .Please check dat.

    ReplyDelete
  2. Hi,I'm getting error with this line.
    XElement TOCRefNode = xmlelement.First();
    The error message was "Invalid operation Exception"
    will be much helpful if helped with this ..TQ.

    ReplyDelete