Créer des pages en gardant les balises HTML

Petit nouveau ! | 7 Messages

05 déc. 2011, 12:15

Bonjour,

Je me tourne vers vous afin que vous m'aidiez à comprendre comment je pourrai clôturer ma problématique.

Prenons un cas concret :

J'ai un texte de 30000 caractères au format html (utilisation de tinymce).
Mon objectif est de découper ce texte et de créer des pages

Ma base de données contient donc deux tables :


- "ARTICLES" => id_article, titre_article...
- "ARTICLES_PAGES" =>id_article,num_page,contenu_page'

Un article peut donc avoir 1 ou n page


Bien, maintenant, je regarde pour trouver une fonction qui découperait un code HTML sans briser les balises et je trouve :
<?php
/**
 * Coupe une chaine en gardant le formatage HTML
 * @param string $text Texte à couper
 * @param integer $length Longueur à garder
 * @param string $ending Caractères à ajouter à la fin
 * @param boolean $exact Coupure exacte
 * @return string
 */
function trunchtml($text, $length, $ending = '...', $exact = false) {
    if(strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
        return $text;
    }
    preg_match_all('/(<.+?>)?([^<>]*)/is', $text, $matches, PREG_SET_ORDER);
    $total_length = 0;
    $arr_elements = array();
    $truncate = '';
    foreach($matches as $element) {
        if(!empty($element[1])) {
            if(preg_match('/^<\s*.+?\/\s*>$/s', $element[1])) {
            } else if(preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $element[1], $element2)) {
                $pos = array_search($element2[1], $arr_elements);
                if($pos !== false) {
                    unset($arr_elements[$pos]);
                }
            } else if(preg_match('/^<\s*([^\s>!]+).*?>$/s', $element[1], $element2)) {
                array_unshift($arr_elements,
                strtolower($element2[1]));
            }
            $truncate .= $element[1];
        }
        $content_length = strlen(preg_replace('/(&[a-z]{1,6};|&#[0-9]+;)/i', ' ', $element[2]));
        if($total_length >= $length) {
            break;
        } elseif ($total_length+$content_length > $length) {
            $left = $total_length>$length?$total_length-$length:$length-$total_length;
            $entities_length = 0;
            if(preg_match_all('/&[a-z]{1,6};|&#[0-9]+;/i', $element[2], $element3, PREG_OFFSET_CAPTURE)) {
                foreach($element3[0] as $entity) {
                    if($entity[1]+1-$entities_length <= $left) {
                        $left--;
                        $entities_length += strlen($entity[0]);
                    } else break;
                }
            }
            $truncate .= substr($element[2], 0, $left+$entities_length);
            break;
        } else {
            $truncate .= $element[2];
            $total_length += $content_length;
        }
    }
    if(!$exact) {
        $spacepos = strrpos($truncate, ' ');
        if(isset($spacepos)) {
            $truncate = substr($truncate, 0, $spacepos);
        }
    }
    $truncate .= $ending;
    foreach($arr_elements as $element) {
        $truncate .= '</' . $element . '>';
    }
    return $truncate;
}

Ensuite, j’essaie ceci :

$texte = html_entity_decode(htmlspecialchars_decode('<p style="margin-bottom: 0in;">Un week end de septembre annonc&eacute;
magnifique en milieu de semaine par la m&eacute;t&eacute;o. Il ne nous en faut
pas plus pour programmer une sortie sur deux jours dans le haut Jura.
Les deux plus hauts sommets du massif Jurassien devraient &ecirc;tre alors
re-visit&eacute;s (<a title="Destination cr&ecirc;t de la neige!" href="/balades_pieds/voir_balades_pieds.php?numdep=1&dep=&voir_balade=121" target="_blank">voir balade</a>) mais cette fois-ci &agrave; quatre et en dormant
dans un refuge.</p>'));


$longueurTexte = strlen($texte);
$break = 100;
$nombrePages = ceil($longueurTexte/$break);
if ( $longueurTexte > 0 ) 
{
    while ($nombrePages >= 1) 
	{
        $resume = '';
        // appel a la fonction et récupération du texte coupé
        $resume = trunchtml($texte,$break);
        $breakAjusting = strlen($resume);
		// on enleve au texte le morceau qu'on a coupé
        $texte = substr_replace($texte,'',0,$breakAjusting);
        // On soustrait une page
        $nombrePages--;
        // On insere le résumé en base
       
		echo $resume.'--------------------------------------------------------------<br />';
    }
    // s'il reste encore des caractères mais qu'on est plus dans le while 
    // c'est que le nombre restant est inferieur a la taille d'une page complete...
    // On gère aussi le cas ou on est pas entré dans le while car le texte fait moins de 1000
    // caractère (donc moins d'une page complete....
    if ( strlen($texte) && ($nombrePages > 0 ))
	{
		echo $texte;
    }
    $msg = 'Tout est OK';
}
else
{
    $msg = 'Texte manquant';
}
echo $msg;

Sauf que les coupures ne sont pas bonnes, le résultat sur le web est disponible ici : http://www.partir-en-vtt.com/test_trunc.php

Exemple :

Code : Tout sélectionner

Il ne nous en... -------------------------------------------------------------- as plus pour programmer une sortie sur deux jours dans le haut Jura.
On remarque bien que la coupure n'est pas bonne, il manque des mots.


Par avance, merci de me dire comment améliorer et réussir à résoudre ce souci.

@bientôt

Mammouth du PHP | 672 Messages

05 déc. 2011, 14:55

Bonjour.

En fait tu as récupéré une fonction dont l'objectif initial est d'afficher grosso modo les X premiers caractères et tu l'as bidouillée pour découper le texte. C'est bien ça ?

Pour débugger, on va procéder par tâtonnement...

Première étape : un texte simple - non formaté :
Il te manque à chaque fois trois caractères. Ce qui correspond à quoi ? Peut-être aux "..." :roll:
=> essai avec juste un point, puis avec "...." => c'est confirmé. Ta chaîne $resume contient ces "...", contrairement au texte original #-o
Comme on ne vaut pas commencer par un espace, il "suffit" de soustraire 2 (les ... moins l'espace) à la variable $breakAjusting...

Deuxième étape : test avec des balises simples (un texte encadré par <p>...</p>).
Là, il manque juste 4 caractères dans la première "page". Ces 4 caractères correspondent à quoi ? A la balise de fermeture du paragraphe #-o
On peut tester avec une autre balise (<em>...</em> par exemple), pour constater que c'est bien ça...

etc.

Au final, la fonction que tu utilises pose pas mal de problèmes, et tu risques de vite arriver à une usine à gaz...
Par exemple, pourquoi conserver le paragraphe sur ta première page et pas sur les suivantes ? Ou comment gérer la ponctuation (tu peux vérifier avec un texte sans espace mais avec des virgules pour voir) ?

Petit nouveau ! | 7 Messages

05 déc. 2011, 15:19

Bonjour et merci pour l'aide apportée
on ne vaut pas commencer par un espace, il "suffit" de soustraire 2 (les ... moins l'espace) à la variable $breakAjusting...
Et donc faire :

Code : Tout sélectionner

$breakAjusting = strlen($resume)-2;
?

Si c'est ça, cela ne fonctionne pas mieux.

En fait, ça ne me dérange pas forcément qu'il y ait une coupure au milieu d'une phrase.

Mon objectif principal est de garder le formatage en ne coupant pas une balise HTML et créer ainsi des pages selon le nombre de caractères initial.

Que pourrait-on faire pour éviter "l'usine à gaz" ? Avez-vous déjà résolu un problème similaire ?

Mammouth du PHP | 2278 Messages

05 déc. 2011, 22:49

Spontanément, je me tournerais vers preg_split
http://fr2.php.net/manual/fr/function.preg-split.php
en particulier cette intervention : nesbert at gmail dot com 28-Jan-2010 11:46
Je n'ai rien vérifié, mais ça me semble une bonne piste.
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

Petit nouveau ! | 7 Messages

06 déc. 2011, 15:38

<a href="/php/articles/voir_article.php?id_article='.$tag['id_article'].'" title="Voir l\'article" tagret="_self">
Bonjour,

Pourriez vous m'aider à utiliser cette fonction ?

Merci

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

06 déc. 2011, 15:51

pour traiter du html utilise plutôt l'extension DOM (ou simpleXML).

@+
Il en faut peu pour être heureux ......

Petit nouveau ! | 7 Messages

06 déc. 2011, 16:39

C'est étrange que personne n'ait eu ce genre de besoin. Pourtant, cela me semble important pour créer un article qui possède plusieurs pages.

je ne voudrais pas réinventer la roue, est-ce que vous avez des exemples ?

Merci

Mammouth du PHP | 2278 Messages

06 déc. 2011, 22:46

/**
<?PHP
/ * Split a string into groups of words with a line no longer than $max
 * characters.
 * @author nesbert at gmail dot com
 * @param string $string la chaîne à traiter
 * @param integer $max le nombre de caractères par ligne
 * @return array lines : tableau de lignes
 **/
function split_words($string, $max = 1)
{
    //$words = preg_split('/\s/', $string);l'original qui se fait tromper par les espaces dans les balises HTML
    $words = preg_split('/\[,;:!?]/', $string); // ma solution  ne s'occupe pas des espaces pour séparer les mots

    $lines = array();
    $line = '';
   
    foreach ($words as $k => $word) {
        $length = strlen($line . ' ' . $word);
        if ($length <= $max) {
            $line .= ' ' . $word;
        } else if ($length > $max) {
            if (!empty($line)) $lines[] = trim($line);
            $line = $word;
        } else {
            $lines[] = trim($line) . ' ' . $word;
            $line = '';
        }
    }
    $lines[] = ($line = trim($line)) ? $line : $word;

    return $lines;
}

$texte =<<<EOD
le texte sous n'importe quelle forme (j'ai testé sur un texte de 39 Ko)
EOD;
$table = split_words ($texte, 50) ; // 50 est arbitraire c'est la longueur de ligne
print_r($table);
?>
Le vrai problème, c'est s'il y a une feuille de styles externe ou des valeurs récupérées par des include
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

Petit nouveau ! | 7 Messages

07 déc. 2011, 10:18

Bonjour et merci pour le code !

J'ai essayé ce dernier avec un texte assez long (30000 caractères env). J'ai essayé de modifier le nombre de mot mais à chaque fois l'array contient l'ensemble du texte.

Comment cela se fait ?

Merci :)

Petit nouveau ! | 7 Messages

12 déc. 2011, 10:48

up svp

Invité
Invité n'ayant pas de compte PHPfrance

19 déc. 2011, 10:55

Un petit up :)

merci