Gyakori feladat, hogy valamely alkalmazásunkból Office dokumentumot kell előállítanunk. Mivel az Office 2007 alapértelmezett fájl formátuma, az Office Open XML éppen ebben a hónapban kapta meg a szükséges szavazatokat, hogy az ECMA (ECMA 376, 2006. december) után ISO szabvánnyá is válhasson, aktuális a téma!
Az egyszerűség kedvéért koncentráljunk a Wordre. Talán már köztudott, hogy az új DOCX fájl valójában egy ZIP állomány, benne sok apró fájllal, melynek többsége XML. Ki is próbálhatjuk, készítsünk például egy igen egyszerű dokumentumot:
Mentsük el a dokumentumot mondjuk Szia.docx néven, majd nevezzük át Szia.docx.zip-re és akár a Windows segítségével csomagoljuk ki. Elég sok fájlt fogunk találni benne, hogy a lényegre térjek, a dokumentum szövege a worddocument.xml fájlban található:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"> <w:body> <w:p w:rsidR="00A268F4" w:rsidRDefault="00632D74"> <w:r> <w:t xml:space="preserve">Szia Word 2007! </w:t> </w:r> </w:p> <w:sectPr w:rsidR="00A268F4" w:rsidSect="00E20B73"> <w:pgSz w:w="11906" w:h="16838"/> <w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0"/> <w:cols w:space="708"/> <w:docGrid w:linePitch="360"/> </w:sectPr> </w:body> </w:document>
A lényeg ott van középen, az xml:space=preserve attribútumnak köszönhetően még az utolsó szóköz is megmaradt.
A .NET Framework 3.0 verziójától kezdve van lehetőségünk arra, hogy ezt a tömörített fájlt, az ún. package-et programozottan kezeljük a System.IO.Packaging névtérben lévő Packaging API segítségével. Az API támogatása addig terjed, hogy meg tudja nyitni a package-et és segít elérni benne az egyes részeket, az ún. package part-okat. A part-ok tartalmát streamek formájában tudjuk elérni és mivel többségük formátuma XML, ezért a System.Xml névtérben található jól ismert osztályokkal gyerekjáték (?) a tartalom módosítása.
Ezek ismeretében hogyan foghatunk hozzá egy Word dokumentum előállításához? Több lehetőség is van:
- Az API segítségével létrehozunk egy új package-et, majd abban új part-okat, és elkezdjük írni az XML-t. Mivel ebben az esetben tiszta lappal indulunk, ismernünk kell a Word ML nyelvet, amely értelmet ad a fenti XML-nek is. Ez nehezen járható út, de egyszerűbb esetekben működhet.
- Másik lehetőség, hogy Worddel létrehozunk egy ízlésünknek megfelelő dokumentumot és abban megjelöljük azokat a részeket, amiket cserélni szeretnénk. Ez már egyszerűbb, mert nem kell az egész dokumentumot megírnunk, csak a változó részeket kell megkeresnünk és átírnunk. Sajnos azonban ez sem triviális, mindjárt meglátjuk, miért…
Vegyük például a fenti dokumentumot és ne változtassuk meg a tartalmát, csak futtassunk egy helyesírás ellenőrzést, mégpedig angol nyelven. Szegény Word nem fogja megérteni a Szia szót, ráadásul és ezt a Word ML-ben is megjeleníti, nálam például ez lett az új document.xml (a sémákat kihagyva):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document ...> <w:body> <w:p w:rsidR="00A268F4" w:rsidRPr="00144B7E" w:rsidRDefault="00632D74"> <w:pPr> <w:rPr> <w:lang w:val="en-US"/> </w:rPr> </w:pPr> <w:proofErr w:type="spellStart"/> <w:r w:rsidRPr="00144B7E"> <w:rPr> <w:lang w:val="en-US"/> </w:rPr> <w:t>Szia</w:t> </w:r> <w:proofErr w:type="spellEnd"/> <w:r w:rsidRPr="00144B7E"> <w:rPr> <w:lang w:val="en-US"/> </w:rPr> <w:t xml:space="preserve"> Word 2007! </w:t> </w:r> </w:p> <w:sectPr w:rsidR="00A268F4" w:rsidRPr="00144B7E" w:rsidSect="00E20B73"> <w:pgSz w:w="11906" w:h="16838"/> <w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0"/> <w:cols w:space="708"/> <w:docGrid w:linePitch="360"/> </w:sectPr> </w:body> </w:document>
Bár nem változtattuk meg a dokumentum tartalmát, a belső szerkezete mégis megváltozott. Ahhoz, hogy ezt megértsük, kicsit dekódolnunk kell a fenti tag-eket:
- A w:p egy paragraph, azaz egy bekezdés leírása.
- A bekezdés tulajdonságai, jelen esetben, hogy angol nyelvű, a w:pPr elemben, paragraph properties elemben találhatóak.
- A bekezdésen belül ún. run elem található, erre vonatkozik a w:r. A run tulajdonságait a run properties elem, a w:rPr tartalmazza.
- A szöveg a run elemen belül text elemekben található, ez a w:t.
Ezek közül a legfontosabb a run. A run ugyanis a legkisebb formázható egység a Wordben. Másként fogalmazva: nem szövegre alkalmazzuk a formázásokat, hanem vagy bekezdésre, vagy run-ra. Tehát ha van egy összefüggő szövegünk, majd azt elkezdjük formázgatni, run-okra fog szétesni. Nézzünk erre is egy példát előbb Wordben:
Majd lássuk ugyanezt a bekezdést Word ML-ben is:
<w:p w:rsidR="00A268F4" w:rsidRDefault="00DF7BD5"> <w:r> <w:t>S</w:t> </w:r> <w:r w:rsidRPr="00DF7BD5"> <w:rPr> <w:b/> </w:rPr> <w:t>z</w:t> </w:r> <w:r w:rsidRPr="00DF7BD5"> <w:rPr> <w:color w:val="FF0000"/> </w:rPr> <w:t>i</w:t> </w:r> <w:r w:rsidRPr="00DF7BD5"> <w:rPr> <w:i/> </w:rPr> <w:t>a</w:t> </w:r> </w:p>
Egy kis segítség a megértéshez: a w:color talán nyilvánvaló, a hozzá tartozó FF0000 érték webfejlesztőknek különösen ismerős lehet, ez RGB-ben a piros. A w:b bold-ot, azaz félkövér formázást, a w:i pedig italic, azaz dőlt betűs formázást jelent.
Miért van szükség arra, hogy a run-on belül még újabb w:t text elemek is legyenek? Azért, mert ott nem csak szöveg lehet, lehetnek láthatatlan karakerek is. Íme két szöveg, közöttük pedig egy sortörés (nem új bekezdés!):
A sortörés egy speciális w:br tagként jelenik meg Word ML-ben:
<w:p w:rsidR="00A268F4" w:rsidRDefault="003A21DF"> <w:r> <w:t>Szia</w:t> </w:r> <w:r> <w:br/> <w:t>Word!</w:t> </w:r> </w:p>
Ráadásul teljesen jogos egy szöveget több run-ra bontani akkor is, ha azok semmiben nem különböznek egymástól. Sőt, nem csak formázáskor, hanem egyéb tulajdonságok változásakor is több run-ra tagolódik a szöveg, ahogy ezt az első példában a helyesírás ellenőrzésnél láttuk. Megváltozott az XML szerkezete, pedig mi nem is változtattunk a szövegen, de a Word igen!
Összefoglalva: bár a Word dokumentum belső formátuma XML, aminek a feldolgozásához kapunk API-t, a feladatnak az a része, hogy megtaláljuk a felülírandó szöveget az XML-ben nem egyszerű, mert az XML szerkezete változhat. Ezek alapján elmondhatjuk, hogy a kettes számmal jelölt megoldási módszerünk is nehezen megvalósíthatónak bizonyult.
Szerencsére van ennél is jobb megoldás, mégpedig…
(folytatjuk)