SGML


1. Introduction : HTML

Les textes disponibles sous forme électronique ne sont pas structurés : généralement, il n'y a pas moyen de savoir où est le début d'un chapitre, d'une section, pas moyen de savoir qu'il s'agit d'un dictionnaire ou de distoinguer les différentes entrées de ce dictionnaire. Les seules informations disponibles ne décrivent pas la structure du document, mais la manière dont il faut l'afficher : elles ne sont pas sémantiques mais typographiques.

HTML constitue un premier pas vers la structuration des documents électroniques : on peut indiquer des titres, des sous-titres, faire ressortir des passages, etc. Ce fut néanmoins un échec, car d'une part HTML mélange de l'information structurelle et de l'information typographique, d'autre part, il est trop limité, tant sur le plan typographique que sur celui de la structure (un document doit toujours avoir la même structure, HTML ne connait qu'un seul type de document.

SGML permet de pallier à ces problèmes. On commence par décrire la structure du document, en disant par exemple « un livre est constitué d'un titre, d'une éventuelle préface, d'une introduction, de chapitres, d'éventuels appendices ; un chapitre est constitué de sections ; les sections sont constituées de paragraphes ». On a une description différente pour chaque type de document (livre, dictionnaire, formulaire administratif, annuaire de téléphone, etc.). C'est ce que l'on appelle le DTD. Ensuite, chaque document de ce type devra se conformer à ce DTD. Ces documents sont au format SGML, c'est à dire qu'il y a des « balises » partout : cela correspond aux environements ou aux macros de LaTeX. La conversion d'un document SGML en quelque chose d'imprimable ou d'affichable sur écran est un autre problème, que nous mettons de côté pour l'instant.

2. SGML et DTD

Pour nos premier essais, nous mettrons le DTD et le document SGML dans le même fichier. Voici un exemple.
<!DOCTYPE document [
  <!ELEMENT document - - (head,body)>
  <!ELEMENT head     O O (title, author, date)>
  <!ELEMENT title    - O (#PCDATA)>
  <!ELEMENT author   - O (#PCDATA)>
  <!ELEMENT date     - O (#PCDATA)>
  <!ELEMENT body     O O (abstract?,sect1*)>
  <!ELEMENT abstract - O (p+)>
  <!ELEMENT sect1    - O (#PCDATA, p*, sect2*)>
  <!ELEMENT sect2    - O (#PCDATA, p*)>
  <!ELEMENT p        O O (#PCDATA)>
]> 

<DOCUMENT>
<HEAD>
  <TITLE> aaa
  <AUTHOR> aaa
  <DATE> aaa
<BODY>
  <ABSTRACT>
    Bla bla bla
    <P> Bla bla bla
    <P> Bla bla bla
  <SECT1> Bla bla bla
    <P> Bla bla bla
    <P> Bla bla bla
    <SECT2> Bla bla bla
      <P> Bla bla bla
      <P> Bla bla bla
    <SECT2> Bla bla bla
      <P> Bla bla bla
      <P> Bla bla bla  
  <SECT1> Bla bla bla
    <P> Bla bla bla
    <P> Bla bla bla  
  <SECT1> Bla bla bla
    <SECT2> Bla bla bla
    <SECT2> Bla bla bla  
</DOCUMENT>

Le nom du DTD

La première ligne indique le DTD. Elle peut contenir le nom d'un DTD « public » (les noms sont très bizarres...).
<!DOCTYPE Mail PUBLIC "-//GolfClub/Mail DTD/EN/1.0"> 
Elle peut contenir le nom d'un DTD local, stocké dans un fichié.
<!DOCTYPE mail SYSTEM "c:\sgml\dtd\mail.dtd">
Elle peut contenir le DTD tout entier, comme dans notre exemple.
<!DOCTYPE Mail [ ... ]>
Elle peut aussi contenir le nom d'un DTD (public ou local) accompagné de quelques modifications.
<!DOCTYPE mail SYSTEM "c:\sgml\dtd\mail.dtd" [ ... ]>

Décrire la structure d'un document dans un DTD

Dans notre exemple, un document de type document est constitué d'une tête head puis d'un corps body. La tête est constituée du titre, de l'auteur et de la date.

Quand un élément est suivi d'une étoile, cela signifie qu'il peut y en avoir un nombre quelconque (zéro ou plus) : c'est le cas des paragraphes ou des sous-sections dans une section.

Quand un élément est suivi d'un point d'interrogation, cela signifie qu'il est facultatif : il peut y en avoir zéro ou un. Dans notre exemple, c'est le cas du résumé (abstract).

Quand un élément est suivi d'un symbole plus (+), cela signifie qu'il en faut au moins un. C'est le cas des paragraphes dans le résumé : il peut y en avoir un nombre quelconque, mais il en faut au moins un (sinon, on ne met pas de résumé).

Enfin, #PCDATA signifie « n'importe quoi » : pour l'instant, il s'agit de texte sans aucune balise.

Omission des balises

Certaines balises sont facultatives. Normalement, les balises apparaissent par paire, une balise ouvrante et une balise fermante, avec éventuellement des choses entre deux. Par exemple, notre document SGML pourrait aussi s'écrire ainsi.
<!DOCTYPE document SYSTEM "document.dtd" >

<DOCUMENT>
<HEAD>
  <TITLE>  aaa </TITLE>
  <AUTHOR> aaa </AUTHOR>
  <DATE>   aaa </DATE>
</HEAD>
<BODY>
  <ABSTRACT>
    Bla bla bla
    <P> Bla bla bla </P>
    <P> Bla bla bla </P>
  </ABSTRACT>
  <SECT1> Bla bla bla
    <P> Bla bla bla </P>
    <P> Bla bla bla </P>
    <SECT2> Bla bla bla
      <P> Bla bla bla </P>
      <P> Bla bla bla </P>
    </SECT2>
    <SECT2> Bla bla bla
      <P> Bla bla bla </P>
      <P> Bla bla bla </P>
    </SECT2>
  </SECT1>
  <SECT1> Bla bla bla
    <P> Bla bla bla </P>
    <P> Bla bla bla </P>
  </SECT1>
  <SECT1> Bla bla bla
    <SECT2> Bla bla bla </SECT2>
    <SECT2> Bla bla bla </SECT2>
  </SECT1>
</BODY>
</DOCUMENT>
Mais toute cette information est redondante : quand le titre est fini, on sait que c'est l'auteur qui commence. Il n'est donc pas nécessaire d'indiquer à la fois la fin du titre et le début de l'auteur. Le titre était défini de la manière suivante :
  <!ELEMENT title    - O (#PCDATA)>
Le tiret signifie que la balise de début est obligatoire, le O signifie que la balise de fin peut être omise. On peut remarquer qu'il y a des balises qui peuvent être omises à la fois au début et à la fin : c'est le cas, par exemple, de HEAD et BODY, que l'on aurait pu complètement enlever de notre document SGML.

L'omission de la balise de fin est particulièrement justifiée quand il n'y a jamais rien entre la balise de début et la balise de fin. Par exemple, une table des matières pourrait être définie de la manière suivante (si on reprend la comparaison avec LaTeX, ce n'est plus un environement, mais une macro).

  <!ELEMENT toc      - O EMPTY>

Il y a d'autre raccourcis. Ainsi, les trois lignes suivantes sont équivalentes.

  <TT>toto</TT>
  <TT>toto</>
  <TT/toto/

Structure du document

La ligne suivante signifie que toto est constitué soit de A, soit de B.
  <!ELEMENT toto      - O (A|B)>

La ligne suivante signifie que A et B doivent apparaitre, mais l'ordre importe peu.

  <!ELEMENT toto      - O (A & B)>
C'est équivalent à la ligne suivante.
  <!ELEMENT toto      - O ((A,B)|(B,A))>

Structures plus compliquées

On veut parfois qu'une certaine balise puisse apparaître n'importe où dans le document. On peut écrire un DTD permettant ce genre de chose en remplaçant (#PCDATA) par autre chose, mais il y a une solution plus simple. Dans l'exemple suivant, les notes et les variantes peuvent apparaître n'importe où dans un poem.
  <!ELEMENT (note | variant) - - (#PCDATA)>
  <!ELEMENT poem - O (title?, (stanza+|line+) ) +(note|variant) >
Il est possible d'autoriser les notes et les variantes n'importe où dans un poème, sauf à l'intérieur de certaines balises : par exemple les titres, ou encore les notes et les variantes (pour qu'elles ne soient pas imbriquées).
  <!ELEMENT (note|variant) - - (#PCDATA)  -(note|variant) >
  <!ELEMENT poem - O (title?, (stanza+|line+) ) +(note|variant) >
  <!ELEMENT title  - O  (#PCDATA)  -(note|variant) >

Commentaires

Les commentaires sont comme en HTML.
  <!-- Ceci est un commentaire -->

Plusieures structures

Il est possible qu'un document ait plusieures découpages logiques, que l'on ne peut pas réunir. Par exemple, le découpage d'un poème en vers et son découpage en pages dans sa première édition. Dans ce genre de situation, il est possible de « mélanger » plusieurs DTD. Je ne donne pas de détails, juste un exemple (qui n'est pas de moi, d'ailleurs).
    <!DOCTYPE anthology [
    <!ELEMENT anthology      - -  (poem+)             >
    <!ELEMENT poem           - -  (title?, stanza+)   >
    <!ELEMENT stanza         - O  (line+)             >
    <!ELEMENT (title | line) - O  (#PCDATA)           >
    ]>



    <!DOCTYPE p.anth [
    <!ELEMENT p.anth         - -  (page+)               >
    <!ELEMENT page           - -  ((title?, line+)+)    >
    <!ELEMENT (title|line)   - O  (#PCDATA)             >
    ]>



    <(anthology)anthology>
    <(p.anth)p.anth>
    <(p.anth)page>
 
    <!--      other titles and lines on this page here -->
 
         <(anthology)poem><title>The SICK ROSE
         <(anthology)stanza>
              <line>O Rose thou art sick.
              <line>The invisible worm,
    </(p.anth)page>
    <(p.anth)page>
              <line>That flies in the night
              <line>In the howling storm:
         <(anthology)stanza>
              <line>Has found out thy bed
              <line>Of crimson joy:
              <line>And his dark secret love
              <line>Does thy life destroy.
         </(anthology)poem>
 
    <!--      rest of material on this page here    -->
    </(p.anth)page>
 
    </(p.anth)p.anth)
    </(anthology)anthology>

Attributs

Comme en HTML, les balises peuvent avoir des attribus (i.e., pour reprendre l'analogie avec les macros de LaTeX, des paramètres). Dans un fichier SGML, on pourrait ainsi trouver la ligne suivante.
  <poem id=P1 status="draft"> ... </poem>
Dans le DTD, les attributs auraient été déclarés de la manière suivante.
<!ATTLIST poem
          id     ID                            #IMPLIED
          status (draft | revised | published) draft    >
Le type de l'attribut peut être ID (identificateur unique, utile pour les références), CDATA (texte arbitraire, par exemple le nom d'un fichier, d'un lien HTML), IDREF (pointeur sur un autre élément), NMTOKEN (name token, i.e., chaine alphanumérique), NUMBER (nombre) ou, comme dans le deuxième exemple, une liste de chaines de caractères.

Après le type de l'attribut, on indique #REQUIRED (l'attribut doit impérativement être là), #IMPLIED (l'attribut est facultatif), #CURRENT (si l'attribut est absent, on prend sa dernière valeur) ou une valeur par défaut.

Références

Juste un exemple. Dans le DTD :
  <!ELEMENT poemref - O EMPTY                  >
  <!ATTLIST poemref     target IDREF #REQUIRED >
Dans le fichier SGML :
  <POEM ID=Rose>
    Text of poem with identifier 'ROSE'
  </POEM>
 
  <POEM ID=P40>
    Text of poem with identifier 'P40'
  </POEM>
 
  <POEM>
    This poem has no identifier
  </POEM>

  Blake's poem on the sick rose <POEMREF TARGET=Rose>...

Entités

Les entités sont des « raccourcis-clavier » permettant de remplacer le nom d'une « macro » par du texte (des caractères difficiles à taper, du texte récurrent et long à taper, un fichier entier). Voici deux exemples, le premier pour du texte, le second pour un fichier entier.
  <!ENTITY tei "Text Encoding Initiative">
  <!ENTITY ChapTwo SYSTEM "sgmlmkup.txt">
On utilise les entités comme cela.
  The &tei; is...

  Here is the second chapter.
  &ChapTwo;
  This was the second chapter.

Passages marqués et Entités-paramètres

On peut « commenter » certains passages (un peut comme le paquetage xcomment sous LaTeX).
  In such cases, the bank will reimburse the customer for all losses.
  <![ IGNORE [
    Liability is limited to $50,000.
  ]]>

  In such cases, the bank will reimburse the customer for all losses.
  <![ INCLUDE [
    Liability is limited to $50,000.
  ]]>
Dans cet exemple, ce n'est pas très utile, mais dans le DTD on peut définir une « entité-paramètre » dont la valeur sera INCLUDE ou IGNORE. (Ainsi, en ayant deux copies du même DTD qui ne différent que par cette ligne, on peut obtenir deux versions du même document : l'une avec les détails, l'autre sans.)
  <!ENTITY % TEI.prose 'IGNORE'>
Contrairement aux entités, on ne l'utilise pas avec un « et commercial » mais avec le caractère « pour cent ».
  <![ %TEI.prose [
    Liability is limited to $50,000.
  ]]>

Exemple

Voici un DTD que j'ai écris moi-même, avec un exemple de document.
<!DOCTYPE document [
  <!ELEMENT document - - (head,body) 
                         +(example|img|tt|ref|url)>
  <!ELEMENT head     O O (title, author, date)>
  <!ELEMENT title    - O (#PCDATA)>
  <!ELEMENT author   - O (#PCDATA)>
  <!ELEMENT date     - O (#PCDATA)>
  <!ELEMENT body     O O (abstract?,toc?,sect1*)>
  <!ELEMENT abstract - O (p+)>
  <!ELEMENT toc      - O EMPTY>
  <!ELEMENT sect1    - O (#PCDATA, p*, sect2*)>
  <!ELEMENT sect2    - O (#PCDATA, p*)>
  <!ELEMENT p        O O (#PCDATA)>

  <!ELEMENT tt       - - (#PCDATA)>
  <!ELEMENT example  - - (#PCDATA)>
  <!ELEMENT img      - O EMPTY>
  <!ATTLIST img 
            src      CDATA   #REQUIRED>
  <!ELEMENT url      - O EMPTY>
  <!ATTLIST url
            url      CDATA   #REQUIRED>

  <!ATTLIST sect1
            id       ID      #IMPLIED>
  <!ATTLIST sect2
            id       ID      #IMPLIED>
  <!ELEMENT ref      - O  EMPTY>
  <!ATTLIST ref
            id       IDREF   #REQUIRED>
]> 

<!-- Il manque itemize, enumerate et descrip. -->

<DOCUMENT>
<HEAD>
  <TITLE> aaa
  <AUTHOR> aaa
  <DATE> aaa
<BODY>
  <ABSTRACT>
    Bla bla bla
    <P> Bla bla bla
    <P> Bla bla bla
  <TOC>
  <SECT1 ID="sdfsdg"> Bla bla bla
    <P> Bla bla bla
    <IMG SRC="toto">
    <P> Bla bla bla
    <EXAMPLE>
      Bla bla bla
    </>
    Bla bla <TT/bla/.
    <SECT2> Bla bla bla
      <P> Bla bla bla
      <P> Bla bla bla
    <SECT2> Bla bla bla
      <P> Bla bla bla
      <P> Bla bla bla  
  <SECT1> Bla bla bla
    <P> Bla bla bla
    <P> Bla bla bla  
    Voir <REF ID="sdfsdg">.
    Voir aussi <url url="http://...">
  <SECT1> Bla bla bla
    <SECT2> Bla bla bla
    <SECT2> Bla bla bla  
</DOCUMENT>
Pour d'autres exemples, voir ???

Validation

La commande nsgmls permet de vérifier si un document SGML ne contient pas d'erreurs, i.e., s'il est bien conforme au DTD qu'il utilise. Voici par exemple le résultat de cette commande sur l'exemple précédent.
(DOCUMENT
(HEAD
(TITLE
- aaa\n  
)TITLE
(AUTHOR
- aaa\n  
)AUTHOR
(DATE
- aaa
)DATE
)HEAD
(BODY
(ABSTRACT
(P
-Bla bla bla\n    
)P
(P
- Bla bla bla\n    
)P
(P
- Bla bla bla\n  
)P
)ABSTRACT
(TOC
)TOC
AID TOKEN SDFSDG
(SECT1
- Bla bla bla\n    
(P
- Bla bla bla\n    
ASRC CDATA toto
(IMG
)IMG
-\n    
)P
(P
- Bla bla bla\n    
(EXAMPLE
-      Bla bla bla\n    
)EXAMPLE
-\n    Bla bla 
(TT
-bla
)TT
-.\n    
)P
AID IMPLIED
(SECT2
- Bla bla bla\n      
(P
- Bla bla bla\n      
)P
(P
- Bla bla bla\n    
)P
)SECT2
AID IMPLIED
(SECT2
- Bla bla bla\n      
(P
- Bla bla bla\n      
)P
(P
- Bla bla bla  \n  
)P
)SECT2
)SECT1
AID IMPLIED
(SECT1
- Bla bla bla\n    
(P
- Bla bla bla\n    
)P
(P
- Bla bla bla  \n    Voir 
AID TOKEN SDFSDG
(REF
)REF
-.\n    Voir aussi 
AURL CDATA http://...
(URL
)URL
-\n  
)P
)SECT1
AID IMPLIED
(SECT1
- Bla bla bla\n    
AID IMPLIED
(SECT2
- Bla bla bla\n    
)SECT2
AID IMPLIED
(SECT2
- Bla bla bla  
)SECT2
)SECT1
)BODY
)DOCUMENT
C

3. XML

Comme HTML est trop limité (et comme les extensions au HTML sont particulièrement anarchiques), le Web utilisera bient SGML. Mais comme SGML est assez compliqué, ce sera plutôt une version « simplifiée » de SGML, appelée XML. Voici un échantillon des différences (il y en a plein d'autres, mais elles portent sur des points que je 'ai pas abordés --- i.e., des poinrs que je ne comprends pas). Les fichiers ne commencent plus par une ligne du genre
  <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
mais par une ligne du genre
  <?xml version="1.0" standalone="yes"?>
On n'a plus le droit d'omettre des balises ouvrantes ou fermantes (par exemple, <P>). Pour le cas des balises vides, on peut omettre la balise fermante si on rajoute un / à la fin.
  <IMG SRC="mypic.gif" alt="Picture"/>
Pour plus de détails, allez ici.

4. DSSSL, CSS, XSL

DSSSL est un standard permettant de convertir la structure d'un fichier SGML en instructions typographiques. C'est du scheme, je n'aime pas du tout et je n'y comprends pas grand-chose.

CSS : C'est pour le HTML , non ?

XSL : ?

5. Divers

Est-il possible d'utiliser Perl pour convertir un fichier SGML en ce que l'on veut ? Y a-t-il des modules pour ce genre de tâche ?
Voir http://www.perl.com/CPAN/modules/by-module/XML/perl-xml-modules.html

Perl et SGML :
- Pour construire un « arbre » décrivant l'arborescence d'un document SGML
    SGML::SPGroveBuilder
    Pod::GroveBuilder
- Pour manipuler cet arbre :
    SGML::Grove
- Pour lire la sortie de nsgmls :
    SMGLS.pm
- Pour faire tout à la fois :
    perlSGML (ça a l'air vieux)

Perl et XML :
- Pour créer des fichiers XML :
    XML::Writer
    XML::Generator
- Pour manipuler, convertir des fichiers XML :
    XML::Parser : interface pour le parseur XML de J. Clark, expat  
    XML::DT : utilise XML::Parser pour faire des conversions simples à
              partir d'un fichier XML (par exemple, conversion vers
              LaTeX).
    perlXML : tout une ribambelle de modules
- Recherches dans un fichier XML :
    XML::QL : XML Query Language
    XML::XQL
    XML::miniXQL
- ... :
    XML::Grove : ???
    XML::DOM : ???
- Divers :
    XML::Dumper : afficher des variables Perl en XML
    XML::Encoding : pour les documents utilisant un codage différent (SJIS, EUC, Big5, latin2, etc.)


zoonek@Math.Jussieu.Fr
Last modified: Thu Aug 12 10:45:27 MET DST 1999