UtfNormalGenerate.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. # Copyright (C) 2004 Brion Vibber <brion@pobox.com>
  3. # http://www.mediawiki.org/
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along
  16. # with this program; if not, write to the Free Software Foundation, Inc.,
  17. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. # http://www.gnu.org/copyleft/gpl.html
  19. /**
  20. * This script generates UniNormalData.inc from the Unicode Character Database
  21. * and supplementary files.
  22. *
  23. * @ingroup UtfNormal
  24. * @access private
  25. */
  26. /** */
  27. if( php_sapi_name() != 'cli' ) {
  28. die( "Run me from the command line please.\n" );
  29. }
  30. require_once 'UtfNormalUtil.php';
  31. $in = fopen("DerivedNormalizationProps.txt", "rt" );
  32. if( !$in ) {
  33. print "Can't open DerivedNormalizationProps.txt for reading.\n";
  34. print "If necessary, fetch this file from the internet:\n";
  35. print "http://www.unicode.org/Public/UNIDATA/CompositionExclusions.txt\n";
  36. exit(-1);
  37. }
  38. print "Initializing normalization quick check tables...\n";
  39. $checkNFC = array();
  40. while( false !== ($line = fgets( $in ) ) ) {
  41. $matches = array();
  42. if( preg_match( '/^([0-9A-F]+)(?:..([0-9A-F]+))?\s*;\s*(NFC_QC)\s*;\s*([MN])/', $line, $matches ) ) {
  43. list( $junk, $first, $last, $prop, $value ) = $matches;
  44. #print "$first $last $prop $value\n";
  45. if( !$last ) $last = $first;
  46. for( $i = hexdec( $first ); $i <= hexdec( $last ); $i++) {
  47. $char = codepointToUtf8( $i );
  48. $checkNFC[$char] = $value;
  49. }
  50. }
  51. }
  52. fclose( $in );
  53. $in = fopen("CompositionExclusions.txt", "rt" );
  54. if( !$in ) {
  55. print "Can't open CompositionExclusions.txt for reading.\n";
  56. print "If necessary, fetch this file from the internet:\n";
  57. print "http://www.unicode.org/Public/UNIDATA/CompositionExclusions.txt\n";
  58. exit(-1);
  59. }
  60. $exclude = array();
  61. while( false !== ($line = fgets( $in ) ) ) {
  62. if( preg_match( '/^([0-9A-F]+)/i', $line, $matches ) ) {
  63. $codepoint = $matches[1];
  64. $source = codepointToUtf8( hexdec( $codepoint ) );
  65. $exclude[$source] = true;
  66. }
  67. }
  68. fclose($in);
  69. $in = fopen("UnicodeData.txt", "rt" );
  70. if( !$in ) {
  71. print "Can't open UnicodeData.txt for reading.\n";
  72. print "If necessary, fetch this file from the internet:\n";
  73. print "http://www.unicode.org/Public/UNIDATA/UnicodeData.txt\n";
  74. exit(-1);
  75. }
  76. $compatibilityDecomp = array();
  77. $canonicalDecomp = array();
  78. $canonicalComp = array();
  79. $combiningClass = array();
  80. $total = 0;
  81. $compat = 0;
  82. $canon = 0;
  83. print "Reading character definitions...\n";
  84. while( false !== ($line = fgets( $in ) ) ) {
  85. $columns = split(';', $line);
  86. $codepoint = $columns[0];
  87. $name = $columns[1];
  88. $canonicalCombiningClass = $columns[3];
  89. $decompositionMapping = $columns[5];
  90. $source = codepointToUtf8( hexdec( $codepoint ) );
  91. if( $canonicalCombiningClass != 0 ) {
  92. $combiningClass[$source] = intval( $canonicalCombiningClass );
  93. }
  94. if( $decompositionMapping === '' ) continue;
  95. if( preg_match( '/^<(.+)> (.*)$/', $decompositionMapping, $matches ) ) {
  96. # Compatibility decomposition
  97. $canonical = false;
  98. $decompositionMapping = $matches[2];
  99. $compat++;
  100. } else {
  101. $canonical = true;
  102. $canon++;
  103. }
  104. $total++;
  105. $dest = hexSequenceToUtf8( $decompositionMapping );
  106. $compatibilityDecomp[$source] = $dest;
  107. if( $canonical ) {
  108. $canonicalDecomp[$source] = $dest;
  109. if( empty( $exclude[$source] ) ) {
  110. $canonicalComp[$dest] = $source;
  111. }
  112. }
  113. #print "$codepoint | $canonicalCombiningClasses | $decompositionMapping\n";
  114. }
  115. fclose( $in );
  116. print "Recursively expanding canonical mappings...\n";
  117. $changed = 42;
  118. $pass = 1;
  119. while( $changed > 0 ) {
  120. print "pass $pass\n";
  121. $changed = 0;
  122. foreach( $canonicalDecomp as $source => $dest ) {
  123. $newDest = preg_replace_callback(
  124. '/([\xc0-\xff][\x80-\xbf]+)/',
  125. 'callbackCanonical',
  126. $dest);
  127. if( $newDest === $dest ) continue;
  128. $changed++;
  129. $canonicalDecomp[$source] = $newDest;
  130. }
  131. $pass++;
  132. }
  133. print "Recursively expanding compatibility mappings...\n";
  134. $changed = 42;
  135. $pass = 1;
  136. while( $changed > 0 ) {
  137. print "pass $pass\n";
  138. $changed = 0;
  139. foreach( $compatibilityDecomp as $source => $dest ) {
  140. $newDest = preg_replace_callback(
  141. '/([\xc0-\xff][\x80-\xbf]+)/',
  142. 'callbackCompat',
  143. $dest);
  144. if( $newDest === $dest ) continue;
  145. $changed++;
  146. $compatibilityDecomp[$source] = $newDest;
  147. }
  148. $pass++;
  149. }
  150. print "$total decomposition mappings ($canon canonical, $compat compatibility)\n";
  151. $out = fopen("UtfNormalData.inc", "wt");
  152. if( $out ) {
  153. $serCombining = escapeSingleString( serialize( $combiningClass ) );
  154. $serComp = escapeSingleString( serialize( $canonicalComp ) );
  155. $serCanon = escapeSingleString( serialize( $canonicalDecomp ) );
  156. $serCheckNFC = escapeSingleString( serialize( $checkNFC ) );
  157. $outdata = "<" . "?php
  158. /**
  159. * This file was automatically generated -- do not edit!
  160. * Run UtfNormalGenerate.php to create this file again (make clean && make)
  161. */
  162. /** */
  163. global \$utfCombiningClass, \$utfCanonicalComp, \$utfCanonicalDecomp, \$utfCheckNFC;
  164. \$utfCombiningClass = unserialize( '$serCombining' );
  165. \$utfCanonicalComp = unserialize( '$serComp' );
  166. \$utfCanonicalDecomp = unserialize( '$serCanon' );
  167. \$utfCheckNFC = unserialize( '$serCheckNFC' );
  168. ?" . ">\n";
  169. fputs( $out, $outdata );
  170. fclose( $out );
  171. print "Wrote out UtfNormalData.inc\n";
  172. } else {
  173. print "Can't create file UtfNormalData.inc\n";
  174. exit(-1);
  175. }
  176. $out = fopen("UtfNormalDataK.inc", "wt");
  177. if( $out ) {
  178. $serCompat = escapeSingleString( serialize( $compatibilityDecomp ) );
  179. $outdata = "<" . "?php
  180. /**
  181. * This file was automatically generated -- do not edit!
  182. * Run UtfNormalGenerate.php to create this file again (make clean && make)
  183. */
  184. /** */
  185. global \$utfCompatibilityDecomp;
  186. \$utfCompatibilityDecomp = unserialize( '$serCompat' );
  187. ?" . ">\n";
  188. fputs( $out, $outdata );
  189. fclose( $out );
  190. print "Wrote out UtfNormalDataK.inc\n";
  191. exit(0);
  192. } else {
  193. print "Can't create file UtfNormalDataK.inc\n";
  194. exit(-1);
  195. }
  196. # ---------------
  197. function callbackCanonical( $matches ) {
  198. global $canonicalDecomp;
  199. if( isset( $canonicalDecomp[$matches[1]] ) ) {
  200. return $canonicalDecomp[$matches[1]];
  201. }
  202. return $matches[1];
  203. }
  204. function callbackCompat( $matches ) {
  205. global $compatibilityDecomp;
  206. if( isset( $compatibilityDecomp[$matches[1]] ) ) {
  207. return $compatibilityDecomp[$matches[1]];
  208. }
  209. return $matches[1];
  210. }