PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/spip/ecrire/inc/charsets.php

https://github.com/eyeswebcrea/espace-couture-sittler.fr
PHP | 786 lines | 527 code | 100 blank | 159 comment | 147 complexity | 4bfb76e393ea4df7b1d4bef86d14b758 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-3.0
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2011 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. //
  12. if (!defined('_ECRIRE_INC_VERSION')) return;
  13. /*
  14. * charsets supportes en natif : voir les tables dans ecrire/charsets/
  15. * les autres charsets sont supportes via mbstring()
  16. */
  17. // http://doc.spip.org/@load_charset
  18. function load_charset ($charset = 'AUTO', $langue_site = 'AUTO') {
  19. if ($charset == 'AUTO')
  20. $charset = $GLOBALS['meta']['charset'];
  21. $charset = trim(strtolower($charset));
  22. if (isset($GLOBALS['CHARSET'][$charset]))
  23. return $charset;
  24. if ($langue_site == 'AUTO')
  25. $langue_site = $GLOBALS['meta']['langue_site'];
  26. if ($charset == 'utf-8') {
  27. $GLOBALS['CHARSET'][$charset] = array();
  28. return $charset;
  29. }
  30. // Quelques synonymes
  31. if ($charset == '') $charset = 'iso-8859-1';
  32. else if ($charset == 'windows-1250') $charset = 'cp1250';
  33. else if ($charset == 'windows-1251') $charset = 'cp1251';
  34. else if ($charset == 'windows-1256') $charset = 'cp1256';
  35. if (find_in_path($charset . '.php', 'charsets/', true)) {
  36. return $charset;
  37. } else {
  38. spip_log("Erreur: pas de fichier de conversion 'charsets/$charset'");
  39. $GLOBALS['CHARSET'][$charset] = array();
  40. return false;
  41. }
  42. }
  43. //
  44. // Verifier qu'on peut utiliser mb_string
  45. //
  46. // http://doc.spip.org/@init_mb_string
  47. function init_mb_string() {
  48. static $mb;
  49. // verifier que tout est present (fonctions mb_string pour php >= 4.0.6)
  50. // et que le charset interne est connu de mb_string
  51. if (!$mb) {
  52. if (function_exists('mb_internal_encoding')
  53. AND function_exists('mb_detect_order')
  54. AND function_exists('mb_substr')
  55. AND function_exists('mb_strlen')
  56. AND function_exists('mb_encode_mimeheader')
  57. AND function_exists('mb_encode_numericentity')
  58. AND function_exists('mb_decode_numericentity')
  59. AND mb_detect_order($GLOBALS['meta']['charset'])
  60. ) {
  61. mb_internal_encoding('utf-8');
  62. $mb = 1;
  63. } else
  64. $mb = -1;
  65. }
  66. return ($mb == 1);
  67. }
  68. // Detecter les versions buggees d'iconv
  69. // http://doc.spip.org/@test_iconv
  70. function test_iconv() {
  71. static $iconv_ok;
  72. if (!$iconv_ok) {
  73. if (!function_exists('iconv'))
  74. $iconv_ok = -1;
  75. else {
  76. if (utf_32_to_unicode(@iconv('utf-8', 'utf-32', 'chaine de test')) == 'chaine de test')
  77. $iconv_ok = 1;
  78. else
  79. $iconv_ok = -1;
  80. }
  81. }
  82. return ($iconv_ok == 1);
  83. }
  84. // Test de fonctionnement du support UTF-8 dans PCRE
  85. // (contournement bug Debian Woody)
  86. // http://doc.spip.org/@test_pcre_unicode
  87. function test_pcre_unicode() {
  88. static $pcre_ok = 0;
  89. if (!$pcre_ok) {
  90. $s = " ".chr(195).chr(169)."t".chr(195).chr(169)." ";
  91. if (preg_match(',\W...\W,u', $s)) $pcre_ok = 1;
  92. else $pcre_ok = -1;
  93. }
  94. return $pcre_ok == 1;
  95. }
  96. // Plages alphanumeriques (incomplet...)
  97. // http://doc.spip.org/@pcre_lettres_unicode
  98. function pcre_lettres_unicode() {
  99. static $plage_unicode;
  100. if (!$plage_unicode) {
  101. if (test_pcre_unicode()) {
  102. // cf. http://www.unicode.org/charts/
  103. $plage_unicode = '\w' // iso-latin
  104. . '\x{100}-\x{24f}' // europeen etendu
  105. . '\x{300}-\x{1cff}' // des tas de trucs
  106. ;
  107. }
  108. else {
  109. // fallback a trois sous
  110. $plage_unicode = '\w';
  111. }
  112. }
  113. return $plage_unicode;
  114. }
  115. // Plage ponctuation de 0x2000 a 0x206F
  116. // (i.e. de 226-128-128 a 226-129-176)
  117. // http://doc.spip.org/@plage_punct_unicode
  118. function plage_punct_unicode() {
  119. return '\xE2(\x80[\x80-\xBF]|\x81[\x80-\xAF])';
  120. }
  121. // corriger caracteres non-conformes : 128-159
  122. // cf. charsets/iso-8859-1.php (qu'on recopie ici pour aller plus vite)
  123. // on peut passer un charset cible en parametre pour accelerer le passage iso-8859-1 -> autre charset
  124. // http://doc.spip.org/@corriger_caracteres_windows
  125. function corriger_caracteres_windows($texte, $charset='AUTO', $charset_cible='unicode') {
  126. static $trans;
  127. if (is_array($texte)) {
  128. return array_map('corriger_caracteres_windows', $texte);
  129. }
  130. if ($charset=='AUTO') $charset = $GLOBALS['meta']['charset'];
  131. if ($charset == 'utf-8') {
  132. $p = chr(194);
  133. if (strpos($texte,$p)===false) return $texte;
  134. } else if ($charset == 'iso-8859-1') {
  135. $p = '';
  136. } else
  137. return $texte;
  138. if (!isset($trans[$charset][$charset_cible])) {
  139. $trans[$charset][$charset_cible] = array(
  140. $p.chr(128) => "&#8364;",
  141. $p.chr(129) => ' ', # pas affecte
  142. $p.chr(130) => "&#8218;",
  143. $p.chr(131) => "&#402;",
  144. $p.chr(132) => "&#8222;",
  145. $p.chr(133) => "&#8230;",
  146. $p.chr(134) => "&#8224;",
  147. $p.chr(135) => "&#8225;",
  148. $p.chr(136) => "&#710;",
  149. $p.chr(137) => "&#8240;",
  150. $p.chr(138) => "&#352;",
  151. $p.chr(139) => "&#8249;",
  152. $p.chr(140) => "&#338;",
  153. $p.chr(141) => ' ', # pas affecte
  154. $p.chr(142) => "&#381;",
  155. $p.chr(143) => ' ', # pas affecte
  156. $p.chr(144) => ' ', # pas affecte
  157. $p.chr(145) => "&#8216;",
  158. $p.chr(146) => "&#8217;",
  159. $p.chr(147) => "&#8220;",
  160. $p.chr(148) => "&#8221;",
  161. $p.chr(149) => "&#8226;",
  162. $p.chr(150) => "&#8211;",
  163. $p.chr(151) => "&#8212;",
  164. $p.chr(152) => "&#732;",
  165. $p.chr(153) => "&#8482;",
  166. $p.chr(154) => "&#353;",
  167. $p.chr(155) => "&#8250;",
  168. $p.chr(156) => "&#339;",
  169. $p.chr(157) => ' ', # pas affecte
  170. $p.chr(158) => "&#382;",
  171. $p.chr(159) => "&#376;",
  172. );
  173. if ($charset_cible!='unicode'){
  174. foreach($trans[$charset][$charset_cible] as $k=>$c)
  175. $trans[$charset][$charset_cible][$k] = unicode2charset($c, $charset_cible);
  176. }
  177. }
  178. return @str_replace(array_keys($trans[$charset][$charset_cible]),
  179. array_values($trans[$charset][$charset_cible]),$texte);
  180. }
  181. //
  182. // Transformer les &eacute; en &#123;
  183. // $secure = true pour *ne pas convertir* les caracteres malins &lt; &amp; etc.
  184. //
  185. // http://doc.spip.org/@html2unicode
  186. function html2unicode($texte, $secure=false) {
  187. if (strpos($texte,'&') === false) return $texte;
  188. static $trans = array();
  189. if (!$trans) {
  190. global $CHARSET;
  191. load_charset('html');
  192. foreach ($CHARSET['html'] as $key => $val) {
  193. $trans["&$key;"] = $val;
  194. }
  195. }
  196. if ($secure)
  197. return str_replace(array_keys($trans),array_values($trans),$texte);
  198. else
  199. return str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'),array('&', '"', '<', '>'),
  200. str_replace(array_keys($trans),array_values($trans),$texte)
  201. );
  202. }
  203. //
  204. // Transformer les &eacute; en &#123;
  205. //
  206. // http://doc.spip.org/@mathml2unicode
  207. function mathml2unicode($texte) {
  208. static $trans;
  209. if (!$trans) {
  210. global $CHARSET;
  211. load_charset('mathml');
  212. foreach ($CHARSET['mathml'] as $key => $val)
  213. $trans["&$key;"] = $val;
  214. }
  215. return str_replace(array_keys($trans),array_values($trans),$texte);
  216. }
  217. //
  218. // Transforme une chaine en entites unicode &#129;
  219. //
  220. // Note: l'argument $forcer est obsolete : il visait a ne pas
  221. // convertir les accents iso-8859-1
  222. // http://doc.spip.org/@charset2unicode
  223. function charset2unicode($texte, $charset='AUTO' /* $forcer: obsolete*/) {
  224. static $trans;
  225. if ($charset == 'AUTO')
  226. $charset = $GLOBALS['meta']['charset'];
  227. if ($charset == '') $charset = 'iso-8859-1';
  228. $charset = strtolower($charset);
  229. switch ($charset) {
  230. case 'utf-8':
  231. case 'utf8':
  232. return utf_8_to_unicode($texte);
  233. case 'iso-8859-1':
  234. $texte = corriger_caracteres_windows($texte, 'iso-8859-1');
  235. // pas de break; ici, on suit sur default:
  236. default:
  237. // mbstring presente ?
  238. if (init_mb_string()) {
  239. if ($order = mb_detect_order() # mb_string connait-il $charset?
  240. AND mb_detect_order($charset)) {
  241. $s = mb_convert_encoding($texte, 'utf-8', $charset);
  242. if ($s && $s != $texte) return utf_8_to_unicode($s);
  243. }
  244. mb_detect_order($order); # remettre comme precedemment
  245. }
  246. // Sinon, peut-etre connaissons-nous ce charset ?
  247. if (!isset($trans[$charset])) {
  248. global $CHARSET;
  249. if ($cset = load_charset($charset)
  250. AND is_array($CHARSET[$cset]))
  251. foreach ($CHARSET[$cset] as $key => $val) {
  252. $trans[$charset][chr($key)] = '&#'.$val.';';
  253. }
  254. }
  255. if (count($trans[$charset]))
  256. return str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte);
  257. // Sinon demander a iconv (malgre le fait qu'il coupe quand un
  258. // caractere n'appartient pas au charset, mais c'est un probleme
  259. // surtout en utf-8, gere ci-dessus)
  260. if (test_iconv()) {
  261. $s = iconv($charset, 'utf-32le', $texte);
  262. if ($s) return utf_32_to_unicode($s);
  263. }
  264. // Au pire ne rien faire
  265. spip_log("erreur charset '$charset' non supporte");
  266. return $texte;
  267. }
  268. }
  269. //
  270. // Transforme les entites unicode &#129; dans le charset specifie
  271. // Attention on ne transforme pas les entites < &#128; car si elles
  272. // ont ete encodees ainsi c'est a dessein
  273. // http://doc.spip.org/@unicode2charset
  274. function unicode2charset($texte, $charset='AUTO') {
  275. static $CHARSET_REVERSE;
  276. static $trans = array();
  277. if ($charset == 'AUTO')
  278. $charset = $GLOBALS['meta']['charset'];
  279. switch($charset) {
  280. case 'utf-8':
  281. return unicode_to_utf_8($texte);
  282. break;
  283. default:
  284. $charset = load_charset($charset);
  285. if (!is_array($CHARSET_REVERSE[$charset])) {
  286. $CHARSET_REVERSE[$charset] = array_flip($GLOBALS['CHARSET'][$charset]);
  287. }
  288. if (!isset($trans[$charset])){
  289. $trans[$charset]=array();
  290. $t = &$trans[$charset];
  291. for($e=128;$e<255;$e++){
  292. $h = dechex($e);
  293. if ($s = isset($CHARSET_REVERSE[$charset][$e])){
  294. $s = $CHARSET_REVERSE[$charset][$e];
  295. $t['&#'.$e.';'] = $t['&#0'.$e.';'] = $t['&#00'.$e.';'] = chr($s);
  296. $t['&#x'.$h.';'] = $t['&#x0'.$h.';'] = $t['&#x00'.$h.';'] = chr($s);
  297. }
  298. else{
  299. $t['&#'.$e.';'] = $t['&#0'.$e.';'] = $t['&#00'.$e.';'] = chr($e);
  300. $t['&#x'.$h.';'] = $t['&#x0'.$h.';'] = $t['&#x00'.$h.';'] = chr($e);
  301. }
  302. }
  303. }
  304. $texte = str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte);
  305. return $texte;
  306. }
  307. }
  308. // Importer un texte depuis un charset externe vers le charset du site
  309. // (les caracteres non resolus sont transformes en &#123;)
  310. // http://doc.spip.org/@importer_charset
  311. function importer_charset($texte, $charset = 'AUTO') {
  312. // on traite le cas le plus frequent iso-8859-1 vers utf directement pour aller plus vite !
  313. if (($charset == 'iso-8859-1') && ($GLOBALS['meta']['charset']=='utf-8') && function_exists('utf8_encode')){
  314. $texte = corriger_caracteres_windows($texte, 'iso-8859-1','unicode');
  315. $texte = utf8_encode($texte);
  316. return $texte;
  317. }
  318. return unicode2charset(charset2unicode($texte, $charset));
  319. }
  320. // UTF-8
  321. // http://doc.spip.org/@utf_8_to_unicode
  322. function utf_8_to_unicode($source) {
  323. // mb_string : methode rapide
  324. if (init_mb_string()) {
  325. $convmap = array(0x7F, 0xFFFFFF, 0x0, 0xFFFFFF);
  326. return mb_encode_numericentity($source, $convmap, 'UTF-8');
  327. }
  328. // Sinon methode pas a pas
  329. static $decrement;
  330. static $shift;
  331. // Cf. php.net, par Ronen. Adapte pour compatibilite < php4
  332. if (!is_array($decrement)) {
  333. // array used to figure what number to decrement from character order value
  334. // according to number of characters used to map unicode to ascii by utf-8
  335. $decrement[4] = 240;
  336. $decrement[3] = 224;
  337. $decrement[2] = 192;
  338. $decrement[1] = 0;
  339. // the number of bits to shift each charNum by
  340. $shift[1][0] = 0;
  341. $shift[2][0] = 6;
  342. $shift[2][1] = 0;
  343. $shift[3][0] = 12;
  344. $shift[3][1] = 6;
  345. $shift[3][2] = 0;
  346. $shift[4][0] = 18;
  347. $shift[4][1] = 12;
  348. $shift[4][2] = 6;
  349. $shift[4][3] = 0;
  350. }
  351. $pos = 0;
  352. $len = strlen ($source);
  353. $encodedString = '';
  354. while ($pos < $len) {
  355. $char = '';
  356. $ischar = false;
  357. $asciiPos = ord (substr ($source, $pos, 1));
  358. if (($asciiPos >= 240) && ($asciiPos <= 255)) {
  359. // 4 chars representing one unicode character
  360. $thisLetter = substr ($source, $pos, 4);
  361. $pos += 4;
  362. }
  363. else if (($asciiPos >= 224) && ($asciiPos <= 239)) {
  364. // 3 chars representing one unicode character
  365. $thisLetter = substr ($source, $pos, 3);
  366. $pos += 3;
  367. }
  368. else if (($asciiPos >= 192) && ($asciiPos <= 223)) {
  369. // 2 chars representing one unicode character
  370. $thisLetter = substr ($source, $pos, 2);
  371. $pos += 2;
  372. }
  373. else {
  374. // 1 char (lower ascii)
  375. $thisLetter = substr ($source, $pos, 1);
  376. $pos += 1;
  377. $char = $thisLetter;
  378. $ischar = true;
  379. }
  380. if ($ischar)
  381. $encodedString .= $char;
  382. else { // process the string representing the letter to a unicode entity
  383. $thisLen = strlen ($thisLetter);
  384. $thisPos = 0;
  385. $decimalCode = 0;
  386. while ($thisPos < $thisLen) {
  387. $thisCharOrd = ord (substr ($thisLetter, $thisPos, 1));
  388. if ($thisPos == 0) {
  389. $charNum = intval ($thisCharOrd - $decrement[$thisLen]);
  390. $decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
  391. } else {
  392. $charNum = intval ($thisCharOrd - 128);
  393. $decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
  394. }
  395. $thisPos++;
  396. }
  397. $encodedLetter = "&#". preg_replace('/^0+/', '', $decimalCode) . ';';
  398. $encodedString .= $encodedLetter;
  399. }
  400. }
  401. return $encodedString;
  402. }
  403. // UTF-32 ne sert plus que si on passe par iconv, c'est-a-dire quand
  404. // mb_string est absente ou ne connait pas notre charset
  405. // mais on l'optimise quand meme par mb_string
  406. // => tout ca sera osolete quand on sera surs d'avoir mb_string
  407. // http://doc.spip.org/@utf_32_to_unicode
  408. function utf_32_to_unicode($source) {
  409. // mb_string : methode rapide
  410. if (init_mb_string()) {
  411. $convmap = array(0x7F, 0xFFFFFF, 0x0, 0xFFFFFF);
  412. $source = mb_encode_numericentity($source, $convmap, 'UTF-32LE');
  413. return str_replace(chr(0), '', $source);
  414. }
  415. // Sinon methode lente
  416. $texte = '';
  417. while ($source) {
  418. $words = unpack("V*", substr($source, 0, 1024));
  419. $source = substr($source, 1024);
  420. foreach ($words as $word) {
  421. if ($word < 128)
  422. $texte .= chr($word);
  423. // ignorer le BOM - http://www.unicode.org/faq/utf_bom.html
  424. else if ($word != 65279)
  425. $texte .= '&#'.$word.';';
  426. }
  427. }
  428. return $texte;
  429. }
  430. // Ce bloc provient de php.net, auteur Ronen
  431. // http://doc.spip.org/@caractere_utf_8
  432. function caractere_utf_8($num) {
  433. if($num<128)
  434. return chr($num);
  435. if($num<2048)
  436. return chr(($num>>6)+192).chr(($num&63)+128);
  437. if($num<65536)
  438. return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);
  439. if($num<1114112)
  440. return chr($num>>18+240).chr((($num>>12)&63)+128).chr(($num>>6)&63+128). chr($num&63+128);
  441. return '';
  442. }
  443. // http://doc.spip.org/@unicode_to_utf_8
  444. function unicode_to_utf_8($texte) {
  445. // 1. Entites &#128; et suivantes
  446. $vu = array();
  447. if (preg_match_all(',&#0*([1-9][0-9][0-9]+);,S',
  448. $texte, $regs, PREG_SET_ORDER))
  449. foreach ($regs as $reg) {
  450. if ($reg[1]>127 AND !isset($vu[$reg[0]]))
  451. $vu[$reg[0]] = caractere_utf_8($reg[1]);
  452. }
  453. //$texte = str_replace(array_keys($vu), array_values($vu), $texte);
  454. // 2. Entites > &#xFF;
  455. //$vu = array();
  456. if (preg_match_all(',&#x0*([1-9a-f][0-9a-f][0-9a-f]+);,iS',
  457. $texte, $regs, PREG_SET_ORDER))
  458. foreach ($regs as $reg) {
  459. if (!isset($vu[$reg[0]]))
  460. $vu[$reg[0]] = caractere_utf_8(hexdec($reg[1]));
  461. }
  462. return str_replace(array_keys($vu), array_values($vu), $texte);
  463. }
  464. // convertit les &#264; en \u0108
  465. // http://doc.spip.org/@unicode_to_javascript
  466. function unicode_to_javascript($texte) {
  467. $vu = array();
  468. while (preg_match(',&#0*([0-9]+);,S', $texte, $regs) AND !isset($vu[$regs[1]])) {
  469. $num = $regs[1];
  470. $vu[$num] = true;
  471. $s = '\u'.sprintf("%04x", $num);
  472. $texte = str_replace($regs[0], $s, $texte);
  473. }
  474. return $texte;
  475. }
  476. // convertit les %uxxxx (envoyes par javascript)
  477. // http://doc.spip.org/@javascript_to_unicode
  478. function javascript_to_unicode ($texte) {
  479. while (preg_match(",%u([0-9A-F][0-9A-F][0-9A-F][0-9A-F]),", $texte, $regs))
  480. $texte = str_replace($regs[0],"&#".hexdec($regs[1]).";", $texte);
  481. return $texte;
  482. }
  483. // convertit les %E9 (envoyes par le browser) en chaine du charset du site (binaire)
  484. // http://doc.spip.org/@javascript_to_binary
  485. function javascript_to_binary ($texte) {
  486. while (preg_match(",%([0-9A-F][0-9A-F]),", $texte, $regs))
  487. $texte = str_replace($regs[0],chr(hexdec($regs[1])), $texte);
  488. return $texte;
  489. }
  490. // http://doc.spip.org/@translitteration_rapide
  491. function translitteration_rapide($texte, $charset='AUTO', $complexe='') {
  492. static $trans;
  493. if ($charset == 'AUTO')
  494. $charset = $GLOBALS['meta']['charset'];
  495. if (!strlen($texte))
  496. return $texte;
  497. $table_translit ='translit'.$complexe;
  498. // 2. Translitterer grace a la table predefinie
  499. if (!$trans[$complexe]) {
  500. global $CHARSET;
  501. load_charset($table_translit);
  502. foreach ($CHARSET[$table_translit] as $key => $val)
  503. $trans[$complexe][caractere_utf_8($key)] = $val;
  504. }
  505. return str_replace(array_keys($trans[$complexe]),array_values($trans[$complexe]),$texte);
  506. }
  507. //
  508. // Translitteration charset => ascii (pour l'indexation)
  509. // Attention les caracteres non reconnus sont renvoyes en utf-8
  510. //
  511. // http://doc.spip.org/@translitteration
  512. function translitteration($texte, $charset='AUTO', $complexe='') {
  513. // 0. Supprimer les caracteres illegaux
  514. include_spip('inc/filtres');
  515. $texte = corriger_caracteres($texte);
  516. // 1. Passer le charset et les &eacute en utf-8
  517. $texte = unicode_to_utf_8(html2unicode(charset2unicode($texte, $charset, true)));
  518. return translitteration_rapide($texte,$charset,$complexe);
  519. }
  520. // &agrave; est retourne sous la forme "a`" et pas "a"
  521. // mais si $chiffre=true, on retourne "a8" (vietnamien)
  522. // http://doc.spip.org/@translitteration_complexe
  523. function translitteration_complexe($texte, $chiffres=false) {
  524. $texte = translitteration($texte,'AUTO','complexe');
  525. if ($chiffres) {
  526. $texte = preg_replace("/[aeiuoyd]['`?~.^+(-]{1,2}/eS",
  527. "translitteration_chiffree('\\0')", $texte);
  528. }
  529. return $texte;
  530. }
  531. // http://doc.spip.org/@translitteration_chiffree
  532. function translitteration_chiffree($car) {
  533. return strtr($car, "'`?~.^+(-", "123456789");
  534. }
  535. // Reconnaitre le BOM utf-8 (0xEFBBBF)
  536. // http://doc.spip.org/@bom_utf8
  537. function bom_utf8($texte) {
  538. return (substr($texte, 0,3) == chr(0xEF).chr(0xBB).chr(0xBF));
  539. }
  540. // Verifie qu'un document est en utf-8 valide
  541. // http://us2.php.net/manual/fr/function.mb-detect-encoding.php#50087
  542. // http://w3.org/International/questions/qa-forms-utf-8.html
  543. // note: preg_replace permet de contourner un "stack overflow" sur PCRE
  544. // http://doc.spip.org/@is_utf8
  545. function is_utf8($string) {
  546. return !strlen(
  547. preg_replace(
  548. ',[\x09\x0A\x0D\x20-\x7E]' # ASCII
  549. . '|[\xC2-\xDF][\x80-\xBF]' # non-overlong 2-byte
  550. . '|\xE0[\xA0-\xBF][\x80-\xBF]' # excluding overlongs
  551. . '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}' # straight 3-byte
  552. . '|\xED[\x80-\x9F][\x80-\xBF]' # excluding surrogates
  553. . '|\xF0[\x90-\xBF][\x80-\xBF]{2}' # planes 1-3
  554. . '|[\xF1-\xF3][\x80-\xBF]{3}' # planes 4-15
  555. . '|\xF4[\x80-\x8F][\x80-\xBF]{2}' # plane 16
  556. . ',sS',
  557. '', $string));
  558. }
  559. // http://doc.spip.org/@is_ascii
  560. function is_ascii($string) {
  561. return !strlen(
  562. preg_replace(
  563. ',[\x09\x0A\x0D\x20-\x7E],sS',
  564. '', $string));
  565. }
  566. // Transcode une page (attrapee sur le web, ou un squelette) en essayant
  567. // par tous les moyens de deviner son charset (y compris headers HTTP)
  568. // http://doc.spip.org/@transcoder_page
  569. function transcoder_page($texte, $headers='') {
  570. // Si tout est < 128 pas la peine d'aller plus loin
  571. if (is_ascii($texte)) {
  572. #spip_log('charset: ascii');
  573. return $texte;
  574. }
  575. // Reconnaitre le BOM utf-8 (0xEFBBBF)
  576. if (bom_utf8($texte)) {
  577. $charset = 'utf-8';
  578. $texte = substr($texte,3);
  579. }
  580. // charset precise par le contenu (xml)
  581. else if (preg_match(
  582. ',<[?]xml[^>]*encoding[^>]*=[^>]*([-_a-z0-9]+?),UimsS', $texte, $regs))
  583. $charset = trim(strtolower($regs[1]));
  584. // charset precise par le contenu (html)
  585. else if (preg_match(
  586. ',<(meta|html|body)[^>]*charset[^>]*=[^>]*([-_a-z0-9]+?),UimsS',
  587. $texte, $regs)
  588. # eviter #CHARSET des squelettes
  589. AND (($tmp = trim(strtolower($regs[2]))) != 'charset'))
  590. $charset = $tmp;
  591. // charset de la reponse http
  592. else if (preg_match(',charset=([-_a-z0-9]+),i', $headers, $regs))
  593. $charset = trim(strtolower($regs[1]));
  594. else $charset = '';
  595. // normaliser les noms du shif-jis japonais
  596. if (preg_match(',^(x|shift)[_-]s?jis$,i', $charset))
  597. $charset = 'shift-jis';
  598. if ($charset) {
  599. spip_log("charset: $charset");
  600. } else {
  601. // valeur par defaut
  602. if (is_utf8($texte))
  603. $charset = 'utf-8';
  604. else
  605. $charset = 'iso-8859-1';
  606. spip_log("charset probable: $charset");
  607. }
  608. return importer_charset($texte, $charset);
  609. }
  610. //
  611. // Gerer les outils mb_string
  612. //
  613. // http://doc.spip.org/@spip_substr
  614. function spip_substr($c, $start=0, $length = NULL) {
  615. // Si ce n'est pas utf-8, utiliser substr
  616. if ($GLOBALS['meta']['charset'] != 'utf-8') {
  617. if ($length)
  618. return substr($c, $start, $length);
  619. else
  620. substr($c, $start);
  621. }
  622. // Si utf-8, voir si on dispose de mb_string
  623. if (init_mb_string()) {
  624. if ($length)
  625. return mb_substr($c, $start, $length);
  626. else
  627. return mb_substr($c, $start);
  628. }
  629. // Version manuelle (cf. ci-dessous)
  630. return spip_substr_manuelle($c, $start, $length);
  631. }
  632. // version manuelle de substr utf8, pour php vieux et/ou mal installe
  633. // http://doc.spip.org/@spip_substr_manuelle
  634. function spip_substr_manuelle($c, $start, $length = NULL) {
  635. // Cas pathologique
  636. if ($length === 0)
  637. return '';
  638. // S'il y a un demarrage, on se positionne
  639. if ($start > 0)
  640. $c = substr($c, strlen(spip_substr_manuelle($c, 0, $start)));
  641. elseif ($start < 0)
  642. return spip_substr_manuelle($c, spip_strlen($c)+$start, $length);
  643. if (!$length)
  644. return $c;
  645. if ($length > 0) {
  646. // on prend n fois la longueur desiree, pour etre surs d'avoir tout
  647. // (un caractere utf-8 prenant au maximum n bytes)
  648. $n = 0; while (preg_match(',[\x80-\xBF]{'.(++$n).'},', $c));
  649. $c = substr($c, 0, $n*$length);
  650. // puis, tant qu'on est trop long, on coupe...
  651. while (($l = spip_strlen($c)) > $length)
  652. $c = substr($c, 0, $length - $l);
  653. return $c;
  654. }
  655. // $length < 0
  656. return spip_substr_manuelle($c, 0, spip_strlen($c)+$length);
  657. }
  658. // http://doc.spip.org/@spip_strlen
  659. function spip_strlen($c) {
  660. // Si ce n'est pas utf-8, utiliser strlen
  661. if ($GLOBALS['meta']['charset'] != 'utf-8')
  662. return strlen($c);
  663. // Sinon, utiliser mb_strlen() si disponible
  664. if (init_mb_string())
  665. return mb_strlen($c);
  666. // Methode manuelle : on supprime les bytes 10......,
  667. // on compte donc les ascii (0.......) et les demarrages
  668. // de caracteres utf-8 (11......)
  669. return strlen(preg_replace(',[\x80-\xBF],S', '', $c));
  670. }
  671. // Initialisation
  672. $GLOBALS['CHARSET'] = Array();
  673. // noter a l'occasion dans la meta pcre_u notre capacite a utiliser le flag /u
  674. // dans les preg_replace pour ne pas casser certaines lettres accentuees :
  675. // en utf-8 chr(195).chr(160) = a` alors qu'en iso-latin chr(160) = nbsp
  676. if (!isset($GLOBALS['meta']['pcre_u'])
  677. OR (isset($_GET['var_mode']) AND !isset($_GET['var_profile']))) {
  678. include_spip('inc/meta');
  679. ecrire_meta('pcre_u',
  680. $u = ($GLOBALS['meta']['charset'] == 'utf-8'
  681. AND test_pcre_unicode())
  682. ? 'u' :''
  683. );
  684. }
  685. ?>