PHP 5/CSS: Word-Dokumente einfach ins Web bringen
Schon seit vielen Versionen von Word ist die Ausgabe eines Dokuments im HTML-Format Problem. Zumindest theoretisch - denn tatsächlich ist der erzeugte Code völlig mit Tags überfrachtet, so dass die Integration dieser HTML-Erzeugnisse in eigene Webseiten keine Freude macht.
Was zum bequemen Bestücken eines Blog oder CMS also fehlt, ist eine Export-Variante, die leichtgewichtigen HTML-Code erzeugt – am besten noch mit CSS-Unterstützung, um die Formate zentral zu verwalten.
So sieht ein Word-Dokument aus, das als XML gespeichert wurde. Alle für das Skript nicht notwendigen Teile sind eingeklappt.
Basierend auf der aktuellen Word-Version 2003 ist dies mit dem Mitteln von PHP 5 einfach möglich. Denn die Dokumente lassen sich aus Word heraus im XML-Format speichern. Das kann dank der in PHP 5 integrierten SimpleXML-Funktionen einfach durchgekaut und entsprechend aufbereitet in HTML-Form wieder ausgegeben werden.
Das XML-Format von Word nutzen
Ziel des hier vorgestellten Skripts ist die Unterstützung von CSS. Dazu werden die im Word-Text verwendeten Druckformatnamen herangezogen und zu class-Verweisen umgewandelt. Allerdings muss man selbst für die Erzeugung der CSS-Datei sorgen, damit jedes Word-Format ein eine Entsprechung in der CSS-Datei besitzt.
Das Skript ist so angelegt, dass man ihm per Parameter den Namen der Word-Datei sowie den Namen der CSS-Datei übergibt. Über optionale Zusatzparameter kann man sogar noch Formatnamen angeben, deren Ausgabe unterdrückt werden soll. Nützlich ist dies beispielsweise dann, wenn manche Elemente nur bei der Nutzung des Dokuments als Ausdruck sinnvoll sind. In unserem Beispielartikel ist das der Text, der mit dem Format "Leserführung" ausgezeichnet ist, Dies ist die Zeile ganz oben im Heft, die zur einfacheren Orientierung beim Durchblättern dient. Im Browser bringt das nichts und kann daher entfallen.
Neben dem PHP-Skript finden Sie auf der Heft-CD als Beispieldokument einen Artikel aus einer vergangenen Heftausgabe zusammen mit einem beispielhaften CSS-File.
So funktioniert´s
Die Funktion extract_paragraphs() übernimmt die Hauptarbeit des Umwandlungsskripts. Der Rest des Skripts dient lediglich der Ermittlung übergebener Parameter und der Bildung des HTML-Grundgerüsts für die Ausgabe. Zu Beginn von extract_paragraphs() wird die als XML gespeicherte Word-Datei über diesen Aufruf eingelesen.
$objXml = @simplexml_load_file($strFilename);
Der Dateiname wurde im Hauptprogramm über den Aufrufparameter "quelle" ermittelt und an die Funktion übergeben. Im einfachsten Fall gibt man dabei eine lokal auf dem Server residierende Datei an. Dank der so genannten "fopen-wrappers" von PHP, klappt das aber genauso mit einem http- oder FTP-Quelldokument über die Verwendung der URL-Syntax. Das Programm muss sich um die Unterscheidung der Datenquellen nicht kümmern, weil dies völlig von PHP behandelt wird.
Noch erwähnenswert: Der Aufruf von simplexml_load_file() wird, wie gezeigt, von einem @ eingeleitet. Das bewirkt im Fehlerfall die Unterdrückung der Warnmeldung von PHP. Diese Meldung wird gegebenenfalls in den nächsten beiden Zeilen vom Skript selbst in besser verständlicher Form nachgeholt, als die "hauseigene" Warnung von PHP.
Die nächsten beiden Zeilen finden alle Knoten im XML-File heraus, die einem Absatz im Word-Dokument entsprechen:
$path_para='//w:p';
foreach($objXml->xpath($path_para) as $para) {…}
Dabei wird in der ersten Zeile die Suchmaske für die gewünschten XML-Knoten festgelegt. (Ein nützliches Tutorial zur dabei verwendeten Xpath-Syntax finden Sie auf http://www.zvon.org/xxl/XPathTutorial/General/examples.html)
Tatsächlich befinden sich die Tags der Form
Das foreach-Konstrukt durchläuft alle gefundenen Absätze, wobei der Inhalt der Variablen $para bei jedem Durchlauf dem aktuellen Absatz-Knoten entspricht.
Namensräume beachten
Um den nächsten Codeteil verstehen zu können, muss man sich ein wenig mit den "namespaces" beschäftigen. Dieses Konzept, das es in vielen Programmiersprachen gibt, sorgt dafür, dass gleichlautende Bezeichner aus verschiedenen Teilbereichen nicht zu Problemen führen, wenn sie in einem Projekt gemeinsam zum Einsatz kommen. Meist bekommen dazu die verwendeten Bezeichner einfach ein unterschiedliches Präfix verpasst – wie auch hier bei den Word-Dateien: Alle Word-spezifischen Tags beginnen mit "w:" (die allgemein Microsoft-Office zugewiesenen Tags tragen übrigens ein "o:" im Namen). Jeder offizielle Namensraum wird in einer XML-Schemadatei definiert. Für Word-Dateien ist das zum Beispiel das Dokument 'http://schemas.microsoft.com/office/word/2003/wordml'.
Das Präfix w: kam auch weiter oben schon zum Vorschein, bei der Xpath-Suchmaske. Während es dort allerdings einfach mit in den Suchstring aufgenommen werden konnte, klappt das bei den nun gewünschten SimpleXML-typischen Zugriffen in Objektmanier nicht mehr. Denn wenn man hier den Doppelpunkt mit angeben würde, bekäme man sofort von PHP einen Syntax-Error um die Ohren gehauen.
Mit Trick zum Ziel
Man kann das Namespace-Präfix allerdings mit einer Maßnahme loswerden, die im PHP-Manual so nicht zu finden ist. Angelpunkt dafür ist die Methode children() mit der alle Unterknoten eines SimpleXML-Objekts zurückgeliefert werden. Gibt man hier als Parameter die Schemadatei an, unterdrückt SimpleXML bei den Rückgabewerten den Präfix-Teil.
So kann man sich dann in der Variablen $para_children alle Tochterknoten eines Absatz-Objekts zurückliefern lassen und erhält den Namen des zugrunde liegenden Formats über folgenden Ausdruck:
$style = $para_children->pPr->pStyle['val'];
Dabei ist der letzte Teil in eckigen Klammern notwendig, weil der Name des Formats nicht als Inhalt der Tags
Brauchbares aussortieren
Nun durchläuft eine weitere foreach-Schleife die ermittelten Unterelemente des Absatzknotens und sortiert aus, was zur Ausgabe gelangen soll. Das sind einerseits alle einfachen Textteile, sowie die Hyperlinks, die im erzeugten HMTL gleich in Tags der Form <a href...> eingebettet werden sollen.
foreach($para_children as $name=>$para_child)
Der Ausdruck hinter dem "as" ist in diesem Fall keine einfache Schleifenvariable, sondern die PHP-typische Kombination aus Schlüssel und Wert. Der Schlüssel entspricht hierbei dem ersten Tag des jeweiligen Unterelements, das dringend zur Fallunterscheidung gebraucht wird.
Lautet dieses Tag
So sieht der als Beispiel verwendete Artikel in der HTML-Ansicht aus. Über den Parameter "surpress" sind dabei bestimmte Absatztypen ausgeblendet worden.
Je nach festgestelltem Element fügt der Rumpf der foreach-Schleife der Variablen $content entweder nur den Textteil oder das Hypertext-Link hinzu. Durch Anwendung der Funktion utf8_decode() wird die im XML-File angewendete Kodierung von Umlauten und Sonderzeichen wieder zurückgenommen.
Ergebnisse ausgeben
Sind alle Unterelemente des aktuellen Absatz abgearbeitet, wird der gesammelte Inhalt in einem Tag -Pärchen
geklammert ausgegeben. Dabei erhält der Klassenname den zuvor festgestellten Namen des Word-Druckformats. Der ist übrigens von Word um alle Umlaute beraubt worden. Dass dennoch beim Wiedereinladen eines Word-Textes im XML-Format alle Umlaute in den Formatnamen erscheinen, liegt an einer Umsetzungstabelle im AbschnittDie Ausgabe des Absatzes erfolgt allerdings nur, wenn das Format nicht in der "Schwarzliste" vorkommt. Die definiert man über den optionalen Parameter surpress=… beim Aufruf des Skripts. Dies dient dazu, über den Formatnamen bewusst bestimmte Textteile ausblenden zu können, weil sie etwa in der HTML-Form des Textes keinen Sinn ergeben. Sollen mehrere Formatnamen geblockt werden, muss man die Array-Form verwenden und den Parameter mehrmals aufführen, wie in
…&surpress[]=Format1&surpress[]=Format2
Das Skript bekommt dann ein Array surpress[] geliefert, das aus den beiden Strings "Format1" und "Format2" besteht. Über die Funktion in_array() wird im Skript dann geprüft, ob der Formatname des aktuellen Absatz im Array vorkommt.
Großzügig geht das Programm man mit vergessenen eckigen Klammern bei der Nennung eines einzigen unterdrückten Formats um und wandelt dessen Name selbständig in ein Array, das dann eben nur aus einem einzigen Element besteht. Ansonsten würde die PHP-Funktion in_array() mit einer Fehlermeldung abbrechen, weil sie mit einem String statt einem Array gefüttert wird.
XSL als Alternative
Während die zuvor vorgestellte Lösung zur Konvertierung eines Word-Dokuments nach HTML per PHP-Programm den XML-Code durchwühlt, gibt es noch einen ganz anderen Ansatz: Eine XSL-Datei soll die XML-Vorlage durch die Anwendung von Transformationsregeln Web-fähig machen.
Wer sich schon einmal den komplizierten Aufbau von Word-Texten in XML-Format angesehen hat, weiß, dass die Umsetzung keine leichte Aufgabe sein kann. Gottseidank hat Microsoft aber so ein XSL-File mit Namen word2xml.xsl schon selbst geschaffen. Das mit über 200 kByte dann auch recht üppig ausgefallene Werk ist im kostenlosen Produkt
Wer im Gegensatz zur hier vorgestellten Lösung eine Umsetzung nahezu aller Möglichkeiten sucht, ist damit besser beraten. Die originale URL von Microsoft kann man leider niemandem zumuten, darum hier die Adresse eines Redirectors.
Zum Vergleich der Beispielartikel in der Word-Ansicht. Die erste Zeile mit der Leserführung erscheint hier, während das Skript sie bewusst unterdrückt.
Bedingungen für die Kommentareingabe
Hinweis

Dateien