Adreasář osob

Dokumentace k zápočtovému příkladu z IZI238

Michal Kec

Vysoká škola ekonomická v Praze

     E-mail: <xkecm01@vse.cz>
     Web: http://sorry.vse.cz/~xkecm01/
    

Obsah

Úvod
1. Popis a účel
1.1. Popis
1.2. Účel
2. XSLT, aneb z XML do HTML
3. XML až na papír
3.1. Z XML do FO
3.2. Z FO do PDF

Úvod

Text, který právě začínáte číst je dokumentací k zápočtovému příkladu z předmětu IZI238: XML -- teorie a praxe značkovacích jazyků, vyučovaném od roku 2001 na Vysoké škole ekonomické v Praze.

Text byl napsán v textovém editoru programu FAR a v Notepadu. Celý dokument byl napsán a uložen ve formátu XML. S pomocí systému DocBook a stylů od Normana Walshe lze zdrojový XML dokument snadno převézt do různých formátu vhodných pro tisk, či publikaci na Internetu. Spolu se zdrojovým XML formátem je dokumentace k dispozici ve formátu HTML, RTF (ve verzi Word'95) a PDF.

Takto dokumentace, jakož i dokumentovaný zápočtový příklad je k dispozici na adrese http://sorry.vse.cz/~xkecm01/p_links.php#izi238

Kapitola 1. Popis a účel

Obsah

1.1. Popis
1.2. Účel

1.1. Popis

Jednoduše řečeno, jedná se o systém XML dokumentů a stylů umožňující vedení osobního, nebo firemního adresáře. Záznamem v adresáři je tedy jedna osoba a sledované údaje se dělí do několika skupin.

  1. Osobní údaje

  2. Bydliště

  3. Rodinné údaje

  4. Zaměstnání

Každá skupina dále obsahuje elementy specifické pro danou kategorii. Každá osoba musí mít vždy právě jeden výskyt každé kategorie. Kategorie zaměstnání je nepovinná. Podrobnější informace o datové struktuře poskytne přiložený DTD.

<?xml encoding="windows-1250"?>
<!ELEMENT adresar	(osoba+)>

<!ELEMENT osoba		(osobni, bydliste, rodina, zamestnani?)>
<!ATTLIST osoba
		id	ID	#REQUIRED
		jmeno	CDATA	#REQUIRED
		prijmeni CDATA	#REQUIRED
		titul_pred	CDATA #IMPLIED
		titul_za	CDATA #IMPLIED>


<!-- ========================================================= -->
<!ELEMENT osobni	(výška, váha, barva_očí?, barva_vlasů?, datum_narození, rodné_číslo, pohlaví, občanství?, národnost?)>
<!ELEMENT výška		(#PCDATA)>
<!ATTLIST výška jednotka	CDATA 'cm'>
<!ELEMENT váha		(#PCDATA)>
<!ATTLIST váha jednotka	CDATA 'kg'>
<!ELEMENT barva_očí	(#PCDATA)>
<!ELEMENT barva_vlasů	(#PCDATA)>
<!ELEMENT datum_narození (#PCDATA)>
<!ELEMENT rodné_číslo	(#PCDATA)>
<!ELEMENT pohlaví	(#PCDATA)>
<!ELEMENT občanství	(#PCDATA)>
<!ELEMENT národnost	(#PCDATA)>


<!-- ========================================================= -->
<!ELEMENT bydliste	(okres?, město, ulice?, psč, telefon*, fax*, e-mail*)>

<!ELEMENT okres		(#PCDATA)>
<!ELEMENT město		(#PCDATA)>
<!ELEMENT ulice		(#PCDATA)>
<!ELEMENT psč		(#PCDATA)>
<!ELEMENT telefon	(#PCDATA)>
<!ATTLIST telefon uto	CDATA '02'>
<!ELEMENT fax		(#PCDATA)>
<!ATTLIST fax uto	CDATA '02'>
<!ELEMENT e-mail	(#PCDATA)>


<!-- ========================================================= -->
<!ELEMENT rodina	(manžel-ka?, dítě*)>
<!ATTLIST rodina stav	(svobodný|svobodná|ženatý|vdaná|vdovec|vdova|rozvedený|rozvedená) 'svobodný'>
<!ELEMENT manžel-ka	(#PCDATA)>
<!ATTLIST manžel-ka
		id	IDREF	#IMPLIED
		jmeno	CDATA	#REQUIRED
		prijmeni CDATA	#REQUIRED>
<!ELEMENT dítě		(#PCDATA)>
<!ATTLIST dítě
		id	ID	#IMPLIED
		jmeno	CDATA	#REQUIRED
		prijmeni CDATA	#REQUIRED>


<!-- ========================================================= -->
<!ELEMENT zamestnani	(název, funkce, město?, ulice?, psč?, telefon*, fax*, e-mail*)>
<!ELEMENT název		(#PCDATA)>
<!ELEMENT funkce	(#PCDATA)>

Pro větší názornost přikládám i část zdrojového XML dokumentu se zvýrazněnou syntaxí tak, jak se zobrazuje v prohlížeči.

1.2. Účel

Účelem celé mé snahy bylo naučit se DTD, XML, jakož i XSLT a FO. Výsledné dílo je celkem použitelné, takže v budoucnosti a s drobným přizpůsobením bych to mohl využít i pro osobní potřebu. Motivací mi samozřejmě byla vidina zápočtu z předmětu IZI238: XML -- teorie a praxe značkovacích jazyků. :-)

Kapitola 2. XSLT, aneb z XML do HTML

Pro přehlednost jednotlivých záznamů jsem vybral formátování dat do tabulek. Každý záznam (každá osoba) má vlastní tabulku s vnitřním členěním podle čtyř hlavních kategorií. Abych se vyhnul přemíře formátování v HTML, připojil jsem k HTML dokumentu list stylů (CSS), které obstarávají vizuální prezentaci (pozadí, barvy,... )

Protože je dále přiložen vlastní XSL soubor, který je částečně komentován, popíšu zde pouze stručně jeho činnost a detailněji rozvedu pouze některé části.

V první části se vyberou a podle abecedy setřídí všechny záznamy osob. Využívám zde toho, že jméno je uvedeno formou atributů kořenového elementu osoba a vypíšu celé jméno (i s tituly) jako záhlaví tabulky. Poté vygeneruji základní strukturu tabulky, do které se budou vkládat data z jednotlivých kategorií.

K osobním údajům se přidává také obrázek, který je pojmenován dle atributu ID (který je pro každou osobu jedinečný). Poté se postupně vypíšou všechny osobní údaje včetně příslušných dodatečných údajů (jednotky).

Rodinné údaje: Pokud má manžel, či manželka právě zpracovávané osoby vlastní záznam v adresáři (je uveden atribut ID), pak se vytvoří odkaz na vyhledání v seznamu. Část Děti se vypisuje pouze, pokud je v záznamu alespoň jedno dítě (využití funkce <xsl:if>).

Kategorie Bydliště a Zaměstnání mají podobnou strukturu, proto je popíšu jen zběžně a dohromady. V pravé části tabulky se nachází adresa (u zaměstnání doplněna názvem firmy a funkcí). V pravé části pak jsou uvedeny telefony a faxy (včetně předvoleb) a e-maily formou odkazu. Kliknutím na odkaz se otevře asociovaný poštovní program

<?xml version="1.0" encoding="windows-1250"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="html" encoding="windows-1250"/>


<!-- Vytvoreni zakladni kostry dokumentu -->
<xsl:template match="/">
 <html>
  <head>
   <title>Adresář</title>
   <link type="text/css" rel="StyleSheet" title="Zakladni styly" href="adresar.css" />
  </head>
  <body>
  <xsl:for-each select="adresar/osoba">
  <!-- Seradime podle prijmeni -->
  <xsl:sort select="@prijmeni" />
  <xsl:sort select="@jmeno" />
  <a name="{@id}"></a>
  <!-- Kazdy zaznam ve vlastni tabulce. Zahlavi je cele jmeno -->
  <table width="90%" border="1" align="center" cellpadding="3" cellspacing="1">
   <caption>
    <xsl:value-of select="concat(@titul_pred,' ',@jmeno,' ',@prijmeni,' ',@titul_za)" />
   </caption>
  <xsl:apply-templates/>
  </table>
  </xsl:for-each>
  </body>
 </html>
</xsl:template>


<!-- Pro kazdy oddil samostatne zahlavi -->
<!-- Osobni udaje -->
<xsl:template match="osobni">
  <tr><th class="high">Osobní údaje</th>
   <!-- Odkaz na obrazek -->
   <td width="50%" rowspan="8" valign="center" align="center"><img
    src="images/{../@id}.jpg"
    alt="{../@jmeno} {../@prijmeni}" /></td>
  </tr>
  <!-- Vypis osobnich udaju. Kazdy na samostatne radce -->
  <xsl:for-each select="*">
   <tr>
    <td>
     <b><xsl:value-of select="translate(name(),'_',' ')" />
     <xsl:text>: </xsl:text></b>
     <xsl:value-of select="." />
     <xsl:text> </xsl:text>
     <xsl:value-of select="@*" />
    </td>
   </tr>
  </xsl:for-each>
</xsl:template>


<!-- Rodine udaje -->
<xsl:template match="rodina">
  <tr><th colspan="2" class="high">Rodiný stav: <xsl:value-of select="@stav" /></th></tr>
  <!-- Nejdriv testuje manzela/manzelku -->
  <xsl:apply-templates/>
  <!-- Potom testuje a vypisuje deti -->
  <xsl:if test="dítě">
  <tr><td><b>děti:</b><ul>
  <xsl:for-each select="dítě">
    <li>
     <xsl:value-of select="@jmeno" />
     <xsl:text> </xsl:text>
     <xsl:value-of select="@prijmeni" />
    </li>
  </xsl:for-each>
  </ul></td></tr>
  </xsl:if>
</xsl:template>


<xsl:template match="manžel-ka">
 <tr>
  <td><b>manžel(ka):</b>
   <xsl:value-of select="@jmeno" />
   <xsl:text> </xsl:text>
   <xsl:value-of select="@prijmeni" />
   <!-- Pokud je maznel, ci manzelka v adresari, udelame nan odkaz -->
   <xsl:if test="@id"> <i>(<a href="#{@id}">Vyhledat</a>)</i></xsl:if>
  </td>
 </tr>
</xsl:template>


<!-- Bydliste -->
<xsl:template match="bydliste">
 <tr><th colspan="2" class="high">Bydliště</th></tr>
 <!-- Do leve bunky adresu -->
 <tr>
  <td class="ods">
   <xsl:value-of select="ulice" /><br />
   <xsl:value-of select="psč" />
   <xsl:text>, </xsl:text>
   <xsl:value-of select="město" /><br />
   <i>(okres <xsl:value-of select="okres" />)</i>
  </td>
  <!-- Do prave bunky prijde to ostatni: tel, fax, mail -->
  <td>
  <xsl:apply-templates />
  </td>
 </tr>
</xsl:template>


<!-- Zamestnani je velmi podobne -->
<xsl:template match="zamestnani">
 <tr><th colspan="2" class="high">Zaměstnání</th></tr>
 <tr>
  <!-- Nejdriv nazev a funkce -->
  <td>
   <b><xsl:value-of select="název" /></b><br /><xsl:value-of select="funkce" />
  </td>
  <!-- Pak telefon, fax, mail -->
  <td rowspan="2">
   <xsl:apply-templates/>
  </td>
 </tr>
 <tr>
  <!-- Dolu pak adresu -->
  <td class="ods">
   <xsl:value-of select="ulice" /><br />
   <xsl:value-of select="psč" />
   <xsl:text>, </xsl:text>
   <xsl:value-of select="město" />
   </td>
  </tr>
</xsl:template>


<!-- Samostatna sablona vybira tel. cislo a predvolbu -->
<xsl:template match="telefon | fax">
    <b><xsl:value-of select="name()" />:</b>
     <xsl:text> </xsl:text>
     (<xsl:value-of select="@uto" />)
     <xsl:text> </xsl:text>
     <xsl:value-of select="." /><br />
</xsl:template>


<!-- Samostatna sablona vybira e-amil -->
<xsl:template match="e-mail">
    <b>e-mail:</b>
    <a href="mailto:{.}"><xsl:value-of select="." /></a><br />
</xsl:template>


<xsl:template match="text()">
 <!-- Sablona pro vynechani std. textu -->
</xsl:template>

</xsl:stylesheet>

Výsledek transformace do HTML si můžete prohlédnout na následujícím obrázku.

Kapitola 3. XML až na papír

Obsah

3.1. Z XML do FO
3.2. Z FO do PDF

3.1. Z XML do FO

Pro výstup na tiskárnu je nejprve potřeba vytvořit soubor formátovacích objektů (FO), které říkají nejen co, ale i jak se bude tisknout. Pro tvorbu FO se použije (jak jinak :-)) XSLT. Protože struktura XML dokumentu je stále stejná a rozvržení pro tisk bude podobné, jako HTML dokument zobrazený na obrazovce, lze použít XSLT pro generování HTML a upravit výstup. Prostě místo HTML tagů napsat příslušné FO tagy. :-)

Protože struktura FO tagů je poněkud složitější, než příkazy pro generování HTML, nebudu zde vše detailně popisovat a funkci jednotlivých příkazů ponechám ke studiu zvídavému čtenáři. Na počátku je definice rozvržení stránky: okraje, záhlaví, zápatí, velikost stránky atp. Poté se vypisují všechny údaje ze zdrojového XML dokumentu v rozvržení (a pořadí) obdobném, jako při tvorbě HTML. Obdobně je řešeno i vložení obrázku, odkazy mezi osobami v adresáři a odkaz e-mailové adresy.

<?xml version="1.0" encoding="windows-1250"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                version="1.0">

<!-- Vytvoreni zakladni kostry dokumentu -->
<xsl:template match="/">
 <fo:root>

  <fo:layout-master-set>
   <fo:simple-page-master page-height="297mm" page-width="210mm"
    margin-bottom="2cm" margin-left="2.5cm" margin-right="2cm"
    margin-top="2.5cm" master-name="my-master">
    <fo:region-body margin-bottom="15mm"/>
    <fo:region-after extent="10mm"/>
   </fo:simple-page-master>
  </fo:layout-master-set>

  <fo:page-sequence master-name="my-master">
   <fo:static-content flow-name="xsl-region-after">
    <fo:block font-size="75%" text-align="center">
     <!-- Číslo strany na každé stránce -->
     <xsl:text>Strana </xsl:text>
     <fo:page-number/>
    </fo:block>
   </fo:static-content>
   <fo:flow flow-name="xsl-region-body" font-family="Times Roman" font-size="12pt">
    <fo:block>
    <xsl:for-each select="adresar/osoba">
    <!-- Seradime podle prijmeni -->
    <xsl:sort select="@prijmeni"/>
    <xsl:sort select="@jmeno" />
    <!-- Kazdy zaznam ve vlastni tabulce. Zahlavi je cele jmeno -->
     <fo:block font="bold 200% Helvetica" break-before="page"
      space-before="18pt" space-before.conditionality="discard"
      space-after="6pt" keep-with-next.within-column="always"
      keep-together.within-column="always" text-align="center"
      padding="3pt" background-color="silver" id="{@id}">
      <xsl:value-of select="concat(@titul_pred,' ',@jmeno,' ',@prijmeni,' ',@titul_za)" />
     </fo:block>
     <xsl:apply-templates/>
    </xsl:for-each>
    </fo:block>
   </fo:flow>
  </fo:page-sequence>

 </fo:root>
</xsl:template>


<!-- Pro kazdy oddil samostatne zahlavi -->
<!-- Osobni udaje -->
<xsl:template match="osobni">
 <fo:block font-family="Helvetica" font-size="150%" margin-top="12pt">Osobní údaje</fo:block>
 <fo:table>
  <fo:table-body>
   <fo:table-cell starts-row="true">
  <!-- Vypis osobnich udaju. Kazdy na samostatne radce -->
  <xsl:for-each select="*">
  <fo:block padding-left="0.5cm">
   <fo:inline color="navy">
    <xsl:value-of select="translate(name(),'_',' ')" />
     <xsl:text>: </xsl:text>
    </fo:inline>
     <xsl:value-of select="." />
     <xsl:text> </xsl:text>
     <xsl:value-of select="@*" />
  </fo:block>
  </xsl:for-each>
   </fo:table-cell>
   <fo:table-cell ends-row="true">
    <fo:block text-align="center">
     <!-- Odkaz na obrazek -->
     <fo:external-graphic src="url('images/{../@id}.jpg')"/>
    </fo:block>
   </fo:table-cell>
  </fo:table-body>
 </fo:table>
</xsl:template>


<!-- Rodine udaje -->
<xsl:template match="rodina">
 <fo:block font-family="Helvetica" font-size="150%" margin-top="12pt">Rodiný stav: <xsl:value-of select="@stav" /></fo:block>
  <!-- Nejdriv testuje manzela/manzelku -->
  <xsl:apply-templates/>
  <!-- Potom testuje a vypisuje deti -->
  <xsl:if test="dítě">
   <fo:block color="navy" padding-left="0.5cm">děti:</fo:block>
    <fo:list-block margin-top="-14pt" space-after="6pt" padding-left="1.5cm">
    <xsl:for-each select="dítě">
     <fo:list-item>
      <fo:list-item-label end-indent="label-end()">
       <fo:block font-family="ZapfDingbats">&#10145;</fo:block>
      </fo:list-item-label>
      <fo:list-item-body start-indent="body-start()">
       <fo:block>
       <xsl:value-of select="@jmeno" />
       <xsl:text> </xsl:text>
       <xsl:value-of select="@prijmeni" />
       </fo:block>
      </fo:list-item-body>
     </fo:list-item>
    </xsl:for-each>
    </fo:list-block>
  </xsl:if>
</xsl:template>


<xsl:template match="manžel-ka">
 <fo:block padding-left="0.5cm">
  <xsl:choose>
   <xsl:when test="@id">
    <!-- Pokud je uvedeno Id, vytvori se odkaz -->
    <fo:basic-link color="navy" internal-destination="{@id}"
     text-decoration="underline">manžel(ka):</fo:basic-link>
   </xsl:when>
   <xsl:otherwise>
    <!-- Jinak se napise jenom jmeno -->
    <fo:inline color="navy">manžel(ka):</fo:inline>
   </xsl:otherwise>
  </xsl:choose>
  <xsl:text> </xsl:text>
  <xsl:value-of select="@jmeno" />
  <xsl:text> </xsl:text>
  <xsl:value-of select="@prijmeni" />
 </fo:block>
</xsl:template>


<!-- Bydliste -->
<xsl:template match="bydliste">
 <fo:block font-family="Helvetica" font-size="150%" margin-top="12pt">Bydliště</fo:block>
 <fo:table>
  <fo:table-body>
   <fo:table-cell starts-row="true">
    <!-- Do leve bunky adresu -->
    <fo:block padding-left="0.5cm">
     <xsl:value-of select="ulice" />
    </fo:block>
    <fo:block padding-left="0.5cm">
     <xsl:value-of select="psč" />
     <xsl:text>, </xsl:text>
     <xsl:value-of select="město" />
    </fo:block>
    <fo:block padding-left="0.5cm" font-size="85%">
     (okres <xsl:value-of select="okres" />)
    </fo:block>
   </fo:table-cell>
   <fo:table-cell ends-row="true">
    <!-- Do prave bunky prijde to ostatni: tel, fax, mail -->
    <fo:block padding-left="0.5cm">
     <xsl:text> </xsl:text>
    </fo:block>
    <xsl:apply-templates />
   </fo:table-cell>
  </fo:table-body>
 </fo:table>
</xsl:template>


<!-- Zamestnani je velmi podobne -->
<xsl:template match="zamestnani">
 <fo:block font-family="Helvetica" font-size="150%" margin-top="12pt">Zaměstnání</fo:block>
 <!-- Nejdriv nazev a funkce -->
 <fo:block padding-left="0.5cm">
  <xsl:value-of select="název" />
 </fo:block>
 <fo:block padding-left="1cm" font-size="85%">
  <xsl:value-of select="funkce" />
 </fo:block>
 <fo:table>
  <fo:table-body>
   <fo:table-cell starts-row="true">
  <!-- Dolu doleva pak adresu -->
 <fo:block padding-left="0.5cm">
   <xsl:value-of select="ulice" />
 </fo:block>
 <fo:block padding-left="0.5cm">
   <xsl:value-of select="psč" />
   <xsl:text>, </xsl:text>
   <xsl:value-of select="město" />
 </fo:block>
   </fo:table-cell>
   <fo:table-cell ends-row="true">
    <!-- Do prave bunky prijde to ostatni: tel, fax, mail -->
    <fo:block padding-left="0.5cm">
     <xsl:text> </xsl:text>
    </fo:block>
    <xsl:apply-templates />
   </fo:table-cell>
  </fo:table-body>
 </fo:table>
</xsl:template>


<!-- Samostatna sablona vybira tel. cislo a predvolbu -->
<xsl:template match="telefon | fax">
 <fo:block padding-left="0.5cm">
   <fo:inline color="navy">
    <xsl:value-of select="name()" />:
   </fo:inline>
     <xsl:text> </xsl:text>
     (<xsl:value-of select="@uto" />)
     <xsl:text> </xsl:text>
     <xsl:value-of select="." />
 </fo:block>
</xsl:template>


<!-- Samostatna sablona vybira e-amil -->
<xsl:template match="e-mail">
 <fo:block padding-left="0.5cm">
   <fo:inline color="navy">
    e-mail:
   </fo:inline>
    <fo:basic-link external-destination="url(mailto:{.})"
     color="#0000C0" text-decoration="underline"><xsl:value-of select="." /></fo:basic-link>
 </fo:block>
</xsl:template>



<xsl:template match="text()">
 <!-- Sablona pro vynechani std. textu -->
</xsl:template>

</xsl:stylesheet>

3.2. Z FO do PDF

Poté, co získáme soubor s formátovacími objekty, můžeme tento libovolně upravovat, nebo (což je nepochybně větší zábava) z něj vygenerovat soubor vhodný pro tisk. Použil jsem omezenou verzi programu XEP a vygeneroval výsledný PDF soubor.

Program XEP má bohužel problémy s Češtinou, tudíž některé znaky prostě vynechává, což poněkud kazí výsledný dojem. Na druhou stranu XEP i v omezené demoverzi zvládá dobře standardy vznikající FO včetně tabulek, vícesloupcové sazby apod. Na rozdíl od plné verze demo neumí vytvořit PostScript verzi, do každé stránky vkládá odkaz na svou domovskou stránku a od jedenácté stránky je každá lichá stránka prázdná. :-( Proto příště nebudu hloupý a pořídím si zdarma a legálně nějaký volně šířitelný FO parser. Ale ta podpora!

Jak to vše dopadlo je zřejmé z následujícího obrázku.