Portal dla programistów - artykuły, tutoriale, porady
September 10, 2010, 5:51 pm

Jak odczytać pliki docx w PHP

1 Gwiazdka2 Gwiazdki3 Gwiazdki4 Gwiazdki5 Gwiazdek (4 głosów, średnia: 4,50 / 5)

Najpopularniejszym pakietem biurowym na świecie jest Microsoft Office. Od dobrych kilku lat, programy takie jak Word, Excel, czy PowerPoint, zapisują dokumenty w formacie Office Open XML. Jak sama nazwa wskazuje, jest to format bazujący na XML, a co za tym idzie, możliwy do odczytania w PHP.

Autor: Maciej Wilgucki
Źródło: http://blog.wilgucki.pl/

Struktura dokumentu

Dokument Office Open XML jest niczym innym jak archiwum zip, które zawiera określoną strukturę katalogów i plików. Pliki zapisane są w formacie XML. Tak wygląda zawartość archiwum dla pliku docx.

Jak widać, struktura katalogów jest bardzo prosta. Przeznaczeniem tych katalogów jest:

  • docProps– znajdują się w nim dwa dokumenty core.xml oraz app.xml, które opisują podstawowe właściwości dokumentu
  • word – katalog znajdujący się tylko w plikach docx. Inne dokumenty Office będą zawierały katalog xl w przypadku plików Excel lub ppt w przypadku plików PowerPoint. Katalog ten zawiera kilka plików XML oraz dodatkowe podfoldery, jednak jedynym interesującym nas elementem jest plik document.xml. Jeśli w dokumencie będą osadzone jakieś obrazki, to warto zainteresować się katalogiem media, w którym są one umiejscowione.
  • _rels – zawiera plik opisujący relację między plikami w archiwum a ich przeznaczeniem
  • [Content_Types].xml – jak sama nazwa wskazuje jest to plik określający typy plików znajdujących się w archiwum

PHP i archiwa ZIP

Od wersji 5.2 PHP zawiera klasę ZipArchive, która oferuje szereg możliwości, między innymi przeglądanie zawartości archiwum. Klasa ta idealnie nadaje się do rozpoczęcia pracy z dokumentami Open XML.

$zip = new ZipArchive();
$res = $zip->open('./Dokument testowy.docx');
if($res === true) {// operacje na pliku}
else
{// obsługa błedu
echo 'Error: ' . $res;
}

Jedyne co pozostało to przeparsować dokument XML w celu pobrania treści. Do dyspozycji mamy kilka sposobów. Jeśli plik docx zawiera jedynie niesformatowany tekst, bez żadnych obrazków, tabel i innych elementów, wystarczy że pobierzemy treść pliku document.xml, znajdującego się w katalogu word i potraktujemy go funkcją strip_tags.

$document = $zip->getFromName('word/document.xml');
echo strip_tags($document);

Jeśli jednak potrzebujemy zachować formatowanie dokumentu, trzeba nieco się wysilić. W przypadku niewielkich dokumentów warto skorzystać z SimpleXML, które idealnie się nada. Duże dokumenty będą wymagały zaprzęgnięcia klasy XMLReader.

Parsowanie dokumentu

Zanim będzie można zacząć parsowanie dokumentu, należy poznać jego budowę. Okazuje się, że jest ona zadziwiająco prosta. Na początek będą nas interesować jedynie trzy tagi (wszystkie znajdują się w przestrzeni nazw w):

  • p – akapit
  • r – kolejny element w akapicie – wyraz, spacja, znak specjalny
  • t – konkretny wyraz, wyrazy

Ponieważ elementy te znajdują się w przestrzeni nazw, trzeba je stamtąd wydostać.

$xml = new SimpleXMLElement($document);
$namespaces = $xml->getNamespaces();
$w = $xml->children($namespaces['w']);

Jeśli chcielibyśmy pobrać sam tekst i podzielić go na akapity, wówczas sprawa jest prosta. W pętli wyciągamy wszystkie węzły t, będące potomkami węzłów r, które z kolei znajdują się w węzłach akapitu p. Mimo, że brzmi to nieco skomplikowanie, po spojrzeniu na kod od razu wszystko się wyjaśni.

$out = array();
foreach($w->body->p as $p) {$out[] = array();
$i = count($out) - 1;
foreach($p->r as $r) {
foreach($r->t as $t) {
if(!isset($out[$i]['text'])) $out[$i]['text'] = array();
$out[$i]['text'][] = (string)$t;
}
}
}
print_r($out);

W efekcie uzyskana zostanie tablica, zawierająca wszystkie znaki znajdujące się w dokumencie.

Tekst nie jest jedynym elementem dokumentu. Bardzo często w treści można spotkać tabelkę. Parsowanie takiego kodu jest równie proste, jak w przypadku zwykłego tekstu, wymaga jedynie kilku pętli więcej.

$out = array('table' => array('rows' => array()));
foreach($w->body->tbl as $table) {
foreach($table->tr as $row) {$out['table']['rows'][] = array();
$i = count($out['table']['rows']) - 1;$out['table']['rows'][$i]['cells'] = array();
foreach($row->tc as $cell) {
$out['table']['rows'][$i]['cells'][] = array();
$j = count($out['table']['rows'][$i]['cells']) - 1;
foreach($cell->p as $p) {
foreach($p->r as $r) {
foreach($r->t as $t) {$out['table']['rows'][$i]['cells'][$j][] = (string)$t;
}
}
}
}
}
}
print_r($out);

Nic nie stoi na przeszkodzie, by z treści dokumentu pobrać obrazki, listy i inne elementy.

Jeśli chcielibyśmy oprócz samego tekstu, pobrać informacje o zastosowanych stylach, np kolor i rozmiar czcionki, zastosowana klasa (np. nagłówek 1), kolor tła, itd, musimy sprawdzić, czy w węźle r znajduje element rStyle i/lub w węźle p znajduje się element pStyle.

Jako ciekawostkę podam, że praca “w drugą stronę” jest również możliwa. Jedyne co trzeba zrobić, to stworzyć wszystkie dokumenty XML, a następnie je spakować.

Jeśli chcielibyście dokładniej zgłębić temat odczytywania i zapisywania dokumentów docx, polecam zapoznanie się z następującymi materiałami:

Podziel się na:
  • Wykop
  • Digg
  • Facebook
  • Google Bookmarks
  • Śledzik
  • Gadu-Gadu Live
  • Blip
  • Grono.net
  • PDF
  • Print
  • RSS

Wpisy autorstwa Klaudia Klimkowska.

Zostaw odpowiedź

Comment moderation is enabled. Your comment may take some time to appear.