I managed to update the body of a Word document with the output of an XSL transformation. Now I have to add a hyperlink to the doc. The URL of a hyperlink goes to the word/document.xml.rels
file in the zip-archive of the *.docx file, so I have to do more than just changing the XSL stylesheet.
The method of this MSDN post seems to work, but I cannot get it to work in combination of updating the body.
This is the code I am working on. It is a web application, so I am reading a template Word doc but not storing any files. The template Word doc is transfered to a Stream
, modified as Stream
, and eventually output as a byte[]
array to be sent to the browser.
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
public static void ReplaceDocumentXml(Stream fsDocx, string strDocumentXml, string LinkId, string LinkUrl)
{
// Step 1: change the document body
using (WordprocessingDocument doc = WordprocessingDocument.Open(fsDocx, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
// Replace the existing Document Body with the new content.
mainPart.Document.Body = new Body(strDocumentXml);
//Save the updated output document.
//mainPart.Document.Save();
doc.Save();
}
// Step 2: change the Hyperlink
using (WordprocessingDocument doc = WordprocessingDocument.Open(fsDocx, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
// Search and delete the dummy hyperlink
IEnumerable<Hyperlink> hLinks = mainPart.Document.Body.Descendants<Hyperlink>();
foreach (Hyperlink hLink in hLinks)
{
if (hLink != null && hLink.Id == LinkId)
{
// get hyperlink's relation Id (where path stores)
HyperlinkRelationship hr = mainPart.HyperlinkRelationships.FirstOrDefault(a => a.Id == hLink.Id);
if (hr == null)
{
continue;
}
else
{
mainPart.DeleteReferenceRelationship(hr);
//mainPart.AddHyperlinkRelationship(new Uri(LinkUrl, UriKind.Absolute), true, LinkId);
break;
}
}
}
// add the actual hyperlink
mainPart.AddHyperlinkRelationship(new Uri(LinkUrl, UriKind.Absolute), true/*IsExternal*/, LinkId);
//Save the updated output document.
doc.Save();
}
return;
}
The code is in two steps. I started with both steps in one using
block, but because the hyperlink URL would not be found and replaced, I am trying to change the order. In this code I even save each modification separately.
The body is always updated OK, but the hyperlink gives troubles. If I change the hyperlink in step 1, step 2 seems to erase it from document.xml.rels
. In the code as shown, the foreach
loop does not find the id ("rId7") of the hyperlink, but also cannot add it, with exception
rId7 ID conflicts with the ID of an existing relationship for the specified source.
Now what am I doing wrong here? How to fix this problem?
For a similar project for Excel I found the ClosedXML
package on github which tremendously simplifies using OpenXML and creating spreadsheets. Does something similar exist for creating Word docs?
Edit
The problem seems to be in the Descendants<>
call; it returns no objects. Strange, because there certainly is one Hyperlink
( <w:hyperlink r:id='rId7'>
) tag.
I tried to find and count the many <w:p>
elements:
IEnumerable<Paragraph> hParagraphs = mainPart.Document.Body.Descendants<Paragraph>();
int Dummy1 = hParagraphs.Count();
int I1 = 0;
foreach(Paragraph par in hParagraphs)
{
I1++;
}
but find zero ( 0 ) elements.
Similar with table <tbl>
elements, because most (all) text is in tables; getting zero items.
This is part of the actual document:
<w:tbl>
...
<w:tc>
<w:tcPr>
...
</w:tcPr>
<w:p>
<w:pPr>
...
</w:pPr>
<w:hyperlink r:id='rId7'>
<w:r>
<w:rPr>
...
</w:rPr>
<w:t>click for settings</w:t>
</w:r>
</w:hyperlink>
</w:p>
</w:tc>
What is wrong here?