Ressourcen von der OpenXML WebCast-Reihe

Wie versprochen hier die Beispielcodes aus den WebCasts der OpenXML-Reihe.

 Folgende Samples stehen zur Verfügung:

Text aus Word-Datei extrahieren:

Wenn die im Zip-Container enthaltene Datei document.xml in ein XMLDocument geladen ist, können mit einem XPathNavigator alle Paragraph-Elemente des Dokumentes herausgeholt und verarbeitet werden:

  XmlNamespaceManager nsmgr = new XmlNamespaceManager(xDoc.NameTable);
  nsmgr.AddNamespace("w", wordprocessingML);

  XPathNavigator nav = xDoc.CreateNavigator();
  XPathNodeIterator iter = nav.Select("//w:p", nsmgr);

  ...

  while(iter.MoveNext())
  {
    string s = iter.Current.Value;
    ..
  }

 

Bilder aus Word-Dokument extrahieren:

Ausgehend vom Main Document Part werden innerhalb einer Schleife die Beziehungen herausgeholt, die auf eine Bild-Beziehung hinweisen:

  string imageRelationshipType = @"https://schemas.openxmlformats.org/officeDocument/2006/relationships/image";

  foreach (PackageRelationship imageRel in docPart.GetRelationshipsByType(imageRelationshipType))
  {
    ...
if (imageRel.TargetMode == TargetMode.External)
{ ... }
...
  }

Wenn TargetMode = External, dann befindet sich das Bild nicht im Dokument und es muß ermittelt werden, ob die imageUrl "https://" oder "file://" enthält und der entsprechenden Lademechanismus muß gestartet werden:

  Image.FromStream(WebRequest.Create(URL).GetResponse().GetResponseStream())

 

Custom XML Data Store auslesen

Angenommen, der auszulesende Custom XML Part schaut folgendermaßen aus:

  <Books xmlns="https://contoso.com/2007/Books">
    <Title>OpenXML für Alle</Title>
    <Author>Dan Appleman</Author>
  </Books>

Ebenfalls wieder ausgehend von Main Document Part werden die CustomXML Beziehungen ermittelt, der Part geladen und dann am ersten Child-Element die NamespaceUri mit dem Namespace unserer Custom Data Stores verglichen: 

  customXmlRelationshipType = "https://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml";

  foreach (PackageRelationship customXmlRel in docPart.GetRelationshipsByType(customXmlRelationshipType))
  {
    customXMLUri = PackUriHelper.ResolvePartUri(new Uri(baseUri, UriKind.Relative), customXmlRel.TargetUri);
    PackagePart customPart = zipPack.GetPart(customXMLUri);
    XmlDocument xDoc = new XmlDocument();
    xDoc.Load(customPart.GetStream(FileMode.Open));

    // entspricht gefundener Part dem gesuchten Schema?
    if (xDoc.FirstChild.NamespaceURI == customBookSchema)
    {
      XmlNamespaceManager nsmgr = new XmlNamespaceManager(xDoc.NameTable);
      nsmgr.AddNamespace("ns0", customBookSchema);
      XmlNode xn = xDoc.SelectSingleNode("ns0:Books/ns0:Title", nsmgr);
      tbTitle.Text = xn.InnerText;
      xn = xDoc.SelectSingleNode("ns0:Books/ns0:Author", nsmgr);
tbAuthor.Text = xn.InnerText;
    }
  }

 

Word-Datei erzeugen

Zuerst einmal muß die Document.xml erzeugt werden, die den Inhalt des Dokuments (WordML) enthält. Das ist pures XML. Minimal sollte diese so aussehen:

<w:document xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Beispieltext</w:t>
</w:r>
</w:p>
</w:body>
</w:document>

Dann müssen die Content Types und Relationships erzeugt und die Struktur (Document Parts) erstellt werden. Dazu gibt es in System.IO.Packaging entsprechende Klassen:

  string mainContentType = @"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml";

  string mainRelationship = @"https://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";

  ZipPack.CreatePart(mainUri, mainContentType);

  zipPack.CreateRelationship(mainUri, TargetMode.Internal, mainRelationship, "rId1");

CreatePart erzeugt nicht nur den DocumentPart, sondern trägt auch den entsprechenden Content Type (2. Parameter) im Container ein.

 

Excel-Datei erzeugen

Eigentlich ähnlich wie beim Erzeugen von Word-Dateien, nur komplizierter, da Excel-Arbeitsmappen einzelne Tabellenblätter enthalten, die in sich (fast) abgeschlossen sind. Außerdem werden Strings in einer sharedString Table abgelegt und in den Zellen nur verlinkt. Auch sind Zellen i.d.R. dazu da, neben Strings Formeln zu enthalten. Zum Glück kann man Strings auch als sog. Inline Strings, also in den Zellen ablegen. Excel wird diese dann selbsttätig in shared Strings umwandeln.

Erzeugen muß mal also die Workbook.xml, die die Beschreibung der enthaltenen Tabellenblättern enthält:

  <workbook xmlns="https://schemas.openxmlformats.org/spreadsheetml/2006/main"
            xmlns:r="https://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets>
<sheet name="Sheet1" sheetId="1" r:id="rId1"/>
</sheets>
</workbook>

Und desweiteren die Worksheet.xml, die den Inhalt der Tabelle enthält:

  <worksheet xmlns="https://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData>
<row r="1">
<c r="A1" t="inlineStr">
<is>
<t>Sample Text</t>
</is>
</c>
</row>
</sheetData>
</worksheet>

Dabei könnte eine Zelle mit Formel so aussehen:

  <c r="A1">
<f>A2+A3</f>
<v>67</>
</c>

Der <v> Tag muß nicht unbedingt vorhanden sein. Er enthält das berechnete Ergebnis der Formel und ist wichtig, wenn selbiger Wert offline, also außerhalb von Excel ausgelesen werden soll. Wenn Excel die Tabelle öffnet und der Value-Tag fehlt oder leer ist, berechnet Excel das Ergebnis neu.

Das Erzeugen dieser Dateien ist ebenfalls wieder reines XML, das Zusammenbauen ein Fall von System.IO.Packaging - CreatePart bzw. CreateRelationship (oder eben entsprechender API befreundeter Technologien, wie Java ;-)).

Für mehr Details einfach mal die Samples ansehen oder bei OpenXMLDevelop.org vorbeischauen.