diff --git a/ecrire/inc/utils.php b/ecrire/inc/utils.php
index f14da669deb93281f6aae1bb2e6c6a812deccc4d..8b3a6ad0305233630bee4ed228e85d53e4b0688f 100644
--- a/ecrire/inc/utils.php
+++ b/ecrire/inc/utils.php
@@ -935,6 +935,7 @@ function spip_initialisation($pi=NULL, $pa=NULL, $ti=NULL, $ta=NULL) {
 	define('_DIR_SESSIONS', $ti . "sessions/");
 	define('_DIR_TRANSFERT', $ti . "upload/");
 	define('_DIR_CACHE', $ti . "CACHE/");
+	define('_DIR_DTD', $ti . "CACHE/dtd");
 	define('_DIR_SKELS', $ti . "CACHE/skel/");
 	define('_DIR_TMP', $ti);
 
@@ -990,8 +991,8 @@ function spip_initialisation($pi=NULL, $pa=NULL, $ti=NULL, $ta=NULL) {
 
 	define('_DOCTYPE_ECRIRE', 
 		// "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>\n");
-		"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
-		// "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd'>\n");
+		// "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
+	            "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
 
 	define('_DOCTYPE_AIDE', 
 	       "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
diff --git a/ecrire/inc/valider_xml.php b/ecrire/inc/valider_xml.php
index a2774a7b3cb4baa065ec1e3e522698f317aead02..6bcb1645b770d0d8bb5c27bde78be7e40374422b 100644
--- a/ecrire/inc/valider_xml.php
+++ b/ecrire/inc/valider_xml.php
@@ -88,33 +88,38 @@ function analyser_dtd($grammaire, $avail, &$dtc)
 
 	$dtd = preg_replace('/<!--.*?-->/s','',$dtd);
 
-	if (preg_match_all('/<!ENTITY\s+(%?)\s*([.\w]+)\s+(PUBLIC|SYSTEM)?\s*"([^"]*)"\s*("([^"]*)")?\s*>/', $dtd, $r, PREG_SET_ORDER)) {
+	if (preg_match_all('/<!ENTITY\s+(%?)\s*([\w;.-]+)\s+(PUBLIC|SYSTEM)?\s*"([^"]*)"\s*("([^"]*)")?\s*>/', $dtd, $r, PREG_SET_ORDER)) {
 		foreach($r as $m) {
 		  list($t, $term, $nom, $type, $val, $q, $alt) = $m;
 		  if ($type AND $alt) {
 		    // valeur par defaut de $alt obscure. A etudier.
-			$dir = preg_replace(',/[^/]+$,', '/', $grammaire)
+		    if (strpos($alt, '/') === false)
+			$alt = preg_replace(',/[^/]+$,', '/', $grammaire)
 			. ($alt ? $alt : "loose.dtd")  ;
 		    // en cas d'inclusion, l'espace de nom est le meme
-		    analyser_dtd($dir, $type, $dtc);
+		    analyser_dtd($alt, $type, $dtc);
 		  }
 		  elseif (!$term) {
 		    $dtc->entites[$nom] = $val;
 		  }
-		  else 
+		  else {
 		    $dtc->macros[$nom] = expanserEntite($val, $dtc->macros) ;
+		  }
 		}
-	} 
+	}
 
 	// reperer pour chaque noeud ses fils potentiels.
 	// mais tant pis pour leur eventuel ordre de succession (, * +):
 	// les cas sont rares et si aberrants que interet/temps-de-calcul -> 0
-	if (preg_match_all('/<!ELEMENT\s+(\w+)([^>]*)>/', $dtd, $r, PREG_SET_ORDER)) {
+	if (preg_match_all('/<!ELEMENT\s+(\S+)\s+([^>]*)>/', $dtd, $r, PREG_SET_ORDER)) {
 	  foreach($r as $m) {
 	    list(,$nom, $val) = $m;
+	    $nom = expanserEntite($nom, $dtc->macros);
 	    $val = expanserEntite($val, $dtc->macros);
-	    $val = array_values(preg_split('/\W+/', $val,-1,PREG_SPLIT_NO_EMPTY));
+	    $val = array_values(preg_split('/\W+/', $val,-1,
+					   PREG_SPLIT_NO_EMPTY));
 	    $dtc->elements[$nom]= $val;
+
 	    foreach ($val as $k) {
 		if (!isset($dtc->peres[$k])
 		OR !in_array($nom, $dtc->peres[$k]))
@@ -131,23 +136,24 @@ function analyser_dtd($grammaire, $avail, &$dtc)
 	if (preg_match_all('/<!ATTLIST\s+(\S+)\s+([^>]*)>/', $dtd, $r, PREG_SET_ORDER)) {
 	  foreach($r as $m) {
 	    list(,$nom, $val) = $m;
+	    $nom = expanserEntite($nom, $dtc->macros);
 	    $val = expanserEntite($val, $dtc->macros);
 	    $att = array();
+
 	    if (preg_match_all("/\s*(\S+)\s+(([(][^)]*[)])|(\S+))\s+(\S+)(\s*'[^']*')?/", $val, $r2, PREG_SET_ORDER)) {
 		foreach($r2 as $m2) {
 			$v = preg_match('/^\w+$/', $m2[2]) ? $m2[2]
 			  : ('/^' . preg_replace('/\s+/', '', $m2[2]) . '$/');
+			$m21 = expanserEntite($m2[1], $dtc->macros);
+			$m25 = expanserEntite($m2[5], $dtc->macros);
 			$trace[$v] = 1;
-			$att[$m2[1]] = array($v, $m2[5]);
+			$att[$m21] = array($v, $m25);
 		}
 	    }
 	    $dtc->attributs[$nom] = $att;
 	  }
 	}
 
-	// pour voir la liste des regep d'attributs:
-#	echo join('<br />', array_keys($trace));exit;
-
 	spip_log("DTD $avail $grammaire ". strlen($dtd) . ' octets ' . count($dtc->macros)  . ' macros, ' . count($dtc->elements)  . ' elements, ' . count($trace) . " types différents d'attributs " . count($dtc->entites) . " entites");
 }
 
@@ -156,8 +162,9 @@ function expanserEntite($val, $macros)
 {
 	if (preg_match_all('/%([.\w]+);/', $val, $r, PREG_SET_ORDER)) {
 		foreach($r as $m)
-			if ($x = $macros[$m[1]])
-				$val = str_replace($m[0], $x, $val);
+		  // il peut valoir ""
+			if (isset($macros[$m[1]]))
+				$val = str_replace($m[0], $macros[$m[1]], $val);
 	}
 	return $val;
 }