X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=www%2Fecrire%2Finc%2Fcharsets.php;h=a411afff9b4921e0487aa97d732df57f58c0ffbf;hb=4f443dce95ff6f8221c189880a70c74ce1c1f238;hp=01cdecd246977175401209d5f10db405787c29b5;hpb=4a628e9b277d3617535f99d663ca79fa2e891177;p=lhc%2Fweb%2Fwww.git diff --git a/www/ecrire/inc/charsets.php b/www/ecrire/inc/charsets.php index 01cdecd2..a411afff 100644 --- a/www/ecrire/inc/charsets.php +++ b/www/ecrire/inc/charsets.php @@ -3,7 +3,7 @@ /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * - * Copyright (c) 2001-2016 * + * Copyright (c) 2001-2017 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * @@ -16,61 +16,80 @@ * Ce fichier contient les fonctions relatives à la gestion de charsets, * à la conversion de textes dans différents charsets et * propose des fonctions émulant la librairie mb si elle est absente - * - * @package SPIP\Texte\Charsets -**/ + * + * @package SPIP\Core\Texte\Charsets + **/ // securité -if (!defined('_ECRIRE_INC_VERSION')) return; +if (!defined('_ECRIRE_INC_VERSION')) { + return; +} +// se faciliter la lecture du charset +include_spip('inc/config'); /** * Charge en mémoire la liste des caractères d'un charset * - * Charsets supportes en natif : voir les tables dans ecrire/charsets/ - * Les autres charsets sont supportes via mbstring() - * + * Charsets supportés en natif : voir les tables dans ecrire/charsets/ + * Les autres charsets sont supportés via mbstring() + * * @param string $charset - * Charset à charger + * Charset à charger. * Par défaut (AUTO), utilise le charset du site * @return string|bool - * Nom du charset - * false si le charset n'est pas décrit dans le répertoire charsets/ -**/ -function load_charset ($charset = 'AUTO') { - if ($charset == 'AUTO') + * - Nom du charset + * - false si le charset n'est pas décrit dans le répertoire charsets/ + **/ +function load_charset($charset = 'AUTO') { + if ($charset == 'AUTO') { $charset = $GLOBALS['meta']['charset']; + } $charset = trim(strtolower($charset)); - if (isset($GLOBALS['CHARSET'][$charset])) + if (isset($GLOBALS['CHARSET'][$charset])) { return $charset; + } if ($charset == 'utf-8') { $GLOBALS['CHARSET'][$charset] = array(); + return $charset; } - + // Quelques synonymes - if ($charset == '') $charset = 'iso-8859-1'; - else if ($charset == 'windows-1250') $charset = 'cp1250'; - else if ($charset == 'windows-1251') $charset = 'cp1251'; - else if ($charset == 'windows-1256') $charset = 'cp1256'; + if ($charset == '') { + $charset = 'iso-8859-1'; + } else { + if ($charset == 'windows-1250') { + $charset = 'cp1250'; + } else { + if ($charset == 'windows-1251') { + $charset = 'cp1251'; + } else { + if ($charset == 'windows-1256') { + $charset = 'cp1256'; + } + } + } + } if (find_in_path($charset . '.php', 'charsets/', true)) { return $charset; } else { spip_log("Erreur: pas de fichier de conversion 'charsets/$charset'"); $GLOBALS['CHARSET'][$charset] = array(); + return false; } } /** - * Verifier qu'on peut utiliser mb_string + * Vérifier qu'on peut utiliser mb_string * * @return bool * true si toutes les fonctions mb nécessaires sont présentes -**/ + **/ function init_mb_string() { static $mb; @@ -78,18 +97,21 @@ function init_mb_string() { // et que le charset interne est connu de mb_string if (!$mb) { if (function_exists('mb_internal_encoding') - AND function_exists('mb_detect_order') - AND function_exists('mb_substr') - AND function_exists('mb_strlen') - AND function_exists('mb_encode_mimeheader') - AND function_exists('mb_encode_numericentity') - AND function_exists('mb_decode_numericentity') - AND mb_detect_order($GLOBALS['meta']['charset']) + and function_exists('mb_detect_order') + and function_exists('mb_substr') + and function_exists('mb_strlen') + and function_exists('mb_strtolower') + and function_exists('mb_strtoupper') + and function_exists('mb_encode_mimeheader') + and function_exists('mb_encode_numericentity') + and function_exists('mb_decode_numericentity') + and mb_detect_order(lire_config('charset', _DEFAULT_CHARSET)) ) { mb_internal_encoding('utf-8'); $mb = 1; - } else + } else { $mb = -1; + } } return ($mb == 1); @@ -100,25 +122,27 @@ function init_mb_string() { * * Celui-ci coupe sur certaines versions la chaine * quand un caractère n'appartient pas au charset - * + * * @link http://php.net/manual/fr/function.iconv.php - * + * * @return bool * true si iconv fonctionne correctement -**/ + **/ function test_iconv() { static $iconv_ok; if (!$iconv_ok) { - if (!function_exists('iconv')) + if (!function_exists('iconv')) { $iconv_ok = -1; - else { - if (utf_32_to_unicode(@iconv('utf-8', 'utf-32', 'chaine de test')) == 'chaine de test') + } else { + if (utf_32_to_unicode(@iconv('utf-8', 'utf-32', 'chaine de test')) == 'chaine de test') { $iconv_ok = 1; - else + } else { $iconv_ok = -1; + } } } + return ($iconv_ok == 1); } @@ -127,18 +151,22 @@ function test_iconv() { * Test de fonctionnement du support UTF-8 dans PCRE * * Contournement bug Debian Woody - * + * * @return bool * true si PCRE supporte l'UTF-8 correctement -**/ + **/ function test_pcre_unicode() { static $pcre_ok = 0; if (!$pcre_ok) { - $s = " ".chr(195).chr(169)."t".chr(195).chr(169)." "; - if (preg_match(',\W...\W,u', $s)) $pcre_ok = 1; - else $pcre_ok = -1; + $s = " " . chr(195) . chr(169) . "t" . chr(195) . chr(169) . " "; + if (preg_match(',\W...\W,u', $s)) { + $pcre_ok = 1; + } else { + $pcre_ok = -1; + } } + return $pcre_ok == 1; } @@ -153,7 +181,7 @@ function test_pcre_unicode() { * Servait à inc/ortho passé dans le grenier * @return string * Plage de caractères -**/ + **/ function pcre_lettres_unicode() { static $plage_unicode; @@ -164,12 +192,12 @@ function pcre_lettres_unicode() { . '\x{100}-\x{24f}' // europeen etendu . '\x{300}-\x{1cff}' // des tas de trucs ; - } - else { + } else { // fallback a trois sous $plage_unicode = '\w'; } } + return $plage_unicode; } @@ -186,7 +214,7 @@ function pcre_lettres_unicode() { * Servait à inc/ortho passé dans le grenier * @return string * Plage de caractères -**/ + **/ function plage_punct_unicode() { return '\xE2(\x80[\x80-\xBF]|\x81[\x80-\xAF])'; } @@ -196,84 +224,90 @@ function plage_punct_unicode() { * * Cf. charsets/iso-8859-1.php (qu'on recopie ici pour aller plus vite) * On peut passer un charset cible en parametre pour accelerer le passage iso-8859-1 -> autre charset - * - * @param string $texte + * + * @param string|array $texte * Le texte à corriger * @param string $charset * Charset d'origine du texte * Par défaut (AUTO) utilise le charset du site * @param string $charset_cible * Charset de destination (unicode par défaut) - * @return string + * @return string|array * Texte corrigé -**/ -function corriger_caracteres_windows($texte, $charset='AUTO', $charset_cible='unicode') { + **/ +function corriger_caracteres_windows($texte, $charset = 'AUTO', $charset_cible = 'unicode') { static $trans; - + if (is_array($texte)) { return array_map('corriger_caracteres_windows', $texte); } - - if ($charset=='AUTO') $charset = $GLOBALS['meta']['charset']; + + if ($charset == 'AUTO') { + $charset = lire_config('charset', _DEFAULT_CHARSET); + } if ($charset == 'utf-8') { $p = chr(194); - if (strpos($texte,$p)==false) + if (strpos($texte, $p) == false) { return $texte; - } else if ($charset == 'iso-8859-1') { - $p = ''; - } else - return $texte; + } + } else { + if ($charset == 'iso-8859-1') { + $p = ''; + } else { + return $texte; + } + } if (!isset($trans[$charset][$charset_cible])) { $trans[$charset][$charset_cible] = array( - $p.chr(128) => "€", - $p.chr(129) => ' ', # pas affecte - $p.chr(130) => "‚", - $p.chr(131) => "ƒ", - $p.chr(132) => "„", - $p.chr(133) => "…", - $p.chr(134) => "†", - $p.chr(135) => "‡", - $p.chr(136) => "ˆ", - $p.chr(137) => "‰", - $p.chr(138) => "Š", - $p.chr(139) => "‹", - $p.chr(140) => "Œ", - $p.chr(141) => ' ', # pas affecte - $p.chr(142) => "Ž", - $p.chr(143) => ' ', # pas affecte - $p.chr(144) => ' ', # pas affecte - $p.chr(145) => "‘", - $p.chr(146) => "’", - $p.chr(147) => "“", - $p.chr(148) => "”", - $p.chr(149) => "•", - $p.chr(150) => "–", - $p.chr(151) => "—", - $p.chr(152) => "˜", - $p.chr(153) => "™", - $p.chr(154) => "š", - $p.chr(155) => "›", - $p.chr(156) => "œ", - $p.chr(157) => ' ', # pas affecte - $p.chr(158) => "ž", - $p.chr(159) => "Ÿ", + $p . chr(128) => "€", + $p . chr(129) => ' ', # pas affecte + $p . chr(130) => "‚", + $p . chr(131) => "ƒ", + $p . chr(132) => "„", + $p . chr(133) => "…", + $p . chr(134) => "†", + $p . chr(135) => "‡", + $p . chr(136) => "ˆ", + $p . chr(137) => "‰", + $p . chr(138) => "Š", + $p . chr(139) => "‹", + $p . chr(140) => "Œ", + $p . chr(141) => ' ', # pas affecte + $p . chr(142) => "Ž", + $p . chr(143) => ' ', # pas affecte + $p . chr(144) => ' ', # pas affecte + $p . chr(145) => "‘", + $p . chr(146) => "’", + $p . chr(147) => "“", + $p . chr(148) => "”", + $p . chr(149) => "•", + $p . chr(150) => "–", + $p . chr(151) => "—", + $p . chr(152) => "˜", + $p . chr(153) => "™", + $p . chr(154) => "š", + $p . chr(155) => "›", + $p . chr(156) => "œ", + $p . chr(157) => ' ', # pas affecte + $p . chr(158) => "ž", + $p . chr(159) => "Ÿ", ); - if ($charset_cible!='unicode'){ - foreach($trans[$charset][$charset_cible] as $k=>$c) + if ($charset_cible != 'unicode') { + foreach ($trans[$charset][$charset_cible] as $k => $c) { $trans[$charset][$charset_cible][$k] = unicode2charset($c, $charset_cible); + } } } return @str_replace(array_keys($trans[$charset][$charset_cible]), - array_values($trans[$charset][$charset_cible]),$texte); + array_values($trans[$charset][$charset_cible]), $texte); } - /** * Transforme les entités HTML en unicode - * + * * Transforme les é en { * * @param string $texte @@ -282,24 +316,26 @@ function corriger_caracteres_windows($texte, $charset='AUTO', $charset_cible='un * true pour *ne pas convertir* les caracteres malins < & etc. * @return string * Texte converti -**/ -function html2unicode($texte, $secure=false) { - if (strpos($texte,'&') === false) return $texte; + **/ +function html2unicode($texte, $secure = false) { + if (strpos($texte, '&') === false) { + return $texte; + } static $trans = array(); if (!$trans) { - global $CHARSET; load_charset('html'); - foreach ($CHARSET['html'] as $key => $val) { + foreach ($GLOBALS['CHARSET']['html'] as $key => $val) { $trans["&$key;"] = $val; } } - if ($secure) - return str_replace(array_keys($trans),array_values($trans),$texte); - else - return str_replace(array('&', '"', '<', '>'),array('&', '"', '<', '>'), - str_replace(array_keys($trans),array_values($trans),$texte) + if ($secure) { + return str_replace(array_keys($trans), array_values($trans), $texte); + } else { + return str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), + str_replace(array_keys($trans), array_values($trans), $texte) ); + } } @@ -312,18 +348,18 @@ function html2unicode($texte, $secure=false) { * Texte à convertir * @return string * Texte converti -**/ + **/ function mathml2unicode($texte) { static $trans; if (!$trans) { - global $CHARSET; load_charset('mathml'); - - foreach ($CHARSET['mathml'] as $key => $val) + + foreach ($GLOBALS['CHARSET']['mathml'] as $key => $val) { $trans["&$key;"] = $val; + } } - return str_replace(array_keys($trans),array_values($trans),$texte); + return str_replace(array_keys($trans), array_values($trans), $texte); } @@ -331,11 +367,11 @@ function mathml2unicode($texte) { * Transforme une chaine en entites unicode  * * Utilise la librairie mb si elle est présente. - * + * * @internal * Note: l'argument $forcer est obsolete : il visait a ne pas * convertir les accents iso-8859-1 - * + * * @param string $texte * Texte à convertir * @param string $charset @@ -343,59 +379,70 @@ function mathml2unicode($texte) { * Par défaut (AUTO), le charset est celui du site. * @return string * Texte converti en unicode -**/ -function charset2unicode($texte, $charset='AUTO' /* $forcer: obsolete*/) { + **/ +function charset2unicode($texte, $charset = 'AUTO' /* $forcer: obsolete*/) { static $trans; - if ($charset == 'AUTO') - $charset = $GLOBALS['meta']['charset']; + if ($charset == 'AUTO') { + $charset = lire_config('charset', _DEFAULT_CHARSET); + } - if ($charset == '') $charset = 'iso-8859-1'; + if ($charset == '') { + $charset = 'iso-8859-1'; + } $charset = strtolower($charset); switch ($charset) { - case 'utf-8': - case 'utf8': - return utf_8_to_unicode($texte); + case 'utf-8': + case 'utf8': + return utf_8_to_unicode($texte); - case 'iso-8859-1': - $texte = corriger_caracteres_windows($texte, 'iso-8859-1'); + case 'iso-8859-1': + $texte = corriger_caracteres_windows($texte, 'iso-8859-1'); // pas de break; ici, on suit sur default: - default: - // mbstring presente ? - if (init_mb_string()) { - if ($order = mb_detect_order() # mb_string connait-il $charset? - AND mb_detect_order($charset)) { - $s = mb_convert_encoding($texte, 'utf-8', $charset); - if ($s && $s != $texte) return utf_8_to_unicode($s); + default: + // mbstring presente ? + if (init_mb_string()) { + if ($order = mb_detect_order() # mb_string connait-il $charset? + and mb_detect_order($charset) + ) { + $s = mb_convert_encoding($texte, 'utf-8', $charset); + if ($s && $s != $texte) { + return utf_8_to_unicode($s); + } + } + mb_detect_order($order); # remettre comme precedemment } - mb_detect_order($order); # remettre comme precedemment - } - // Sinon, peut-etre connaissons-nous ce charset ? - if (!isset($trans[$charset])) { - global $CHARSET; - if ($cset = load_charset($charset) - AND is_array($CHARSET[$cset])) - foreach ($CHARSET[$cset] as $key => $val) { - $trans[$charset][chr($key)] = '&#'.$val.';'; + // Sinon, peut-etre connaissons-nous ce charset ? + if (!isset($trans[$charset])) { + if ($cset = load_charset($charset) + and is_array($GLOBALS['CHARSET'][$cset]) + ) { + foreach ($GLOBALS['CHARSET'][$cset] as $key => $val) { + $trans[$charset][chr($key)] = '&#' . $val . ';'; + } + } + } + if (count($trans[$charset])) { + return str_replace(array_keys($trans[$charset]), array_values($trans[$charset]), $texte); } - } - if (count($trans[$charset])) - return str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte); - - // Sinon demander a iconv (malgre le fait qu'il coupe quand un - // caractere n'appartient pas au charset, mais c'est un probleme - // surtout en utf-8, gere ci-dessus) - if (test_iconv()) { - $s = iconv($charset, 'utf-32le', $texte); - if ($s) return utf_32_to_unicode($s); - } - // Au pire ne rien faire - spip_log("erreur charset '$charset' non supporte"); - return $texte; + // Sinon demander a iconv (malgre le fait qu'il coupe quand un + // caractere n'appartient pas au charset, mais c'est un probleme + // surtout en utf-8, gere ci-dessus) + if (test_iconv()) { + $s = iconv($charset, 'utf-32le', $texte); + if ($s) { + return utf_32_to_unicode($s); + } + } + + // Au pire ne rien faire + spip_log("erreur charset '$charset' non supporte"); + + return $texte; } } @@ -405,7 +452,7 @@ function charset2unicode($texte, $charset='AUTO' /* $forcer: obsolete*/) { * * Attention on ne transforme pas les entites < € car si elles * ont ete encodees ainsi c'est a dessein - * + * * @param string $texte * Texte unicode à transformer * @param string $charset @@ -413,44 +460,45 @@ function charset2unicode($texte, $charset='AUTO' /* $forcer: obsolete*/) { * Par défaut (AUTO), le charset sera celui du site. * @return string * Texte transformé dans le charset souhaité -**/ -function unicode2charset($texte, $charset='AUTO') { + **/ +function unicode2charset($texte, $charset = 'AUTO') { static $CHARSET_REVERSE; static $trans = array(); - - if ($charset == 'AUTO') - $charset = $GLOBALS['meta']['charset']; - switch($charset) { - case 'utf-8': - return unicode_to_utf_8($texte); - break; + if ($charset == 'AUTO') { + $charset = lire_config('charset', _DEFAULT_CHARSET); + } - default: - $charset = load_charset($charset); + switch ($charset) { + case 'utf-8': + return unicode_to_utf_8($texte); + break; - if (!is_array($CHARSET_REVERSE[$charset])) { - $CHARSET_REVERSE[$charset] = array_flip($GLOBALS['CHARSET'][$charset]); - } + default: + $charset = load_charset($charset); - if (!isset($trans[$charset])){ - $trans[$charset]=array(); - $t = &$trans[$charset]; - for($e=128;$e<255;$e++){ - $h = dechex($e); - if ($s = isset($CHARSET_REVERSE[$charset][$e])){ - $s = $CHARSET_REVERSE[$charset][$e]; - $t['&#'.$e.';'] = $t['�'.$e.';'] = $t['�'.$e.';'] = chr($s); - $t['&#x'.$h.';'] = $t['�'.$h.';'] = $t['�'.$h.';'] = chr($s); - } - else{ - $t['&#'.$e.';'] = $t['�'.$e.';'] = $t['�'.$e.';'] = chr($e); - $t['&#x'.$h.';'] = $t['�'.$h.';'] = $t['�'.$h.';'] = chr($e); + if (!is_array($CHARSET_REVERSE[$charset])) { + $CHARSET_REVERSE[$charset] = array_flip($GLOBALS['CHARSET'][$charset]); + } + + if (!isset($trans[$charset])) { + $trans[$charset] = array(); + $t = &$trans[$charset]; + for ($e = 128; $e < 255; $e++) { + $h = dechex($e); + if ($s = isset($CHARSET_REVERSE[$charset][$e])) { + $s = $CHARSET_REVERSE[$charset][$e]; + $t['&#' . $e . ';'] = $t['�' . $e . ';'] = $t['�' . $e . ';'] = chr($s); + $t['&#x' . $h . ';'] = $t['�' . $h . ';'] = $t['�' . $h . ';'] = chr($s); + } else { + $t['&#' . $e . ';'] = $t['�' . $e . ';'] = $t['�' . $e . ';'] = chr($e); + $t['&#x' . $h . ';'] = $t['�' . $h . ';'] = $t['�' . $h . ';'] = chr($e); + } } } - } - $texte = str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte); - return $texte; + $texte = str_replace(array_keys($trans[$charset]), array_values($trans[$charset]), $texte); + + return $texte; } } @@ -458,8 +506,8 @@ function unicode2charset($texte, $charset='AUTO') { /** * Importer un texte depuis un charset externe vers le charset du site * - * Les caracteres non resolus sont transformes en { - * + * Les caractères non resolus sont transformés en `{`; + * * @param string $texte * Texte unicode à importer * @param string $charset @@ -467,15 +515,16 @@ function unicode2charset($texte, $charset='AUTO') { * Par défaut (AUTO), le charset d'origine est celui du site. * @return string * Texte transformé dans le charset site -**/ + **/ function importer_charset($texte, $charset = 'AUTO') { static $trans = array(); // on traite le cas le plus frequent iso-8859-1 vers utf directement pour aller plus vite ! - if (($charset == 'iso-8859-1') && ($GLOBALS['meta']['charset']=='utf-8')){ - $texte = corriger_caracteres_windows($texte, 'iso-8859-1',$GLOBALS['meta']['charset']); + if (($charset == 'iso-8859-1') && ($GLOBALS['meta']['charset'] == 'utf-8')) { + $texte = corriger_caracteres_windows($texte, 'iso-8859-1', $GLOBALS['meta']['charset']); if (init_mb_string()) { if ($order = mb_detect_order() # mb_string connait-il $charset? - AND mb_detect_order($charset)) { + and mb_detect_order($charset) + ) { $s = mb_convert_encoding($texte, 'utf-8', $charset); } mb_detect_order($order); # remettre comme precedemment @@ -483,36 +532,41 @@ function importer_charset($texte, $charset = 'AUTO') { } // Sinon, peut-etre connaissons-nous ce charset ? if (!isset($trans[$charset])) { - global $CHARSET; if ($cset = load_charset($charset) - AND is_array($CHARSET[$cset])) - foreach ($CHARSET[$cset] as $key => $val) { - $trans[$charset][chr($key)] = unicode2charset('&#'.$val.';'); + and is_array($GLOBALS['CHARSET'][$cset]) + ) { + foreach ($GLOBALS['CHARSET'][$cset] as $key => $val) { + $trans[$charset][chr($key)] = unicode2charset('&#' . $val . ';'); + } } } - if (count($trans[$charset])) - return str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte); + if (count($trans[$charset])) { + return str_replace(array_keys($trans[$charset]), array_values($trans[$charset]), $texte); + } + return $texte; } + return unicode2charset(charset2unicode($texte, $charset)); } /** - * Transforme un texte UTF-8 en unicode + * Transforme un texte UTF-8 en unicode * * Utilise la librairie mb si présente - * + * * @param string $source * Texte UTF-8 à transformer * @return string * Texte transformé en unicode -**/ + **/ function utf_8_to_unicode($source) { // mb_string : methode rapide if (init_mb_string()) { $convmap = array(0x7F, 0xFFFFFF, 0x0, 0xFFFFFF); + return mb_encode_numericentity($source, $convmap, 'UTF-8'); } @@ -542,79 +596,82 @@ function utf_8_to_unicode($source) { } $pos = 0; - $len = strlen ($source); + $len = strlen($source); $encodedString = ''; while ($pos < $len) { $char = ''; $ischar = false; - $asciiPos = ord (substr ($source, $pos, 1)); + $asciiPos = ord(substr($source, $pos, 1)); if (($asciiPos >= 240) && ($asciiPos <= 255)) { // 4 chars representing one unicode character - $thisLetter = substr ($source, $pos, 4); + $thisLetter = substr($source, $pos, 4); $pos += 4; - } - else if (($asciiPos >= 224) && ($asciiPos <= 239)) { - // 3 chars representing one unicode character - $thisLetter = substr ($source, $pos, 3); - $pos += 3; - } - else if (($asciiPos >= 192) && ($asciiPos <= 223)) { - // 2 chars representing one unicode character - $thisLetter = substr ($source, $pos, 2); - $pos += 2; - } - else { - // 1 char (lower ascii) - $thisLetter = substr ($source, $pos, 1); - $pos += 1; - $char = $thisLetter; - $ischar = true; + } else { + if (($asciiPos >= 224) && ($asciiPos <= 239)) { + // 3 chars representing one unicode character + $thisLetter = substr($source, $pos, 3); + $pos += 3; + } else { + if (($asciiPos >= 192) && ($asciiPos <= 223)) { + // 2 chars representing one unicode character + $thisLetter = substr($source, $pos, 2); + $pos += 2; + } else { + // 1 char (lower ascii) + $thisLetter = substr($source, $pos, 1); + $pos += 1; + $char = $thisLetter; + $ischar = true; + } + } } - if ($ischar) + if ($ischar) { $encodedString .= $char; - else { // process the string representing the letter to a unicode entity - $thisLen = strlen ($thisLetter); + } else { // process the string representing the letter to a unicode entity + $thisLen = strlen($thisLetter); $thisPos = 0; $decimalCode = 0; while ($thisPos < $thisLen) { - $thisCharOrd = ord (substr ($thisLetter, $thisPos, 1)); + $thisCharOrd = ord(substr($thisLetter, $thisPos, 1)); if ($thisPos == 0) { - $charNum = intval ($thisCharOrd - $decrement[$thisLen]); + $charNum = intval($thisCharOrd - $decrement[$thisLen]); $decimalCode += ($charNum << $shift[$thisLen][$thisPos]); } else { - $charNum = intval ($thisCharOrd - 128); + $charNum = intval($thisCharOrd - 128); $decimalCode += ($charNum << $shift[$thisLen][$thisPos]); } $thisPos++; } - $encodedLetter = "&#". preg_replace('/^0+/', '', $decimalCode) . ';'; + $encodedLetter = "&#" . preg_replace('/^0+/', '', $decimalCode) . ';'; $encodedString .= $encodedLetter; } } + return $encodedString; } /** - * Transforme un texte UTF-32 en unicode + * Transforme un texte UTF-32 en unicode * * UTF-32 ne sert plus que si on passe par iconv, c'est-a-dire quand * mb_string est absente ou ne connait pas notre charset. * * Mais on l'optimise quand meme par mb_string * => tout ca sera osolete quand on sera surs d'avoir mb_string - * + * * @param string $source * Texte UTF-8 à transformer * @return string * Texte transformé en unicode -**/ + **/ function utf_32_to_unicode($source) { // mb_string : methode rapide if (init_mb_string()) { $convmap = array(0x7F, 0xFFFFFF, 0x0, 0xFFFFFF); $source = mb_encode_numericentity($source, $convmap, 'UTF-32LE'); + return str_replace(chr(0), '', $source); } @@ -624,13 +681,17 @@ function utf_32_to_unicode($source) { $words = unpack("V*", substr($source, 0, 1024)); $source = substr($source, 1024); foreach ($words as $word) { - if ($word < 128) + if ($word < 128) { $texte .= chr($word); - // ignorer le BOM - http://www.unicode.org/faq/utf_bom.html - else if ($word != 65279) - $texte .= '&#'.$word.';'; + } // ignorer le BOM - http://www.unicode.org/faq/utf_bom.html + else { + if ($word != 65279) { + $texte .= '&#' . $word . ';'; + } + } } } + return $texte; } @@ -638,134 +699,179 @@ function utf_32_to_unicode($source) { /** * Transforme un numéro unicode en caractère utf-8 - * + * * Ce bloc provient de php.net + * * @author Ronen - * + * * @param int $num * Numéro de l'entité unicode * @return char * Caractère utf8 si trouvé, '' sinon -**/ + **/ function caractere_utf_8($num) { - if($num<128) + $num = intval($num); + if ($num < 128) { return chr($num); - if($num<2048) - return chr(($num>>6)+192).chr(($num&63)+128); - if($num<65536) - return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128); - if($num<1114112) - return chr($num>>18+240).chr((($num>>12)&63)+128).chr(($num>>6)&63+128). chr($num&63+128); + } + if ($num < 2048) { + return chr(($num >> 6) + 192) . chr(($num & 63) + 128); + } + if ($num < 65536) { + return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); + } + if ($num < 1114112) { + return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); + } + return ''; } /** * Convertit un texte unicode en utf-8 - * + * * @param string $texte * Texte à convertir * @return string * Texte converti -**/ + **/ function unicode_to_utf_8($texte) { // 1. Entites € et suivantes $vu = array(); if (preg_match_all(',�*([1-9][0-9][0-9]+);,S', - $texte, $regs, PREG_SET_ORDER)) - foreach ($regs as $reg) { - if ($reg[1]>127 AND !isset($vu[$reg[0]])) - $vu[$reg[0]] = caractere_utf_8($reg[1]); + $texte, $regs, PREG_SET_ORDER)) { + foreach ($regs as $reg) { + if ($reg[1] > 127 and !isset($vu[$reg[0]])) { + $vu[$reg[0]] = caractere_utf_8($reg[1]); + } + } } //$texte = str_replace(array_keys($vu), array_values($vu), $texte); // 2. Entites > ÿ //$vu = array(); if (preg_match_all(',�*([1-9a-f][0-9a-f][0-9a-f]+);,iS', - $texte, $regs, PREG_SET_ORDER)) - foreach ($regs as $reg) { - if (!isset($vu[$reg[0]])) - $vu[$reg[0]] = caractere_utf_8(hexdec($reg[1])); + $texte, $regs, PREG_SET_ORDER)) { + foreach ($regs as $reg) { + if (!isset($vu[$reg[0]])) { + $vu[$reg[0]] = caractere_utf_8(hexdec($reg[1])); + } + } } + return str_replace(array_keys($vu), array_values($vu), $texte); } /** * Convertit les unicode Ĉ en javascript \u0108 - * + * * @param string $texte * Texte à convertir * @return string * Texte converti -**/ + **/ function unicode_to_javascript($texte) { $vu = array(); - while (preg_match(',�*([0-9]+);,S', $texte, $regs) AND !isset($vu[$regs[1]])) { + while (preg_match(',�*([0-9]+);,S', $texte, $regs) and !isset($vu[$regs[1]])) { $num = $regs[1]; $vu[$num] = true; - $s = '\u'.sprintf("%04x", $num); + $s = '\u' . sprintf("%04x", $num); $texte = str_replace($regs[0], $s, $texte); } + return $texte; } /** * Convertit les %uxxxx (envoyés par javascript) en &#yyy unicode - * + * * @param string $texte * Texte à convertir * @return string * Texte converti -**/ -function javascript_to_unicode ($texte) { - while (preg_match(",%u([0-9A-F][0-9A-F][0-9A-F][0-9A-F]),", $texte, $regs)) - $texte = str_replace($regs[0],"&#".hexdec($regs[1]).";", $texte); + **/ +function javascript_to_unicode($texte) { + while (preg_match(",%u([0-9A-F][0-9A-F][0-9A-F][0-9A-F]),", $texte, $regs)) { + $texte = str_replace($regs[0], "&#" . hexdec($regs[1]) . ";", $texte); + } + return $texte; } /** * Convertit les %E9 (envoyés par le browser) en chaîne du charset du site (binaire) - * + * * @param string $texte * Texte à convertir * @return string * Texte converti -**/ -function javascript_to_binary ($texte) { - while (preg_match(",%([0-9A-F][0-9A-F]),", $texte, $regs)) - $texte = str_replace($regs[0],chr(hexdec($regs[1])), $texte); + **/ +function javascript_to_binary($texte) { + while (preg_match(",%([0-9A-F][0-9A-F]),", $texte, $regs)) { + $texte = str_replace($regs[0], chr(hexdec($regs[1])), $texte); + } + return $texte; } -// http://doc.spip.org/@translitteration_rapide -function translitteration_rapide($texte, $charset='AUTO', $complexe='') { +/** + * Substition rapide de chaque graphème selon le charset sélectionné. + * + * @uses caractere_utf_8() + * + * @global array $CHARSET + * @staticvar array $trans + * + * @param string $texte + * @param string $charset + * @param string $complexe + * @return string + */ +function translitteration_rapide($texte, $charset = 'AUTO', $complexe = '') { static $trans; - if ($charset == 'AUTO') + if ($charset == 'AUTO') { $charset = $GLOBALS['meta']['charset']; - if (!strlen($texte)) + } + if (!strlen($texte)) { return $texte; + } - $table_translit ='translit'.$complexe; + $table_translit = 'translit' . $complexe; // 2. Translitterer grace a la table predefinie if (!$trans[$complexe]) { - global $CHARSET; load_charset($table_translit); - foreach ($CHARSET[$table_translit] as $key => $val) + foreach ($GLOBALS['CHARSET'][$table_translit] as $key => $val) { $trans[$complexe][caractere_utf_8($key)] = $val; + } } - return str_replace(array_keys($trans[$complexe]),array_values($trans[$complexe]),$texte); + return str_replace(array_keys($trans[$complexe]), array_values($trans[$complexe]), $texte); } -// -// Translitteration charset => ascii (pour l'indexation) -// Attention les caracteres non reconnus sont renvoyes en utf-8 -// -// http://doc.spip.org/@translitteration -function translitteration($texte, $charset='AUTO', $complexe='') { +/** + * Translittération charset => ascii (pour l'indexation) + * + * Permet, entre autres, d’enlever les accents, + * car la table ASCII non étendue ne les comporte pas. + * + * Attention les caractères non reconnus sont renvoyés en utf-8 + * + * @uses corriger_caracteres() + * @uses unicode_to_utf_8() + * @uses html2unicode() + * @uses charset2unicode() + * @uses translitteration_rapide() + * + * @param string $texte + * @param string $charset + * @param string $complexe + * @return string + */ +function translitteration($texte, $charset = 'AUTO', $complexe = '') { // 0. Supprimer les caracteres illegaux include_spip('inc/filtres'); $texte = corriger_caracteres($texte); @@ -773,23 +879,39 @@ function translitteration($texte, $charset='AUTO', $complexe='') { // 1. Passer le charset et les é en utf-8 $texte = unicode_to_utf_8(html2unicode(charset2unicode($texte, $charset, true))); - return translitteration_rapide($texte,$charset,$complexe); + return translitteration_rapide($texte, $charset, $complexe); } -// à est retourne sous la forme "a`" et pas "a" -// mais si $chiffre=true, on retourne "a8" (vietnamien) -// http://doc.spip.org/@translitteration_complexe -function translitteration_complexe($texte, $chiffres=false) { - $texte = translitteration($texte,'AUTO','complexe'); +/** + * Translittération complexe + * + * `à` est retourné sous la forme ``a` `` et pas `à` + * mais si `$chiffre=true`, on retourne `a8` (vietnamien) + * + * @uses translitteration() + * @param string $texte + * @param bool $chiffres + * @return string + */ +function translitteration_complexe($texte, $chiffres = false) { + $texte = translitteration($texte, 'AUTO', 'complexe'); if ($chiffres) { $texte = preg_replace("/[aeiuoyd]['`?~.^+(-]{1,2}/eS", "translitteration_chiffree('\\0')", $texte); } - + return $texte; } -// http://doc.spip.org/@translitteration_chiffree + +/** + * Translittération chiffrée + * + * Remplace des caractères dans une chaîne par des chiffres + * + * @param string $car + * @return string + */ function translitteration_chiffree($car) { return strtr($car, "'`?~.^+(-", "123456789"); } @@ -802,58 +924,69 @@ function translitteration_chiffree($car) { * Texte dont on vérifie la présence du BOM * @return bool * true s'il a un BOM -**/ + **/ function bom_utf8($texte) { - return (substr($texte, 0,3) == chr(0xEF).chr(0xBB).chr(0xBF)); + return (substr($texte, 0, 3) == chr(0xEF) . chr(0xBB) . chr(0xBF)); } /** * Vérifie qu'une chaîne est en utf-8 valide - * + * * Note: preg_replace permet de contourner un "stack overflow" sur PCRE - * + * * @link http://us2.php.net/manual/fr/function.mb-detect-encoding.php#50087 * @link http://w3.org/International/questions/qa-forms-utf-8.html - * + * * @param string $string * Texte dont on vérifie qu'il est de l'utf-8 * @return bool * true si c'est le cas -**/ + **/ function is_utf8($string) { return !strlen( - preg_replace( - ',[\x09\x0A\x0D\x20-\x7E]' # ASCII - . '|[\xC2-\xDF][\x80-\xBF]' # non-overlong 2-byte - . '|\xE0[\xA0-\xBF][\x80-\xBF]' # excluding overlongs - . '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}' # straight 3-byte - . '|\xED[\x80-\x9F][\x80-\xBF]' # excluding surrogates - . '|\xF0[\x90-\xBF][\x80-\xBF]{2}' # planes 1-3 - . '|[\xF1-\xF3][\x80-\xBF]{3}' # planes 4-15 - . '|\xF4[\x80-\x8F][\x80-\xBF]{2}' # plane 16 - . ',sS', - '', $string)); + preg_replace( + ',[\x09\x0A\x0D\x20-\x7E]' # ASCII + . '|[\xC2-\xDF][\x80-\xBF]' # non-overlong 2-byte + . '|\xE0[\xA0-\xBF][\x80-\xBF]' # excluding overlongs + . '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}' # straight 3-byte + . '|\xED[\x80-\x9F][\x80-\xBF]' # excluding surrogates + . '|\xF0[\x90-\xBF][\x80-\xBF]{2}' # planes 1-3 + . '|[\xF1-\xF3][\x80-\xBF]{3}' # planes 4-15 + . '|\xF4[\x80-\x8F][\x80-\xBF]{2}' # plane 16 + . ',sS', + '', $string)); } /** * Vérifie qu'une chaîne est en ascii valide - * + * * @param string $string * Texte dont on vérifie qu'il est de l'ascii * @return bool * true si c'est le cas -**/ + **/ function is_ascii($string) { return !strlen( - preg_replace( - ',[\x09\x0A\x0D\x20-\x7E],sS', - '', $string)); + preg_replace( + ',[\x09\x0A\x0D\x20-\x7E],sS', + '', $string)); } -// Transcode une page (attrapee sur le web, ou un squelette) en essayant -// par tous les moyens de deviner son charset (y compris headers HTTP) -// http://doc.spip.org/@transcoder_page -function transcoder_page($texte, $headers='') { +/** + * Transcode une page vers le charset du site + * + * Transcode une page (attrapée sur le web, ou un squelette) vers le + * charset du site en essayant par tous les moyens de deviner son charset + * (y compris dans les headers HTTP) + * + * @param string $texte + * Page à transcoder, dont on souhaite découvrir son charset + * @param string $headers + * Éventuels headers HTTP liés à cette page + * @return string + * Texte transcodé dans le charset du site + **/ +function transcoder_page($texte, $headers = '') { // Si tout est < 128 pas la peine d'aller plus loin if (is_ascii($texte)) { @@ -864,36 +997,45 @@ function transcoder_page($texte, $headers='') { // Reconnaitre le BOM utf-8 (0xEFBBBF) if (bom_utf8($texte)) { $charset = 'utf-8'; - $texte = substr($texte,3); + $texte = substr($texte, 3); + } // charset precise par le contenu (xml) + else { + if (preg_match( + ',<[?]xml[^>]*encoding[^>]*=[^>]*([-_a-z0-9]+?),UimsS', $texte, $regs)) { + $charset = trim(strtolower($regs[1])); + } // charset precise par le contenu (html) + else { + if (preg_match( + ',<(meta|html|body)[^>]*charset[^>]*=[^>]*([-_a-z0-9]+?),UimsS', + $texte, $regs) + # eviter #CHARSET des squelettes + and (($tmp = trim(strtolower($regs[2]))) != 'charset') + ) { + $charset = $tmp; + } // charset de la reponse http + else { + if (preg_match(',charset=([-_a-z0-9]+),i', $headers, $regs)) { + $charset = trim(strtolower($regs[1])); + } else { + $charset = ''; + } + } + } } - - // charset precise par le contenu (xml) - else if (preg_match( - ',<[?]xml[^>]*encoding[^>]*=[^>]*([-_a-z0-9]+?),UimsS', $texte, $regs)) - $charset = trim(strtolower($regs[1])); - // charset precise par le contenu (html) - else if (preg_match( - ',<(meta|html|body)[^>]*charset[^>]*=[^>]*([-_a-z0-9]+?),UimsS', - $texte, $regs) - # eviter #CHARSET des squelettes - AND (($tmp = trim(strtolower($regs[2]))) != 'charset')) - $charset = $tmp; - // charset de la reponse http - else if (preg_match(',charset=([-_a-z0-9]+),i', $headers, $regs)) - $charset = trim(strtolower($regs[1])); - else $charset = ''; // normaliser les noms du shif-jis japonais - if (preg_match(',^(x|shift)[_-]s?jis$,i', $charset)) + if (preg_match(',^(x|shift)[_-]s?jis$,i', $charset)) { $charset = 'shift-jis'; + } if ($charset) { spip_log("charset: $charset"); } else { // valeur par defaut - if (is_utf8($texte)) + if (is_utf8($texte)) { $charset = 'utf-8'; - else + } else { $charset = 'iso-8859-1'; + } spip_log("charset probable: $charset"); } @@ -912,28 +1054,31 @@ function transcoder_page($texte, $headers='') { * * @link http://fr.php.net/manual/fr/function.mb-substr.php * @link http://www.php.net/manual/fr/function.substr.php - * - * @param string $c Le texte - * @param int $start Début - * @param null|int $length Longueur ou fin + * @uses spip_substr_manuelle() si les fonctions php mb sont absentes + * + * @param string $c Le texte + * @param int $start Début + * @param null|int $length Longueur ou fin * @return string * Le texte coupé -**/ -function spip_substr($c, $start=0, $length = NULL) { + **/ +function spip_substr($c, $start = 0, $length = null) { // Si ce n'est pas utf-8, utiliser substr if ($GLOBALS['meta']['charset'] != 'utf-8') { - if ($length) + if ($length) { return substr($c, $start, $length); - else + } else { substr($c, $start); + } } // Si utf-8, voir si on dispose de mb_string if (init_mb_string()) { - if ($length) + if ($length) { return mb_substr($c, $start, $length); - else + } else { return mb_substr($c, $start); + } } // Version manuelle (cf. ci-dessous) @@ -947,71 +1092,96 @@ function spip_substr($c, $start=0, $length = NULL) { * Version manuelle de substr utf8, pour php vieux et/ou mal installe * * @link http://fr.php.net/manual/fr/function.mb-substr.php - * @used-by spip_substr() - * - * @param string $c Le texte - * @param int $start Début - * @param null|int $length Longueur ou fin + * + * @param string $c Le texte + * @param int $start Début + * @param null|int $length Longueur ou fin * @return string * Le texte coupé -**/ -function spip_substr_manuelle($c, $start, $length = NULL) { + **/ +function spip_substr_manuelle($c, $start, $length = null) { // Cas pathologique - if ($length === 0) + if ($length === 0) { return ''; + } // S'il y a un demarrage, on se positionne - if ($start > 0) + if ($start > 0) { $c = substr($c, strlen(spip_substr_manuelle($c, 0, $start))); - elseif ($start < 0) - return spip_substr_manuelle($c, spip_strlen($c)+$start, $length); + } elseif ($start < 0) { + return spip_substr_manuelle($c, spip_strlen($c) + $start, $length); + } - if (!$length) + if (!$length) { return $c; + } if ($length > 0) { // on prend n fois la longueur desiree, pour etre surs d'avoir tout // (un caractere utf-8 prenant au maximum n bytes) - $n = 0; while (preg_match(',[\x80-\xBF]{'.(++$n).'},', $c)); - $c = substr($c, 0, $n*$length); + $n = 0; + while (preg_match(',[\x80-\xBF]{' . (++$n) . '},', $c)) { + ; + } + $c = substr($c, 0, $n * $length); // puis, tant qu'on est trop long, on coupe... - while (($l = spip_strlen($c)) > $length) + while (($l = spip_strlen($c)) > $length) { $c = substr($c, 0, $length - $l); + } + return $c; } // $length < 0 - return spip_substr_manuelle($c, 0, spip_strlen($c)+$length); + return spip_substr_manuelle($c, 0, spip_strlen($c) + $length); } /** * Rend majuscule le premier caractère d'une chaîne utf-8 - * + * * Version utf-8 d'ucfirst - * + * * @param string $c * La chaîne à transformer * @return string - * La chaîne avec une majuscule sur le premier mot + * La chaîne avec une majuscule sur le premier mot */ -function spip_ucfirst($c){ - // Si ce n'est pas utf-8, utiliser ucfirst - if ($GLOBALS['meta']['charset'] != 'utf-8') +function spip_ucfirst($c) { + // Si on n'a pas mb_* ou si ce n'est pas utf-8, utiliser ucfirst + if (!init_mb_string() or $GLOBALS['meta']['charset'] != 'utf-8') { return ucfirst($c); - // Si on n'a pas mb_* on utilise ucfirst - if (!init_mb_string()) - return ucfirst($c); - - $lettre1 = mb_strtoupper(spip_substr($c,0,1)); - return $lettre1.spip_substr($c,1); + } + + $lettre1 = mb_strtoupper(spip_substr($c, 0, 1)); + + return $lettre1 . spip_substr($c, 1); +} + +/** + * Passe une chaîne utf-8 en minuscules + * + * Version utf-8 de strtolower + * + * @param string $c + * La chaîne à transformer + * @return string + * La chaîne en minuscules + */ +function spip_strtolower($c) { + // Si on n'a pas mb_* ou si ce n'est pas utf-8, utiliser strtolower + if (!init_mb_string() or $GLOBALS['meta']['charset'] != 'utf-8') { + return strtolower($c); + } + + return mb_strtolower($c); } /** * Retourne la longueur d'une chaîne utf-8 - * + * * Version utf-8 de strlen - * + * * @param string $c * La chaîne à compter * @return int @@ -1020,14 +1190,16 @@ function spip_ucfirst($c){ function spip_strlen($c) { // On transforme les sauts de ligne pour ne pas compter deux caractères $c = str_replace("\r\n", "\n", $c); - + // Si ce n'est pas utf-8, utiliser strlen - if ($GLOBALS['meta']['charset'] != 'utf-8') + if ($GLOBALS['meta']['charset'] != 'utf-8') { return strlen($c); + } // Sinon, utiliser mb_strlen() si disponible - if (init_mb_string()) + if (init_mb_string()) { return mb_strlen($c); + } // Methode manuelle : on supprime les bytes 10......, // on compte donc les ascii (0.......) et les demarrages @@ -1036,19 +1208,46 @@ function spip_strlen($c) { } // Initialisation -$GLOBALS['CHARSET'] = Array(); +$GLOBALS['CHARSET'] = array(); // noter a l'occasion dans la meta pcre_u notre capacite a utiliser le flag /u // dans les preg_replace pour ne pas casser certaines lettres accentuees : // en utf-8 chr(195).chr(160) = a` alors qu'en iso-latin chr(160) = nbsp if (!isset($GLOBALS['meta']['pcre_u']) - OR (isset($_GET['var_mode']) AND !isset($_GET['var_profile']))) { + or (isset($_GET['var_mode']) and !isset($_GET['var_profile'])) +) { include_spip('inc/meta'); ecrire_meta('pcre_u', - $u = ($GLOBALS['meta']['charset'] == 'utf-8' - AND test_pcre_unicode()) - ? 'u' :'' + $u = (lire_config('charset', _DEFAULT_CHARSET) == 'utf-8' + and test_pcre_unicode()) + ? 'u' : '' ); } -?> + +/** + * Transforme une chaîne utf-8 en utf-8 sans "planes" + * ce qui permet de la donner à MySQL "utf8", qui n'est pas un utf-8 complet + * L'alternative serait d'utiliser utf8mb4 + * + * @param string $x + * La chaîne à transformer + * @return string + * La chaîne avec les caractères utf8 des hauts "planes" échappée + * en unicode : 💩 + */ +function utf8_noplanes($x) { + $regexp_utf8_4bytes = '/( + \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +)/xS'; + if (preg_match_all($regexp_utf8_4bytes, $x, $z, PREG_PATTERN_ORDER)) { + foreach ($z[0] as $k) { + $ku = utf_8_to_unicode($k); + $x = str_replace($k, $ku, $x); + } + } + + return $x; +}