diff --git a/.gitattributes b/.gitattributes
index a4950dddf2b407347db8b506e644e081a36b0d1f..7c9dfb5d37adce8b2628f93276fdd812c7597a72 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -9,6 +9,10 @@ dist/inc-head.html -text
 dist/inc-petition.html -text
 dist/inc-pied.html -text
 dist/inc-rubriques.html -text
+dist/modeles/doc.html -text
+dist/modeles/emb.html -text
+dist/modeles/fromage.html -text
+dist/modeles/img.html -text
 dist/modeles/lesauteurs.html -text
 dist/modeles/pagination.html -text
 dist/modeles/pagination_page.html -text
diff --git a/dist/modeles/doc.html b/dist/modeles/doc.html
new file mode 100644
index 0000000000000000000000000000000000000000..77932613032a96647c8d589462cc6ee8d2ae1398
--- /dev/null
+++ b/dist/modeles/doc.html
@@ -0,0 +1,7 @@
+<BOUCLE_doc (DOCUMENTS) {id_document=#ENV{id_doc}}>
+<div class='spip_document_#ID_DOCUMENT spip_documents[ spip_documents_(#ENV{align})][ (#ENV{class})]' 
+ style='[(#ENV{align}=={left}|?{float:left; })][(#ENV{align}=={right}|?{float:right; })]width: [(#LOGO_DOCUMENT||largeur)]px;'>
+<a href='#EVAL{_DIR_RACINE}#FICHIER' type='#MIME_TYPE'><img src='[(#LOGO_DOCUMENT||extraire_attribut{src})]' width='[(#LOGO_DOCUMENT||largeur)]' heigth='[(#LOGO_DOCUMENT||hauteur)]' alt='#TYPE_DOCUMENT - [(#TAILLE|taille_en_octets)]' title='#TYPE_DOCUMENT - [(#TAILLE|taille_en_octets)]' /></a>[
+<div class='spip_doc_titre'><strong>(#TITRE)</strong></div>][
+<div class='spip_doc_descriptif'>(#DESCRIPTIF)</div>]</div>
+</BOUCLE_doc>
\ No newline at end of file
diff --git a/dist/modeles/emb.html b/dist/modeles/emb.html
new file mode 100644
index 0000000000000000000000000000000000000000..feae0363daeb79d0a66e8b201c2e7c7dd5c5e68c
--- /dev/null
+++ b/dist/modeles/emb.html
@@ -0,0 +1,85 @@
+<BOUCLE_tous (DOCUMENTS spip_types_documents) {id_document=#ENV{id_emb}}>
+
+[(#REM) on trouvera plusieurs variable de hauteur/largeur
+- les balises #HAUTEUR et #LARGEUR
+- #ENV{hauteur} et {largeur} correspondant <emb|hauteur=xx...>
+- #GET{hauteur} et #GET{largeur} correspondent prioritairemnt a #ENV,
+  puis #LARGEUR/HAUTEUR sauf si il y a un controleur
+]
+#SET{hauteur,#ENV{hauteur}|sinon{#HAUTEUR}}
+#SET{largeur,#ENV{largeur}|sinon{#LARGEUR}}
+[(#ENV{controls}=={PlayButton}|?{#SET{hauteur,25},''})]
+[(#ENV{controls}=={PlayButton}|?{#SET{largeur,40},''})]
+[(#ENV{controls}=={PositionSlider}|?{#SET{hauteur,25},''})]
+[(#ENV{controls}=={PositionSlider}|?{#SET{largeur,#ENV{largeur}|moins{40}},''})]
+
+[(#INCLUS|=={embed}|?{
+<div class='spip_document_#ID_DOCUMENT spip_documents[ spip_document_(#ENV{align})]' style='[(#ENV{align}=={left}|?{float:left; })][(#ENV{align}=={right}|?{float:right; })]width:[(#LARGEUR|<{120}|?{120,#LARGEUR})[(#REM)<!--ligne 196 de inc/documents-->]]px'>
+})]
+
+<BOUCLE_svg (DOCUMENTS) {id_document} {doublons} {extension==svg}>
+
+<embed src='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' [(#ENV*|params_spip_to_attributs_html)] width='#LARGEUR' height='#HAUTEUR' />
+
+</BOUCLE_svg>
+
+<BOUCLE_real (DOCUMENTS) {id_document}{doublons} {extension==rm|ram|ra}>
+<div><object width='0' height='0'>
+<param name='movie' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+<param name='src' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+<param name='controls' value='ImageWindow' /><param name='type' value='audio/x-pn-realaudio-plugin' /><param name='console' value='Console#ID_DOCUMENT' /><param name='nojava' value='true' /><inclus src='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER'  controls='ImageWindow' type='audio/x-pn-realaudio-plugin' console='Console#ID_DOCUMENT' nojava='true' width='0' height='0'></inclus>
+</object>
+</div><object width='40' height='25'>
+<param name='movie' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+<param name='src' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+<param name='controls' value='PlayButton' /><param name='type' value='audio/x-pn-realaudio-plugin' /><param name='console' value='Console#ID_DOCUMENT' /><param name='nojava' value='true' /><inclus src='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER'  controls='PlayButton' type='audio/x-pn-realaudio-plugin' console='Console#ID_DOCUMENT' nojava='true' width='40' height='25'></inclus>
+</object>
+<object width='-40' height='25'>
+<param name='movie' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+
+<param name='src' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+<param name='controls' value='PositionSlider' /><param name='type' value='audio/x-pn-realaudio-plugin' /><param name='console' value='Console#ID_DOCUMENT' /><param name='nojava' value='true' /><inclus src='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER'  controls='PositionSlider' type='audio/x-pn-realaudio-plugin' console='Console#ID_DOCUMENT' nojava='true' width='-40' height='25'></inclus>
+</object>
+
+</BOUCLE_real>
+
+<BOUCLE_flash(DOCUMENTS) {id_document}{doublons} {extension=swf}>
+<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" 
+ codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" 
+ width='#GET{largeur}' height='#GET{hauteur}'>
+	<param name='movie' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+	[(#ENV{quality,''}|?{'',' '})<param name="quality" value="high" />]
+	[(#ENV*|params_spip_to_params_html)]
+
+	<!--[if !IE]> <-->
+	<object data="[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER" width='#GET{largeur}' height='#GET{hauteur}' type="application/x-shockwave-flash">
+		[(#ENV{quality,''}|?{'',' '})<param name="quality" value="high" />]
+		[(#ENV*|params_spip_to_params_html)]
+		<param name="pluginurl" value="http://www.macromedia.com/go/getflashplayer" />
+   	
+	</object>
+	<!--> <![endif]-->
+</object>
+</BOUCLE_flash>
+
+<BOUCLE_inclus (DOCUMENTS) {id_document} {doublons} {inclus=embed}>
+
+<object width='#GET{largeur}' height='#GET{hauteur}'>
+<param name='movie' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+<param name='src' value='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' />
+[(#ENV*|params_spip_to_params_html)]
+<embed src='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' [(#ENV*|params_spip_to_attributs_html)] width='#GET{largeur}' height='#GET{hauteur}'></embed></object>
+
+</BOUCLE_inclus>
+
+<BOUCLE_image (DOCUMENTS) {id_document}{inclus=image} {doublons}>
+[(#INCLUDE{fond=modeles/img}{id_document}{align=#ENV{align}})]
+</BOUCLE_image>
+[(#INCLUS|=={embed}|?{
+	[<div class='spip_doc_titre'><strong>(#TITRE)</strong></div>][<div class='spip_doc_descriptif'>(#DESCRIPTIF)</div>]</div>
+})]
+
+
+</BOUCLE_tous>
+
+
diff --git a/dist/modeles/fromage.html b/dist/modeles/fromage.html
new file mode 100644
index 0000000000000000000000000000000000000000..76d1f40901f8984dc6931cf967b3fc8403f759e0
--- /dev/null
+++ b/dist/modeles/fromage.html
@@ -0,0 +1,57 @@
+[(#REM)
+
+	Voici un modele de fromage inspire de
+	http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Mod%C3%A8les/Infobox#Fromage
+
+	Le modele wikipedia est :
+	http://fr.wikipedia.org/wiki/Mod%C3%A8le:Fromage
+
+	On reprend ici le meme code, on ajoute un peu de magie SPIP autour,
+	et on entre dans n'importe quel champ d'un article :
+
+	<fromage1020|lait=brebis|pays=France|region=Poitou|date_appellation=1923>
+
+	ou 1020 est le numero du document qui contient la photo, le nom du
+	fromage (titre), et le commentaire du critique culinaire (descriptif).
+]
+
+<BOUCLE_doc(DOCUMENTS){id_document=#ENV{id_fromage}}>
+[(#REM) Code pour {doublons}]
+<table class='spip_document_#ID_DOCUMENT' border="1" align="right" cellpadding="4" cellspacing="0" style="margin: 0 0 1em 1em; border: 1px solid #999; border-right-width: 2px; border-bottom-width: 2px; background-color: #FFFFDD; font-size: 90%; border-collapse: collapse;">
+<tr>
+<th colspan="2" style="background-color: #F6E396">
+<table style="background: #F6E396" align="center" width="100%">
+<tr>
+<td></td>
+<td style="background: #F6E396" align="center" width="100%"><font color="peru" size="4"><b>#TITRE</b></font></td>
+<td></td>
+</tr>
+</table>
+</th>
+</tr>
+[<tr>
+<td align="center" colspan="2" style="background: #FFFFFF">(#LOGO_DOCUMENT)</td>
+</tr>
+]
+[<tr>
+<td><b>Pays d'origine</b></td>
+<td align="center" style="background: #FFFFFF">(#ENV{pays})</td>
+</tr>]
+[<tr>
+<td><b>R&eacute;gion, ville</b></td>
+<td align="center" style="background: #FFFFFF">(#ENV{region})</td>
+</tr>]
+[<tr>
+<td><b>Lait de</b></td>
+<td align="center" style="background: #FFFFFF">(#ENV{lait})</td>
+</tr>]
+[<tr>
+<td><b>Appellation, depuis</b></td>
+<td align="center" style="background: #FFFFFF">(#ENV{date_appellation})</td>
+</tr>]
+[<tr>
+<td><b>Commentaire</b></td>
+<td align="center" style="background: #FFFFFF">(#DESCRIPTIF)</td>
+</tr>]
+</table>
+</BOUCLE_doc>
diff --git a/dist/modeles/img.html b/dist/modeles/img.html
new file mode 100644
index 0000000000000000000000000000000000000000..6a60ab9630d60a7c5d38a57de22b158771ec2fee
--- /dev/null
+++ b/dist/modeles/img.html
@@ -0,0 +1,14 @@
+<BOUCLE_document (DOCUMENTS) {id_document=#ENV{id_img}} {mode=document}>
+[(#CONFIG{creer_preview}=={oui}|?{
+#SET{fichier,#FICHIER|reduire_image{#CONFIG{taille_preview}}||extraire_attribut{src}},#SET{fichier,#FICHIER}})]
+<span class='spip_document_#ID_DOCUMENT spip_documents[ spip_documents_(#ENV{align})][ (#ENV{class})]' 
+ style='[(#ENV{align}=={left}|?{float:left; })][(#ENV{align}=={right}|?{float:right; })]width: [(#GET|{fichier}|largeur)];'>
+ [(#CONFIG{creer_preview}=={oui}|?{<a href='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#FICHIER' 	type='#MIME_TYPE'>,''})]<img src='#GET{fichier}' width='[(#GET{fichier}|largeur)]' height='[(#GET{fichier}|hauteur)]' alt='#TITRE (#TYPE_DOCUMENT)'  title='#TITRE' />[(#CONFIG{creer_preview}|=={oui}|?{</a>,''})]</span>
+</BOUCLE_document>
+
+<BOUCLE_vignette (DOCUMENTS) {id_document=#ENV{id_img}} {mode=vignette}>
+#SET{fichier,#FICHIER}
+<span class='spip_document_#ID_DOCUMENT spip_documents[ spip_documents_(#ENV{align})][ (#ENV{class}]' style='[(#ENV{align}=={left}|?{float:left; })][(#ENV{align}=={right}|?{float:right; })]width: [(#GET|{fichier}|largeur)];'><img src='[(#DISTANT|=={non}|?{#EVAL{_DIR_RACINE}})]#GET{fichier}' width='[(#GET{fichier}|largeur)]' height='[(#GET{fichier}|hauteur)]' alt='#TITRE (#TYPE_DOCUMENT)'  title='#TITRE' /></span>
+</BOUCLE_vignette>
+
+<//B_document>
\ No newline at end of file
diff --git a/ecrire/exec/articles_edit.php b/ecrire/exec/articles_edit.php
index f2a0acfb86651cc38fc3916e95de4f1503cb82f2..0f244689137f6f1dade9db7de682082f4ea387f5 100644
--- a/ecrire/exec/articles_edit.php
+++ b/ecrire/exec/articles_edit.php
@@ -314,10 +314,12 @@ function exec_articles_edit_dist()
 
 	if (!$new){
 
-	# affichage sur le cote des pieces jointes, en reperant les inserees
-		if (isset($row['descriptif'])) document_a_voir($row['descriptif']);
-		if (isset($row['chapo'])) document_a_voir($row['chapo']);
-		if (isset($row['texte'])) document_a_voir($row['texte']);
+		# affichage sur le cote des pieces jointes, en reperant les inserees
+		# note : typo() repere les doublons aussi eficacement que propre(),
+		# mais beaucoup plus rapidement
+		$GLOBALS['doublons']['documents'] = '0';
+		traiter_doublons_documents($GLOBALS['doublons'],
+			echappe_retour(pipeline('modeles', join('',$row))));
 		afficher_documents_colonne($id_article, 'article', true);
 	}
 	$GLOBALS['id_article_bloque'] = $id_article;	// globale dans debut_droite
diff --git a/ecrire/exec/breves_edit.php b/ecrire/exec/breves_edit.php
index f3c203da60aea21986906181682daf25099c6189..56a257fa0afebcd69d4aa6a31b7fcb5ea510cf1b 100644
--- a/ecrire/exec/breves_edit.php
+++ b/ecrire/exec/breves_edit.php
@@ -71,8 +71,10 @@ afficher_hierarchie($id_rubrique);
 fin_grand_cadre();
 debut_gauche();
 if ($new != 'oui' AND ($connect_statut=="0minirezo" OR $statut=="prop")) {
-
-	if (isset($texte)) document_a_voir($texte);
+	$GLOBALS['doublons']['documents'] = '0';
+	$GLOBALS['doublons']['documents'] = '0';
+	traiter_doublons_documents($GLOBALS['doublons'],
+		echappe_retour(pipeline('modeles', "$titre$texte")));
 	afficher_documents_colonne($id_breve, "breve", true);
 }
 echo pipeline('affiche_gauche',array('args'=>array('exec'=>'breves_edit','id_breve'=>$id_breve),'data'=>''));
diff --git a/ecrire/inc/documents.php b/ecrire/inc/documents.php
index faba39e3f2d61c47b46ccedb2682a8d2ffa71352..101492464965e26dd3d18150007520cc23c57060 100644
--- a/ecrire/inc/documents.php
+++ b/ecrire/inc/documents.php
@@ -51,27 +51,6 @@ function vignette_par_defaut($ext, $size=true, $loop = true) {
 }
 
 
-// Quels documents a-t-on deja vu ? (gestion des doublons dans l'espace prive)
-// http://doc.spip.org/@document_vu
-function document_vu($id_document=0) {
-	static $vu = array();
-
-	if (!_DIR_RESTREINT) {
-		if (!$id_document) return join(',', array_keys($vu));
-		if (!isset($vu[$id_document])) $vu[$id_document] = true;
-	}
-}
-
-# Affecter les document_vu() utilises dans afficher_case_document 
-# utile pour un affichage differencie des image "libres" et des images
-# integrees via <imgXX|left> dans le texte
-
-// http://doc.spip.org/@document_a_voir
-function document_a_voir($texte) {
-	preg_match_all(__preg_img, $texte, $matches, PREG_SET_ORDER);
-	foreach ($matches as $match) document_vu($match[2]);
-}
-
 //
 // Affiche le document avec sa vignette par defaut
 //
@@ -129,362 +108,6 @@ function document_et_vignette($document, $url, $portfolio=false) {
 		return "<a href='$url'\n\ttype='$mime'>$image</a>";
 }
 
-//
-// Integration (embed) multimedia
-//
-
-// http://doc.spip.org/@embed_document
-function embed_document($id_document, $les_parametres="", $afficher_titre=true) {
-	document_vu($id_document);
-	charger_generer_url();
-
-	if ($les_parametres) {
-		$parametres = explode("|",$les_parametres);
-
-		for ($i = 0; $i < count($parametres); $i++) {
-			$parametre = $parametres[$i];
-			
-			if (eregi("^(left|right|center)$", $parametre)) {
-				$align = strtolower($parametre);
-			}
-			else {
-				$params[] = $parametre;
-			}
-		}
-	}
-
-	$id_document = intval($id_document);
-
-	if (!$row = spip_abstract_fetsel('*', 'spip_documents',
-					 "id_document=$id_document"))
-		return '';
-
-	$id_type = $row['id_type'];
-	$titre = propre($row ['titre']);
-	$descriptif = propre($row['descriptif']);
-	$fichier = generer_url_document($id_document);
-	$largeur = $row['largeur'];
-	$hauteur = $row['hauteur'];
-	$taille = $row['taille'];
-	$mode = $row['mode'];
-	$largeur_vignette = $largeur;
-
-	if ($row_type = @spip_abstract_fetsel('*', 'spip_types_documents',
-					      "id_type=" . intval($id_type)))
-	  {
-		$type = $row_type['titre'];
-		$inclus = $row_type['inclus'];
-		$extension = $row_type['extension'];
-	}
-	else $type = 'fichier';
-
-	if ($inclus == "embed") 
-		$vignette = parametrer_embed_document($fichier, $id_document, $hauteur, $largeur, $extension, $les_parametres, $params);
-	else if ($inclus == "image") {
-		$fichier_vignette = $fichier;
-		$largeur_vignette = $largeur;
-		$hauteur_vignette = $hauteur;
-		if ($fichier_vignette) {
-			$vignette = "<img src='$fichier_vignette'";
-			if ($largeur_vignette && $hauteur_vignette)
-				$vignette .= " width='$largeur_vignette' height='$hauteur_vignette'";
-			if ($titre) {
-				$titre_ko = ($taille > 0) ? ($titre . " - ". taille_en_octets($taille)) : $titre;
-				$titre_ko = supprimer_tags(propre($titre_ko));
-				$vignette .= " alt=\"$titre_ko\" title=\"$titre_ko\"";
-			}else{  $vignette .= " alt=\"\" title=\"\""; }
-			$vignette .= " />";
-		}
-	}
-		
-	if (!$afficher_titre) return $vignette;
-
-	if ($largeur_vignette < 120) $largeur_vignette = 120;
-	$forcer_largeur = " width: ".$largeur_vignette."px;";
-
-	if ($align) {
-		$class_align = " spip_documents_".$align;
-		if ($align <> 'center')
-			$float = " style='float: $align;$forcer_largeur'";
-	}
-
-	$retour .= "<div class='spip_document_$id_document spip_documents$class_align'$float>\n";
-	$retour .= $vignette;
-	
-	if ($titre) $retour .= "<div class='spip_doc_titre'><strong>$titre</strong></div>";
-	
-	if ($descriptif) {
-	  $retour .= "<div class='spip_doc_descriptif'>$descriptif</div>"; 
-	}
-
-	$retour .= "</div>\n";
-	
-	return $retour;
-}
-
-// http://doc.spip.org/@parametrer_embed_document
-function parametrer_embed_document($fichier, $id_document, $hauteur, $largeur, $extension, $les_parametres, $params)
-{
-	if ((!ereg("^controls", $les_parametres)) AND (ereg("^(rm|ra|ram)$", $extension)))
-	// Pour RealVideo (??? -- c'est toujours irreel la video. [esj])
-	 {
-		$param = "|type=audio/x-pn-realaudio-plugin|console=Console$id_document|nojava=true|$les_parametres";
-
-		return "\n<div>" .
-		  embed_document($id_document, "controls=ImageWindow$param", false) . 
-		  "</div>" .
-		  embed_document($id_document, "controls=PlayButton$param", false) .
-		  embed_document($id_document, "controls=PositionSlider$param", false);
-	 } else {
-		$inserer_vignette = '';
-
-		for ($i = 0; $i < count($params); $i++) {
-			if (ereg("([^\=]*)\=([^\=]*)", $params[$i], $vals)){
-				$nom = $vals[1];
-				$valeur = $vals[2];
-				$inserer_vignette .= "<param name='$nom' value='$valeur' />";
-				$param_emb .= " $nom='$valeur'";
-				if ($nom == "controls" AND $valeur == "PlayButton") { 
-					$largeur = 40;
-					$hauteur = 25;
-				} else if ($nom == "controls" AND $valeur == "PositionSlider") { 
-					$largeur = $largeur - 40;
-					$hauteur = 25;
-				}
-			}
-		}
-
-		$params = "<param name='movie' value='$fichier' />\n"
-		  . "<param name='src' value='$fichier' />\n"
-		  . $inserer_vignette;
-
-		// Pour Flash
-		if ((!ereg("^controls", $les_parametres)) AND ($extension=='swf'))
-
-			return "<object "
-			  . "type='application/x-shockwave-flash' data='$fichier' "
-			  . "width='$largeur' height='$hauteur'>\n"
-			  . $params
-			  . "</object>\n";
-		else {
-			$emb = "<embed src='$fichier' $param_emb width='$largeur' height='$hauteur'>$alt</embed>\n";
-
-			// Cas particulier du SVG : pas d'object
-			if ($extension == 'svg')
-				return $emb;
-
-			/* 
-			// essai pour compatibilite descendante (helas ca ne marche pas)
-			// cf. http://www.yoyodesign.org/doc/w3c/svg1/backward.html
-			if ($extension == 'svg') return 
-			"<object type='image/svg+xml' data='$fichier'
-			$param_emb width='$largeur' height='$hauteur'>$alt</object>\n";
-			*/
-
-			return "<object width='$largeur' height='$hauteur'>\n"
-			  . $params
-			  . $emb
-			  . "</object>\n";
-		}
-	}
-}
-
-//
-// Integration des images et documents
-//
-
-// http://doc.spip.org/@integre_image
-function integre_image($id_document, $align, $type_aff) {
-	document_vu($id_document);
-	charger_generer_url();
-	$id_document = intval($id_document);
-
-	$row = spip_abstract_fetsel('*', 'spip_documents', "id_document=$id_document");
-	if (!$row) return '';
-
-	$id_type = $row['id_type'];
-	$titre = !strlen($row['titre']) ? '' : typo($row['titre']);
-	$descriptif = !strlen($row['descriptif']) ?'' : propre($row['descriptif']);
-	$fichier = $row['fichier'];
-	$url_fichier = generer_url_document($id_document);
-	$largeur = $row['largeur'];
-	$hauteur = $row['hauteur'];
-	$taille = $row['taille'];
-	$mode = $row['mode'];
-	$id_vignette = $row['id_vignette'];
-
-	// on construira le lien en fonction du type de doc
-	if ($t = @spip_abstract_fetsel("titre,extension", 'spip_types_documents', "id_type = $id_type")) {
-			$extension = $t['extension']; # jpg, tex
-			$type = $t['titre']; # JPEG, LaTeX
-	}
-
-	// Attention ne pas confondre :
-	// pour un document affiche avec le raccourci <IMG> on a
-	// $mode == 'document' et $type_aff == 'IMG'
-	// inversement, pour une image presentee en mode 'DOC',
-	// $mode == 'vignette' et $type_aff == 'DOC'
-
-	// Type : vignette ou document ?
-	if ($mode == 'document') {
-		$vignette = document_et_vignette($row, $url_fichier);
-	} else {
-		$vignette = image_pattern($row);
-	}
-
-	//
-	// Regler le alt et title
-	//
-	$alt_titre_doc = entites_html(texte_backend(supprimer_tags($titre)));
-	$alt_infos_doc = entites_html($type
-		. (($taille>0) ? ' - '.texte_backend(taille_en_octets($taille)) : ''));
-	if ($row['distant'] == 'oui')
-		$alt_infos_doc .= ", ".$url_fichier;
-	if ($alt_titre_doc) $alt_sep = ', ';
-
-	$alt = "";
-	$title = "";
-	// documents presentes en mode <DOC> : alt et title "JPEG, 54 ko"
-	// mais pas de titre puisqu'il est en dessous
-	if ($mode == 'document' AND $type_aff == 'DOC') {
-		$alt = $alt_infos_doc;
-		$title = $alt_infos_doc;
-	}
-	// document en mode <IMG> : alt + title detailles
-	else if ($mode == 'document' AND $type_aff == 'IMG') {
-		$alt = "$alt_titre_doc$alt_sep$alt_infos_doc";
-		$title = "$alt_titre_doc$alt_sep$alt_infos_doc";
-	}
-	// vignette en mode <DOC> : alt disant "JPEG", pas de title
-	else if ($mode == 'vignette' AND $type_aff == 'DOC') {
-		$alt = "($type)";
-	}
-	// vignette en mode <IMG> : alt + title s'il y a un titre
-	else if ($mode == 'vignette' AND $type_aff == 'IMG') {
-		if (strlen($titre)){
-			$alt = "$alt_titre_doc ($type)";
-			$title = "$alt_titre_doc";
-		}
-		else
-			$alt = "($type)";
-	}
-
-	$vignette = inserer_attribut($vignette, 'alt', $alt);
-	if (strlen($title))
-		$vignette = inserer_attribut($vignette, 'title', $title);
-
-	// Preparer le texte sous l'image pour les <DOC>
-	if ($type_aff == 'DOC') {
-		if (strlen($titre))
-			$txt = "<div class='spip_doc_titre'><strong>"
-				. $titre
-				. "</strong></div>\n";
-		if (strlen($descriptif))
-			$txt .= "<div class='spip_doc_descriptif'>$descriptif</div>\n";
-	}
-
-	// Passer un DIV pour les images centrees et, dans tous les cas, les <DOC>
-	if (preg_match(',^(left|center|right)$,i', $align))
-		$align = strtolower($align);
-	else
-		$align = '';
-	if ($align == 'center' OR $type_aff =='DOC') {
-		$span = "div";
-	} else {
-		$span = "span";
-	}
-
-	if ($align) {
-		$class_align = " spip_documents_".$align;
-		if ($align <> 'center')
-			$float = "float: $align; ";
-	}
-
-	# extraire la largeur de la vignette
-	$width = extraire_attribut($vignette, 'width');
-
-	# mode <span ...> : ne pas mettre d'attributs de type block sinon MSIE Windows refuse de faire des liens dessus
-	if ($span == 'span') {
-		$width = 'width: '.$width.'px;';
-		$vignette = "<span class='spip_document_$id_document spip_documents$class_align' style='${float}${width}'>$vignette</span>";
-		return $vignette;
-	}
-	# mode <div ...>
-	else {
-		if ($align != 'center') {
-			// Largeur de la div = celle de l'image ; mais s'il y a une legende
-			// mettre au moins 120px
-			if (strlen($txt) AND $width < 120) $width = 120;
-			$width = 'width: '.$width.'px;';
-			if (strlen($style = "$float$width"))
-				$style = " style='$style'";
-		}
-		return
-			"<div class='spip_document_$id_document spip_documents$class_align'$style>"
-			. $vignette
-			. $txt
-			. '</div>';
-	}
-}
-
-
-//
-// Traitement des images et documents <IMGxx|right> pour inc_texte
-//
-// http://doc.spip.org/@inserer_documents
-function inserer_documents($letexte) {
-	# HACK: empecher les boucles infernales lorsqu'un document est mentionne
-	# dans son propre descriptif (on peut citer un document dans un autre,
-	# mais il faut pas trop pousser...)
-	static $pile = 0;
-	if (++$pile > 5) return '';
-
-	preg_match_all(__preg_img, $letexte, $matches, PREG_SET_ORDER);
-	foreach ($matches as $match) {
-		$type = strtoupper($match[1]);
-		if ($type == 'EMB')
-			$rempl = embed_document($match[2], $match[4]);
-		else
-			$rempl = integre_image($match[2], $match[4], $type);
-
-		// Temporaire : un pis-aller pour eviter que des paragraphes dans
-		// le descriptif d'un document ne soient doublonnes en <br /> :
-		// le probleme est que propre() est passe deux fois...
-		$rempl = preg_replace(",\n+,", " ", $rempl);
-
-		// XHTML : remplacer par une <div onclick> le lien
-		// dans le cas [<docXX>->lien] ; sachant qu'il n'existe
-		// pas de bonne solution en XHTML pour produire un lien
-		// sur une div (!!)...
-		if (substr($rempl, 0, 5) == '<div '
-		AND preg_match(',(<a [^>]+>)'.preg_quote($match[0]).'</a>,Uims',
-		$letexte, $r)) {
-			$lien = extraire_attribut($r[1], 'href');
-			$re = '<div style="cursor:pointer;cursor:hand;" '
-				.'onclick="document.location=\''.$lien
-				.'\'"'
-##				.' href="'.$lien.'"' # href deviendra legal en XHTML2
-				.'>'
-				.$rempl
-				.'</div>';
-			$letexte = str_replace($r[0], $re, $letexte);
-		}
-
-		// cas normal
-		// Installer le document ; les <div> sont suivies de deux \n de maniere
-		// a provoquer un paragraphe a la suite ; les span, non, sinon les liens
-		// [<img|left>->URL] ne fonctionnent pas.
-		else {
-			$saut = preg_match(',<div ,', $rempl) ? "\n\n" : "";
-			$letexte = str_replace($match[0], $rempl . $saut, $letexte);
-		}
-	}
-
-	$pile--;
-	return $letexte;
-}
-
-
 //
 // Retourner le code HTML d'utilisation de fichiers envoyes
 //
@@ -750,8 +373,8 @@ function afficher_portfolio(
 			  $case = 0;
 			  echo "</tr>\n";
 		}
-			
-		document_vu($id_document);
+
+		$GLOBALS['doublons']['documents'].=",$id_document";
 	}
 	// fermer la derniere ligne
 	if ($case) {
@@ -877,7 +500,8 @@ function afficher_documents_non_inclus($id_article, $type = "article", $flag_mod
 	// Afficher portfolio
 	/////////
 
-	$doublons = document_vu();
+	// recupere les doublons du texte de l'article
+	$doublons = $GLOBALS['doublons']['documents'];
 
 	$images_liees = spip_query("SELECT docs.*,l.id_$type FROM spip_documents AS docs, spip_documents_".$type."s AS l, spip_types_documents AS lestypes WHERE l.id_$type=$id_article AND l.id_document=docs.id_document AND docs.mode='document' AND docs.id_type=lestypes.id_type AND lestypes.extension IN ('gif', 'jpg', 'png')" . (!$doublons ?'':" AND docs.id_document NOT IN ($doublons) ") . " ORDER BY 0+docs.titre, docs.date");
 
@@ -900,7 +524,8 @@ function afficher_documents_non_inclus($id_article, $type = "article", $flag_mod
 		echo "\n</table>\n";
 	}
 
-	$doublons = document_vu();
+	// recupere les doublons du texte de l'article + ceux du portfolio
+	$doublons = $GLOBALS['doublons']['documents'];
 
 	//// Documents associes
 	$documents_lies = spip_query("SELECT docs.*,l.id_$type FROM spip_documents AS docs, spip_documents_".$type."s AS l WHERE l.id_$type=$id_article AND l.id_document=docs.id_document AND docs.mode='document'" . (!$doublons ? '' : " AND docs.id_document NOT IN ($doublons) ") . " ORDER BY 0+docs.titre, docs.date");
@@ -1034,8 +659,6 @@ function afficher_case_document($id_document, $id, $script, $type, $deplier = fa
 	charger_generer_url();
 	$flag_deplie = teste_doc_deplie($id_document);
 
-	$doublons = ','.document_vu().',';
-
 	$document = spip_fetch_array(spip_query("SELECT * FROM spip_documents WHERE id_document = " . intval($id_document)));
 
 	$id_vignette = $document['id_vignette'];
@@ -1094,9 +717,11 @@ function afficher_case_document($id_document, $id, $script, $type, $deplier = fa
 		      ( _T('info_document').' '.majuscules($type_extension)));
 		echo "</div>";
 
+		// le doc est-il appele dans le texte ?
+		$doublon = ereg(",$id_document,", ','.$GLOBALS['doublons']['documents'].',');
 
 		// Affichage du raccourci <doc...> correspondant
-		if (!ereg(",$id_document,", $doublons)) {
+		if (!$doublon) {
 			echo "<div style='padding:2px;'><font size='1' face='arial,helvetica,sans-serif'>";
 			if ($options == "avancees" AND ($type_inclus == "embed" OR $type_inclus == "image") AND $largeur > 0 AND $hauteur > 0) {
 				echo "<b>"._T('info_inclusion_vignette')."</b><br />";
@@ -1147,7 +772,7 @@ function afficher_case_document($id_document, $id, $script, $type, $deplier = fa
 			$doc = 'doc';
 		else
 			$doc = 'img';
-		if (!ereg(",$id_document,", $doublons)) {
+		if (!$doublon) {
 			$raccourci_doc .=
 				affiche_raccourci_doc($doc, $id_document, 'left')
 				. affiche_raccourci_doc($doc, $id_document, 'center')
@@ -1164,11 +789,11 @@ function afficher_case_document($id_document, $id, $script, $type, $deplier = fa
 			echo "<div style='text-align: center; padding: 2px;'>\n";
 			echo document_et_vignette($document, $url, true);
 			echo "</div>\n";
-			if (!ereg(",$id_document,", $doublons))
+			if (!$doublon)
 				echo $raccourci_doc;
 		}
 
-		if (ereg(",$id_document,", $doublons))
+		if ($doublon)
 			echo $raccourci_doc;
 
 		echo formulaire_documenter($id_document, $document, $script, $type, $id, "document$id_document");
diff --git a/ecrire/inc/filtres.php b/ecrire/inc/filtres.php
index 0ae0ec953b5295123c1d028c45570b30ea52907f..ae1b59b1a9f8ea41b37ec23bd3a2719c8cee78b8 100644
--- a/ecrire/inc/filtres.php
+++ b/ecrire/inc/filtres.php
@@ -1581,11 +1581,34 @@ function table_valeur($table,$cle,$defaut=''){
 
 // filtre match pour faire des tests avec expression reguliere
 // [(#TEXTE|match{^ceci$,Uims})]
+// retourne le fragment de chaine qui "matche"
 // http://doc.spip.org/@match
-function match($texte,$expression,$modif="UimsS"){
+function match($texte, $expression, $modif="UimsS") {
 	$expression=str_replace("\/","/",$expression);
 	$expression=str_replace("/","\/",$expression);
-  return preg_match("/$expression/$modif",$texte);
+	return preg_match("/$expression/$modif",$texte, $r) ? $r[0] : false;
+}
+
+// filtre replace pour faire des operations avec expression reguliere
+// [(#TEXTE|replace{^ceci$,cela,Uims})]
+// http://doc.spip.org/@replace
+function replace($texte, $expression, $replace='', $modif="UimsS") {
+	$expression=str_replace("\/","/",$expression);
+	$expression=str_replace("/","\/",$expression);
+	return preg_replace("/$expression/$modif",$replace,$texte);
+}
+
+
+// cherche les documents numerotes dans un texte traite par propre()
+// et affecte les doublons['documents']
+// http://doc.spip.org/@traiter_doublons_documents
+function traiter_doublons_documents(&$doublons, $letexte) {
+	if (strstr($letexte, 'spip_document_') // evite le preg_match_all si inutile
+	AND preg_match_all(
+	',<[^>]+\sclass=["\']spip_document_([0-9]+)[\s"\'],imsS',
+	$letexte, $matches, PREG_PATTERN_ORDER))
+		$doublons['documents'] .= "," . join(',', $matches[1]);
+	return $letexte;
 }
 
 // filtre vide qui ne renvoie rien
diff --git a/ecrire/inc/texte.php b/ecrire/inc/texte.php
index 99a2b7b3a61d922c42b4c06113b1d47ee287e71b..7052190907dfe2d8b47837977c1efc19905a5683 100644
--- a/ecrire/inc/texte.php
+++ b/ecrire/inc/texte.php
@@ -537,15 +537,11 @@ function typo($letexte, $echapper=true) {
 	$letexte = strtr($letexte, $illegal, $protege);
 
 	//
-	// Installer les images et documents ;
+	// Installer les modeles, notamment images et documents ;
 	//
 	// NOTE : dans propre() ceci s'execute avant les tableaux a cause du "|",
 	// et apres les liens a cause du traitement de [<imgXX|right>->URL]
-	$letexte = pipeline('modeles', $letexte); # temporaire avant integration des modeles
-	if (preg_match(__preg_img, $letexte)) {
-		include_spip('inc/documents');
-		$letexte = inserer_documents($letexte);
-	}
+	$letexte = traiter_modeles($letexte);
 
 	// Appeler les fonctions de post-traitement
 	$letexte = pipeline('post_typo', $letexte);
@@ -927,19 +923,59 @@ function traiter_listes ($texte) {
 	return substr($texte, 0, -2);
 }
 
-// Definition de la regexp des images/documents
-define('__preg_img', ',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'>,iS');
 
 // fonction en cas de texte extrait d'un serveur distant:
 // on ne sait pas (encore) rapatrier les documents joints
-
+// TODO: gerer les modeles ?
 // http://doc.spip.org/@supprime_img
 function supprime_img($letexte) {
 	$message = _T('img_indisponible');
-	preg_replace(__preg_img, "($message)", $letexte);
+	preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'>,iS',
+		"($message)", $letexte);
 	return $letexte;
 }
 
+// traite les modeles (dans la fonction typo), en remplacant
+// le raccourci <modeleN|parametres> par la page calculee a
+// partir du squelette modeles/modele.html
+// http://doc.spip.org/@traiter_modeles
+function traiter_modeles($texte) {
+	if (preg_match_all(',<([a-z_-]+)([0-9]+)([|]([^>]+))?'.'>,iS',
+	$texte, $matches, PREG_SET_ORDER)) {
+		include_spip('public/assembler');
+		foreach ($matches as $regs) {
+			$modele = inclure_modele($regs[4], $regs[1], $regs[2]);
+			if ($modele !== false) {
+				$rempl = code_echappement($modele);
+				$cherche = $regs[0];
+
+				// XHTML : remplacer par une <div onclick> le lien
+				// dans le cas [<docXX>->lien] ; sachant qu'il n'existe
+				// pas de bonne solution en XHTML pour produire un lien
+				// sur une div (!!)...
+				if (substr($rempl, 0, 5) == '<div '
+				AND preg_match(
+				',(<a [^>]+>)\s*'.preg_quote($regs[0]).'\s*</a>,Uims',
+				$texte, $r)) {
+					$lien = extraire_attribut($r[1], 'href');
+					$cherche = $r[0];
+					$rempl = '<div style="cursor:pointer;cursor:hand;" '
+					.'onclick="document.location=\''.$lien
+					.'\'"'
+##						.' href="'.$lien.'"' # href deviendra legal en XHTML2
+					.'>'
+					.$rempl
+					.'</div>';
+				}
+
+				$texte = str_replace($cherche, $rempl, $texte);
+			}
+		}
+	}
+
+	return $texte;
+}
+
 
 //
 // Une fonction pour fermer les paragraphes ; on essaie de preserver
diff --git a/ecrire/public/assembler.php b/ecrire/public/assembler.php
index 194505ea596b3a73d9f6d5882080062046e29384..ffc3429bab567dd2c5373f8a151423bc702df76e 100644
--- a/ecrire/public/assembler.php
+++ b/ecrire/public/assembler.php
@@ -372,7 +372,7 @@ function recuperer_fond($fond, $contexte=array()) {
 	if ($GLOBALS['flag_ob'] AND ($page['process_ins'] != 'html')) {
 		ob_start();
 		eval('?' . '>' . $page['texte']);
-		$page['texte'] = ob_get_contents(); 
+		$page['texte'] = ob_get_contents();
 		ob_end_clean();
 	}
 
@@ -403,4 +403,52 @@ function creer_contexte_de_modele($args) {
 	return $contexte;
 }
 
+// Calcule le modele et retourne la mini-page ainsi calculee
+function inclure_modele($squelette, $type, $id) {
+	static $compteur;
+	if (++$compteur>10) return ''; # ne pas boucler indefiniment
+
+	$type = strtolower($type);
+
+	$fond = 'modeles/'.$type;
+	if (preg_match(',^([a-z_0-9-]+)([|]|$),i', $squelette, $sub)) {
+		if (in_array(strtolower($sub[1]),
+		array('left', 'right', 'center')))
+			$align = $sub[0];
+
+		$fond = 'modeles/'.$type.'_'.$sub[1];
+
+		if (!find_in_path($fond.'.html')) {
+			$fond = 'modeles/'.$type;
+			if (!$align)
+				$class = $sub[1];
+		}
+	}
+
+	// en cas d'echec on passe la main au suivant
+	if (!find_in_path($fond.'.html'))
+		return false;
+
+	$contexte = array(
+		'id_'.$type => $id,
+		'fond' => $fond,
+		'lang' => $GLOBALS['spip_lang']
+	);
+	if ($align)
+		$contexte['align'] = $align;
+
+	if ($class)
+		$contexte['class'] = $class;
+
+	// Traiter les parametres
+	// par exemple : <img1|center>, <emb12|autostart=true> ou <doc1|lang=en>
+	$contexte = array_merge($contexte, 
+		creer_contexte_de_modele(explode('|', $squelette))); 
+
+	// Appliquer le modele avec le contexte
+	$retour = recuperer_fond($fond, $contexte);
+	$compteur--;
+	return $retour;
+}
+
 ?>
diff --git a/ecrire/public/composer.php b/ecrire/public/composer.php
index 94757da57ea3ba32191f6a442614fd5729d7241c..32f4de9e3ee47275e1e219a2890d2d297f7da211 100644
--- a/ecrire/public/composer.php
+++ b/ecrire/public/composer.php
@@ -460,17 +460,6 @@ function calcule_embed_document($id_document, $filtres, &$doublons, $doubdoc) {
 	return embed_document($id_document, $filtres, false);
 }
 
-// cherche les documents numerotes dans un texte traite par propre()
-// et affecte les doublons['documents']
-// http://doc.spip.org/@traiter_doublons_documents
-function traiter_doublons_documents(&$doublons, $letexte) {
-	if (preg_match_all(
-	',<(span|div\s)[^>]*class=["\']spip_document_([0-9]+) ,',
-	$letexte, $matches, PREG_PATTERN_ORDER))
-		$doublons['documents'] .= "," . join(',', $matches[2]);
-	return $letexte;
-}
-
 
 // les balises dynamiques et EMBED ont des filtres sans arguments 
 // car en fait ce sont des arguments pas des filtres.