Mittwoch, 11. Oktober 2017

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.

Keine Kommentare:

Kommentar veröffentlichen