formatierten Text an ein Word-Dokument anfügen

Die Aufgabe des Tages heute: Text mit Formatierungen (vorzugsweise HTML) an ein Word-Dokument anfügen.

Die Lösung ist eigentlich recht einfach:
Das HTML wird als AlternativeFormatImportPart dem Dokument hinzugefügt und dieser dann im Körper des Dokumentes am Ende hinzugefügt.
Bei Verwendung des OpenXmlSdk sieht das wie folgt aus:

// open some docx-document
using (var docx = WordprocessingDocument.Open(newDoc, true))
using (var htmlStream = new MemoryStream(htmlBytes))
{
var mainDoc = docx.MainDocumentPart;
// some id to use for adding first & referencing later
var partId = string.Format("SPAdded{0:N}",Guid.NewGuid());
// the new AlternativeFormatImportPart
var part = mainDoc.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.Html, partId);
part.FeedData(htmlStream);
// an AltChunk (referencing the AlternativeFormatImportPart by use of ID)
var partRef = new AltChunk {Id = partId};
// add the AltChunk after the last element of the document
var body = mainDoc.Document.Body;
body.InsertAfter(partRef, body.Elements().Last());
// save the document
mainDoc.Document.Save();
}

In heutigen Fall musste das alles natürlich aus dem SharePoint erfolgen und das zu verändernde Dokument als neue Version im SharePoint abgelegt werden.
Meine Lösung ist ein WebService, der entsprechend (client-Seite, Workflow, etc…) verwendet werden kann.
“Fertig” sieht das dann wie folgt aus:

public void AppendContent(string urlToDocInSharePoint, string contentToAdd)
{
if (string.IsNullOrEmpty(urlToDocInSharePoint))
{
throw new ArgumentException("file-url must be given.", "urlToDocInSharePoint");
}
if (string.IsNullOrEmpty(contentToAdd))
{
// nothing to do..
return;
}
using (var site = new SPSite(urlToDocInSharePoint))
using (var web = site.OpenWeb())
{
var webAppUrl = site.Url.Replace(web.ServerRelativeUrl, string.Empty);
var siteRelativeDocumentUrl = urlToDocInSharePoint.Replace(webAppUrl, string.Empty);
if (!siteRelativeDocumentUrl.StartsWith("/"))
{
siteRelativeDocumentUrl = "/" + siteRelativeDocumentUrl;
}
var file = web.GetFile(siteRelativeDocumentUrl);
if (!file.Exists)
{
throw new FileNotFoundException(string.Format("The given file \"{0}\" in site \"{1}\" with relative url \"{2}\" could not be found. No such file exists.",
urlToDocInSharePoint, site.Url, siteRelativeDocumentUrl));
}
if (!file.InDocumentLibrary)
{
throw new ArgumentException(string.Format("The given file \"{0}\" is not in a DocumentLibrary. Unable to modify.", siteRelativeDocumentUrl), "context");
}
var html = contentToAdd;
if (!html.StartsWith("<html", StringComparison.InvariantCultureIgnoreCase))
{
html = string.Format("<html>{0}</html>", html);
}
// according to https://stackoverflow.com/questions/18089921/add-html-string-to-openxml-docx-document the html-bytes must be UTF8-encoded with Preamble...
var htmlBytes = new UTF8Encoding(true).GetPreamble().Concat(Encoding.UTF8.GetBytes(html)).ToArray();
using (var newDoc = new MemoryStream())
{
// copy file from SP to memory
using (var spStream = file.OpenBinaryStream(SPOpenBinaryOptions.None))
{
spStream.CopyTo(newDoc);
}
newDoc.Seek(0, 0);
//open & modify docx
using (var docx = WordprocessingDocument.Open(newDoc, true))
using (var htmlStream = new MemoryStream(htmlBytes))
{
var mainDoc = docx.MainDocumentPart;
// add html as "alternative format" to document, then reference it in the main body....
var partId = string.Format("SPAdded{0:N}",Guid.NewGuid());
var part = mainDoc.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.Html, partId);
part.FeedData(htmlStream);
var partRef = new AltChunk {Id = partId};
var body = mainDoc.Document.Body;
body.InsertAfter(partRef, body.Elements().Last());
mainDoc.Document.Save();
}
newDoc.Seek(0, 0);
//push newDoc as new Version to list
var folder = file.ParentFolder;
if (folder.RequiresCheckout)
{
file.CheckOut();
}
var uploaded = folder.Files.Add(file.Url, newDoc, true);
uploaded.Update();
if (folder.RequiresCheckout)
{
uploaded.CheckIn("Updated via WebService", SPCheckinType.MajorCheckIn);
uploaded.Publish("Updated via WebService");
}
}
}
}

Das ganze hätte noch etwas generischer gefasst werden können – war aber so vorerst ausreichend.