aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/includes')
-rw-r--r--phpBB/includes/acp/acp_captcha.php114
-rw-r--r--phpBB/includes/captcha/captcha_gd.php3434
-rw-r--r--phpBB/includes/functions.php8
-rw-r--r--phpBB/includes/functions_admin.php2
-rw-r--r--phpBB/includes/functions_compress.php54
-rw-r--r--phpBB/includes/ucp/ucp_confirm.php41
-rw-r--r--phpBB/includes/utf/utf_normalizer.php2227
-rw-r--r--phpBB/includes/utf/utf_tools.php4
8 files changed, 1219 insertions, 4665 deletions
diff --git a/phpBB/includes/acp/acp_captcha.php b/phpBB/includes/acp/acp_captcha.php
index b16b76495f..7cef658e93 100644
--- a/phpBB/includes/acp/acp_captcha.php
+++ b/phpBB/includes/acp/acp_captcha.php
@@ -21,91 +21,37 @@ class acp_captcha
$user->add_lang('acp/board');
- $config_vars = array('enable_confirm' => 'REG_ENABLE',
- 'enable_post_confirm' => 'POST_ENABLE',
- 'policy_overlap' => 'OVERLAP_ENABLE',
- 'policy_overlap_noise_pixel' => 'OVERLAP_NOISE_PIXEL',
- 'policy_overlap_noise_line' => 'OVERLAP_NOISE_LINE_ENABLE',
- 'policy_entropy' => 'ENTROPY_ENABLE',
- 'policy_entropy_noise_pixel' => 'ENTROPY_NOISE_PIXEL',
- 'policy_entropy_noise_line' => 'ENTROPY_NOISE_LINE_ENABLE',
- 'policy_shape' => 'SHAPE_ENABLE',
- 'policy_shape_noise_pixel' => 'SHAPE_NOISE_PIXEL',
- 'policy_shape_noise_line' => 'SHAPE_NOISE_LINE_ENABLE',
- 'policy_3dbitmap' => 'THREEDBITMAP_ENABLE',
- 'policy_cells' => 'CELLS_ENABLE',
- 'policy_stencil' => 'STENCIL_ENABLE',
- 'policy_composite' => 'COMPOSITE_ENABLE'
- );
-
- $policy_modules = array('policy_entropy', 'policy_3dbitmap', 'policy_overlap', 'policy_shape', 'policy_cells', 'policy_stencil', 'policy_composite');
-
- switch ($mode)
+ $config_vars = array(
+ 'enable_confirm' => 'REG_ENABLE',
+ 'enable_post_confirm' => 'POST_ENABLE',
+ 'captcha_gd' => 'CAPTCHA_GD',
+ 'captcha_gd_noise' => 'CAPTCHA_GD_NOISE',
+ );
+
+ $this->tpl_name = 'acp_captcha';
+ $this->page_title = 'ACP_VC_SETTINGS';
+ $submit = request_var('submit', '');
+ if ($submit)
{
- case 'visual':
- $this->tpl_name = 'acp_captcha';
- $this->page_title = 'ACP_VC_SETTINGS';
- $submit = request_var('submit', '');
- if ($submit)
- {
- $config_vars = array_keys($config_vars);
- foreach ($config_vars as $config_var)
- {
- set_config($config_var, request_var($config_var, ''));
- }
- trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($this->u_action));
- }
- else
- {
- $array = array();
-
- foreach ($config_vars as $config_var => $template_var)
- {
- $array[$template_var] = $config[$config_var];
- }
- $template->assign_vars($array);
-
-
- if (@extension_loaded('gd'))
- {
- $template->assign_var('GD', true);
- foreach ($policy_modules as $module_name)
- {
- $template->assign_var('U_' . strtoupper($module_name), sprintf($user->lang['CAPTCHA_EXPLAIN'], '<a href="' . append_sid("{$phpbb_root_path}adm/index.$phpEx", 'i=captcha&amp;mode=img&amp;policy=' . $module_name) . '">', '</a>'));
- }
- if (function_exists('imagettfbbox') && function_exists('imagettftext'))
- {
- $template->assign_var('TTF', true);
- }
- }
- }
- break;
-
- case 'img':
- $policy = request_var('policy', '');
-
- if (!@extension_loaded('gd'))
- {
- trigger_error($user->lang['NO_GD'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
-
- if (!($policy === 'policy_entropy' || $policy === 'policy_3dbitmap') && (!function_exists('imagettfbbox') || !function_exists('imagettftext')))
- {
- trigger_error($user->lang['NO_TTF'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
-
- if (!in_array($policy, $policy_modules))
- {
- trigger_error($user->lang['BAD_POLICY'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
-
- $user->add_lang('ucp');
-
- include($phpbb_root_path . 'includes/captcha/captcha_gd.' . $phpEx);
-
- $captcha = new captcha();
- $captcha->execute(gen_rand_string(), $policy);
- break;
+ $config_vars = array_keys($config_vars);
+ foreach ($config_vars as $config_var)
+ {
+ set_config($config_var, request_var($config_var, ''));
+ }
+ trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($this->u_action));
+ }
+ else
+ {
+ $array = array();
+
+ if (@extension_loaded('gd') && function_exists('imagettfbbox') && function_exists('imagettftext'))
+ {
+ $template->assign_var('GD', true);
+ }
+ foreach ($config_vars as $config_var => $template_var)
+ {
+ $template->assign_var($template_var, $config[$config_var]);
+ }
}
}
}
diff --git a/phpBB/includes/captcha/captcha_gd.php b/phpBB/includes/captcha/captcha_gd.php
index 34b1684f3c..e249a46c04 100644
--- a/phpBB/includes/captcha/captcha_gd.php
+++ b/phpBB/includes/captcha/captcha_gd.php
@@ -9,3429 +9,157 @@
*/
/**
-* This file is getting too large.
-*
-* Only bugfixes allowed from now on.
-* If a policy can not be fixed with the minimum amount of code it gets removed.
-*/
-
-/**
-* Main gd based captcha class
-*
-* Thanks to Robert Hetzler (Xore)
+* Based on PHP-Class hn_captcha Version 1.3, released 11-Apr-2006
+* Original Author - Horst Nogajski, horst@nogajski.de
*
* @package VC
*/
class captcha
{
- /**
- * Create the image containing $code
- */
- function execute($code, $policy)
- {
- $this->$policy(str_split($code));
- }
-
- /**
- * Send image and destroy
- */
- function send_image(&$image)
- {
- header('Content-Type: image/png');
- header('Cache-control: no-cache, no-store');
- imagepng($image);
- imagedestroy($image);
- }
-
- /**
- *
- */
- function wave_height($x, $y, $factor = 1, $tweak = 1)
- {
- return ((sin($x / (3 * $factor)) + sin($y / (3 * $factor))) * 10 * $tweak);
- }
-
- /**
- *
- */
- function grid_height($x, $y, $factor = 1, $x_grid, $y_grid)
- {
- return ( (!($x % ($x_grid * $factor)) || !($y % ($y_grid * $factor))) ? 3 : 0);
- }
-
- /**
- *
- */
- function draw_shape($type, $img, $x_min, $y_min, $x_max, $y_max, $color)
- {
- switch ($type)
- {
- case 'Square':
- imagefilledpolygon($img, array($x_min, $y_max, $x_min, $y_min, $x_max, $y_min, $x_max, $y_max), 4, $color);
- break;
-
- case 'TriangleUp':
- imagefilledpolygon($img, array($x_min, $y_max, ($x_min + $x_max) / 2, $y_min, $x_max, $y_max), 3, $color);
- break;
-
- case 'TriangleDown':
- imagefilledpolygon($img, array($x_min, $y_min, ($x_min + $x_max) / 2, $y_max, $x_max, $y_min), 3, $color);
- break;
-
- case 'Circle':
- imagefilledellipse($img, ($x_min + $x_max) / 2, ($y_min + $y_max) / 2, $x_max - $x_min, $y_max - $y_min, $color);
- break;
- }
- }
-
- /**
- *
- */
- function draw_pattern($seed, $img, $x_min, $y_min, $x_max, $y_max, $colors, $thickness = 1)
- {
- $x_size = ($x_max - $x_min) / 4;
- $y_size = ($y_max - $y_min) / 4;
- $bitmap = substr($seed, 16, 4);
- $numcolors = sizeof($colors) - 1;
- for ($y = 0; $y < 4; ++$y)
- {
- $map = hexdec(substr($bitmap, $y, 1));
- for ($x = 0; $x < 4; ++$x)
- {
- if ($map & (1 << $x))
- {
- $char = hexdec(substr($seed, ($y << 2) + $x, 1));
- if (!($char >> 2))
- {
- switch ($char & 3)
- {
- case 0:
- $shape = 'Circle';
- break;
-
- case 1:
- $shape = 'Square';
- break;
-
- case 2:
- $shape = 'TriangleUp';
- break;
-
- case 3:
- $shape = 'TriangleDown';
- break;
- }
- $this->draw_shape($shape, $img, $x_min + ($x * $x_size), $y_min + ($y * $y_size), $x_min + (($x + 1) * $x_size), $y_min + (($y + 1) * $y_size), $colors[array_rand($colors)]);
- }
- }
- }
- }
-
- $cells = array();
- for ($i = 0; $i < 6; ++$i)
- {
- $cells = hexdec(substr($seed, 20 + ($i << 1), 2));
- $x1 = $cells & 3;
- $cells = $cells >> 2;
- $y1 = $cells & 3;
- $cells = $cells >> 2;
- $x2 = $cells & 3;
- $cells = $cells >> 2;
- $y2 = $cells & 3;
- $x1_real = $x_min + (($x1 + 0.5) * $x_size);
- $y1_real = $y_min + (($y1 + 0.5) * $y_size);
- $x2_real = $x_min + (($x2 + 0.5) * $x_size);
- $y2_real = $y_min + (($y2 + 0.5) * $y_size);
- if ($thickness > 1)
- {
- imagesetthickness($img, $thickness);
- }
- imageline($img, $x1_real, $y1_real, $x2_real, $y2_real, $colors[array_rand($colors)]);
- if ($thickness > 1)
- {
- imagesetthickness($img, 1);
- }
- }
- }
-
- /**
- *
- */
- function get_char_string()
- {
- static $chars = false;
- static $charcount = 0;
- if (!$chars)
- {
- $chars = array_merge(range('A', 'Z'), range('1', '9'));
- }
- $word = '';
- for ($i = mt_rand(6, 8); $i > 0; --$i)
- {
- $word .= $chars[array_rand($chars)];
- }
- return $word;
- }
-
- /**
- * shape
- */
- function policy_shape($code)
- {
- global $config, $user;
- // Generate image
- $img_x = 800;
- $img_y = 250;
- $img = imagecreatetruecolor($img_x, $img_y);
-
- // Generate colors
- $c = new color_manager($img, array(
- 'random' => true,
- 'min_saturation' => 70,
- 'min_value' => 65,
- ));
-
- $primaries = $c->color_scheme('background', 'tetradic', false);
-
- $noise = array_shift($primaries);
- $noise = $c->mono_range($noise, 'value', 5, false);
- $primaries = $c->mono_range($primaries, 'value', 5, false);
-
- // Generate code characters
- $characters = array();
- $sizes = array();
- $bounding_boxes = array();
- $width_avail = $img_x;
- $code_num = sizeof($code);
- $char_class = $this->captcha_char('char_ttf');
- for ( $i = 0; $i < $code_num; ++$i )
- {
- $characters[$i] = new $char_class($code[$i]);
- list($min, $max) = $characters[$i]->range();
- $sizes[$i] = mt_rand($min, $max / 2);
- $box = $characters[$i]->dimensions($sizes[$i]);
- $width_avail -= ($box[2] - $box[0]);
- $bounding_boxes[$i] = $box;
- }
-
- // Redistribute leftover x-space
- $offset = array();
- for ( $i = 0; $i < $code_num; ++$i )
- {
- $denom = ($code_num - $i);
- $denom = max(1.5, $denom);
- $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom);
- $width_avail -= $offset[$i];
- }
-
- // Add some line noise
- if ($config['policy_shape_noise_line'])
- {
- $this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), $primaries, $noise);
- }
-
- $real = mt_rand(0, 3);
- $patterns = array('', '', '', '');
- for ($i = 32; $i > 0; --$i)
- {
- $patterns[$i & 3] .= str_pad(dechex(mt_rand(0, 65535)), 4, '0', STR_PAD_LEFT);
- }
-
-
- for ($i = 0; $i < 4; ++$i)
- {
- /*if ($i)
- {
- $y = 5 + ($i * 60);
- imageline($img, 550, $y, 650, $y, $fontcolors[0]);
- }*/
- $this->draw_pattern($patterns[$i], $img, 525, 10 + ($i * 60), 575, ($i + 1) * 60, $primaries);
- if ($i == $real)
- {
- $this->draw_pattern($patterns[$i], $img, 25, 25, 225, 225, $primaries, 3);
- for ($j = 0; $j < $code_num; ++$j)
- {
- $character = new $char_class($code[$j]);
- $character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $c->r('background'), $primaries);
- }
- }
- else
- {
- $word = $this->get_char_string();
- for ($j = strlen($word) - 1; $j >= 0; --$j)
- {
- $character = new $char_class(substr($word, $j, 1));
- $character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $c->r('background'), $primaries);
- }
- }
- }
-
- $count = sizeof($user->lang['CAPTCHA']['shape']);
- $line_height = $img_y / ($count + 1);
- for ($i = 0; $i < $count; ++$i)
- {
- $text = $user->lang['CAPTCHA']['shape'][$i];
- $line_width = strlen($text) * 4.5; // ( / 2, * 9 )
- imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) - 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) + 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) + 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) - 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width, $line_height * ($i + 1), $text, $c->r('white'));
- }
-
-
- // Add some pixel noise
- if ($config['policy_shape_noise_pixel'])
- {
- $this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), $primaries, $noise, $config['policy_shape_noise_pixel']);
- }
-
- // Send image
- $this->send_image($img);
- }
-
- function policy_composite($code)
- {
- // Generate image
- $img_x = 800;
- $img_y = 250;
- $img = imagecreate($img_x, $img_y);
-
- $map = captcha_vectors();
- $fonts = captcha_load_ttf_fonts();
-
- // Generate basic colors
- $c = new color_manager($img, 'white');
- $c->allocate_named('primary', array(
- 'random' => true,
- 'min_saturation' => 50,
- 'min_value' => 75,
- ));
- $bg_colors = $c->color_scheme('primary', 'triadic', false);
- $text_colors = $c->mono_range('primary', 'saturation', 6);
- $bg_colors = $c->mono_range($bg_colors, 'saturation', 6);
-
- // Specificy image portion dimensions.
- $count = sizeof($code);
- $cellsize = $img_x / $count;
- $y_range = min($cellsize, $img_y);
- $y_max = $img_y - $y_range;
- $y_off = array(); // consecutive vertical offset of characters
- $color = array(); // color of characters
- $y_off[0] = mt_rand(0, $y_max);
- for ($i = 1; $i < $count; ++$i)
- {
- // each consective character can be as much as 50% closer to the top or bottom of the image as the previous
- $diff = mt_rand(-50, 50);
- if ($diff > 0)
- {
- $y_off[$i] = $y_off[$i - 1] + ((($y_max - $y_off[$i - 1]) * $diff) / 100);
- }
- else
- {
- $y_off[$i] = $y_off[$i - 1] * ((100 + $diff) / 100);
- }
- }
-
- $range = 0.075;
-
- $chars = array_merge(range('A', 'Z'), range('1', '9'));
-
- // draw some characters. if they're within the vector spec of the code character, color them differently
- for ($i = 0; $i < 8000; ++$i)
- {
- $degree = mt_rand(-30, 30);
- $x = mt_rand(0, $img_x - 1);
- $y = mt_rand(0, $img_y);
- $text = $chars[array_rand($chars)];
- $char = $x / $cellsize;
- $meta_x = ((($x % $cellsize) / $cellsize) * 1.5) - 0.25;
- $meta_y = (($img_y - $y) - $y_off[$char]) / $y_range;
- $font = $fonts[array_rand($fonts)];
-
- $distance = vector_distance($map[$code[$char]], $meta_x, $meta_y, $range);
-
- $switch = !(rand() % 100);
-
- imagettftext($img, 10, $degree, $x, $y,
- (($distance <= $range) xor $switch) ?
- $c->r_rand($text_colors) :
- $c->r_rand($bg_colors),
- $font, $text);
-
- }
-
- // Send image
- $this->send_image($img);
- }
-
- function policy_stencil($code)
- {
- // Generate image
- $img_x = 800;
- $img_y = 250;
- $img = imagecreatetruecolor($img_x, $img_y);
- $stencil = imagecreatetruecolor($img_x, $img_y);
-
- $map = captcha_vectors();
- $fonts = captcha_load_ttf_fonts();
-
- // Generate colors
- $c = new color_manager($img, 'black');
- $cs = new color_manager($stencil, 'gray');
-
- $c->allocate_named('primary', array(
- 'random' => true,
- 'min_saturation' => 75,
- 'min_value' => 80,
- ));
-
- $secondary = $c->color_scheme('primary', 'triadic', false);
-
- //imagefill($stencil, 0, 0, $black2);
- //imagefill($img, 0, 0, $white1);
-
- $chars = array_merge(range('A', 'Z'), range('1', '9'));
- $step = 20;
- $density = 4;
- for ($i = 0; $i < $img_x; $i += $step)
- {
- for ($j = 0; $j < $img_y; $j += $step)
- {
- for ($k = 0; $k < $density; ++$k)
- {
- $degree = mt_rand(-30, 30);
- $x = mt_rand($i, $i + $step);
- $y = mt_rand($j, $j + $step);
- $char = $chars[array_rand($chars)];
- $font = $fonts[array_rand($fonts)];
- imagettftext($stencil, mt_rand(20, 30), $degree, $x, $y, $cs->r('black'), $font, $char);
- }
- }
- }
-
- for ($i = 0; $i < 3; ++$i)
- {
- $degree1 = mt_rand(-30, 30);
- $degree2 = mt_rand(-30, 30);
- $x1 = mt_rand(0, $img_x - 1);
- $x2 = mt_rand(0, $img_x - 1);
- $y1 = mt_rand(0, $img_y);
- $y2 = mt_rand(0, $img_y);
- $char1 = $chars[array_rand($chars)];
- $char2 = $chars[array_rand($chars)];
- $font1 = $fonts[array_rand($fonts)];
- $font2 = $fonts[array_rand($fonts)];
-
- imagettftext($img, mt_rand(75, 100), $degree1, $x1, $y1, $secondary[0], $font1, $char1);
- imagettftext($img, mt_rand(75, 100), $degree2, $x2, $y2, $secondary[1], $font2, $char2);
- }
-
- $characters = array();
- $sizes = array();
- $bounding_boxes = array();
- $width_avail = $img_x;
- $code_num = sizeof($code);
- $char_class = $this->captcha_char('char_ttf');
- for ($i = 0; $i < $code_num; ++$i)
- {
- $characters[$i] = new $char_class($code[$i]);
- $sizes[$i] = mt_rand(75, 100);
- $box = $characters[$i]->dimensions($sizes[$i]);
- $width_avail -= ($box[2] - $box[0]);
- $bounding_boxes[$i] = $box;
- }
-
- //
- // Redistribute leftover x-space
- //
- $offset = array();
- for ($i = 0; $i < $code_num; ++$i)
- {
- $denom = ($code_num - $i);
- $denom = max(1.5, $denom);
- $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom);
- $width_avail -= $offset[$i];
- }
-
- // Draw the text
- $xoffset = 0;
- for ($i = 0; $i < $code_num; ++$i)
- {
- $characters[$i] = new $char_class($code[$i]);
- $dimm = $bounding_boxes[$i];
- $xoffset += ($offset[$i] - $dimm[0]);
- $yoffset = mt_rand(-$dimm[1], $img_y - $dimm[3]);
- $characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $c->r('background'), array($c->r('primary')));
- $xoffset += $dimm[2];
- }
-
- for ($i = 0; $i < $img_x; ++$i)
- {
- for ($j = 0; $j < $img_y; ++$j)
- {
- // if the stencil is not black, set the pixel in the image to gray
- if (imagecolorat($stencil, $i, $j))
- {
- imagesetpixel($img, $i, $j, $c->r('gray'));
- }
- }
- }
-
- // Send image
- $this->send_image($img);
- }
-
- function policy_cells($code)
- {
- global $user;
- // Generate image
- $img_x = 800;
- $img_y = 250;
- $img = imagecreate($img_x, $img_y);
-
- $fonts = captcha_load_ttf_fonts();
-
- $map = captcha_vectors();
-
- //
- // Generate colors
- //
- $c = new color_manager($img, 'white');
-
- $c->allocate_named('primary', array(
- 'random' => true,
- 'min_saturation' => 30,
- 'min_value' => 65,
- ));
- $primaries = $c->color_scheme('primary', 'tetradic');
- $bg_colors = $c->mono_range($primaries, 'value', 4, false);
- shuffle($primaries);
- shuffle($bg_colors);
-
- // Randomize the characters on the right and the left
- $left_characters = array();
- $right_characters = array();
- $chars = array_merge(range('A', 'Z'), range('1', '9'));
- $chars_size = sizeof($chars) - 1;
- $alpha = range('A', 'Z');
- $alpha_size = sizeof($alpha) - 1;
- for ($i = 0; $i < 25; ++$i)
- {
- $left_characters[$i] = $alpha[mt_rand(0, $alpha_size)];
- $right_characters[$i] = $chars[mt_rand(0, $chars_size)];
- }
-
- // Pick locations for our code, shuffle the rest into 3 separate queues
- $code_count = sizeof($code);
- $code_order = range(0, 24);
- shuffle($code_order);
- $remaining = array_splice($code_order, $code_count);
- $lineups = array($code_order, array(), array(), array());
- for ($i = sizeof($remaining) - 1; $i >= 0; --$i)
- {
- $lineups[mt_rand(1, 3)][] = $remaining[$i];
- }
-
- // overwrite the randomized left and right values with our code, where applicable
- for ($i = 0; $i < $code_count; ++$i)
- {
- $left_characters[$code_order[$i]] = $i + 1;
- $right_characters[$code_order[$i]] = $code[$i];
- }
-
-
- $offset1 = 50;
- $offset2 = 550;
-
- // Draw the cells and right hand characters
- $xs = $ys = array();
- for ($i = 0; $i < 25; ++$i)
- {
- $xs[$i] = $offset1 + 20 + (($i % 5) * 40) + mt_rand(-13, 13);
- $ys[$i] = 45 + (intval($i / 5) * 40) + mt_rand(-13, 13);
-
- $bg = $c->r_rand($bg_colors);
-
- // fill the cells with the background colors
- imagefilledrectangle($img,
- $offset1 + 1 + (($i % 5) * 40), 26 + (intval($i / 5) * 40),
- $offset1 + 39 + (($i % 5) * 40), 64 + (intval($i / 5) * 40),
- $bg);
- imagefilledrectangle($img,
- $offset2 + 1 + (($i % 5) * 40), 26 + (intval($i / 5) * 40),
- $offset2 + 39 + (($i % 5) * 40), 64 + (intval($i / 5) * 40),
- $bg);
-
- $level = intval($i / 5);
- $pos = $i % 5;
- imagettftext($img, 12, 0,
- $offset2 + 15 + ($pos * 40), 50 + ($level * 40),
- $c->is_dark($bg) ? $c->r('white'): $c->r('black'), $fonts['genr102.ttf'], $right_characters[$i]);
- }
-
- // draw the lines that appear between nodes (visual hint)
- for ($k = 0; $k < 4; ++$k )
- {
- $lineup = $lineups[$k];
- for ($i = 1, $size = sizeof($lineup); $i < $size; ++$i )
- {
- imageline($img,
- $xs[$lineup[$i - 1]], $ys[$lineup[$i - 1]],
- $xs[$lineup[$i]], $ys[$lineup[$i]],
- $primaries[$k]);
- }
- }
-
- // draw the actual nodes
- $textcolor = $c->is_dark($primaries[0]) ? $c->r('white') : $c->r('black');
- for ($k = 0; $k < 4; ++$k )
- {
- for ($j = 0, $size = sizeof($lineups[$k]); $j < $size; ++$j )
- {
- $i = $lineups[$k][$j];
- imagefilledellipse($img,
- $xs[$i], $ys[$i],
- 20, 20,
- $primaries[$k]);
- imagettftext($img, 12, 0,
- $xs[$i] - 5, $ys[$i] + 5,
- $textcolor, $fonts['genr102.ttf'], $left_characters[$i]);
- }
- }
-
- // Draw poly behind explain text
- $points = mt_rand(3, 6);
- $arc = 360 / $points;
- $vertices = array();
- $c_x = $img_x / 2;
- $c_y = $img_y / 2;
- $radius = $img_y / 2.5;
- $start = deg2rad(mt_rand(0, 360));
- for ($i = 0; $i < $points; ++$i)
- {
- $rad = $start + deg2rad(($arc * $i) + mt_rand(-10, 10));
- $vertices[] = $c_x + (cos($rad) * $radius);
- $vertices[] = $c_y + (sin($rad) * $radius);
- }
- imagefilledpolygon($img, $vertices, $points, $primaries[mt_rand(0,3)]);
-
- // draw explain text
- $count = sizeof($user->lang['CAPTCHA']['cells']);
- $line_height = $img_y / ($count + 1);
- for ($i = 0; $i < $count; ++$i)
- {
- $text = $user->lang['CAPTCHA']['cells'][$i];
- $line_width = strlen($text) * 4.5; // ( / 2, * 9 )
- imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) - 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) + 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) + 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) - 1, $text, $c->r('black'));
- imagestring($img, 6, ($img_x / 2) - $line_width, $line_height * ($i + 1), $text, $c->r('white'));
- }
-
- // Send image
- $this->send_image($img);
- }
-
- /**
- * entropy
- */
- function policy_entropy($code)
+ function execute($code)
{
global $config;
- // Generate image
- $img_x = 800;
- $img_y = 250;
- $img = imagecreatetruecolor($img_x, $img_y);
-
- // Generate colors
- $c = new color_manager($img, array(
- 'random' => true,
- 'min_value' => 60,
- ), 'hsv');
+ $stats = gd_info();
- $scheme = $c->color_scheme('background', 'triadic', false);
- $scheme = $c->mono_range($scheme, 'both', 10, false);
- shuffle($scheme);
- $bg_colors = array_splice($scheme, mt_rand(6, 12));
-
- // Generate code characters
- $characters = $sizes = $bounding_boxes = array();
- $width_avail = $img_x;
- $code_num = sizeof($code);
-
- for ($i = 0; $i < $code_num; ++$i)
- {
- $char_class = $this->captcha_char();
- $characters[$i] = new $char_class($code[$i]);
-
- list($min, $max) = $characters[$i]->range();
- $sizes[$i] = mt_rand($min, $max);
- $box = $characters[$i]->dimensions($sizes[$i]);
- $width_avail -= ($box[2] - $box[0]);
- $bounding_boxes[$i] = $box;
- }
-
- // Redistribute leftover x-space
- $offset = array();
- for ($i = 0; $i < $code_num; ++$i)
+ if (substr($stats['GD Version'], 0, 7) === 'bundled')
{
- $denom = ($code_num - $i);
- $denom = max(1.5, $denom);
- $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom);
- $width_avail -= $offset[$i];
+ $bundled = true;
}
-
- // Add some line noise
- if ($config['policy_entropy_noise_line'])
- {
- $this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), $scheme, $bg_colors);
- }
-
- // Draw the text
- $xoffset = 0;
- for ($i = 0; $i < $code_num; ++$i)
- {
- $dimm = $bounding_boxes[$i];
- $xoffset += ($offset[$i] - $dimm[0]);
- $yoffset = mt_rand(-$dimm[1], $img_y - $dimm[3]);
- $characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $c->r('background'), $scheme);
- $xoffset += $dimm[2];
- }
-
- // Add some pixel noise
- if ($config['policy_entropy_noise_pixel'])
+ else
{
- $this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), $scheme, $bg_colors, $config['policy_entropy_noise_pixel']);
+ $bundled = false;
}
- // Send image
- $this->send_image($img);
- }
-
- /**
- * 3dbitmap
- */
- function policy_3dbitmap($code)
- {
- // Generate image
- $img_x = 700;
- $img_y = 225;
- $img = imagecreatetruecolor($img_x, $img_y);
- $x_grid = mt_rand(6, 10);
- $y_grid = mt_rand(6, 10);
-
- // Ok, so lets cut to the chase. We could accurately represent this in 3d and
- // do all the appropriate linear transforms. my questions is... why bother?
- // The computational overhead is unnecessary when you consider the simple fact:
- // we're not here to accurately represent a model, but to just show off some random-ish
- // polygons
-
- // Conceive of 3 spaces.
- // 1) planar-space (discrete "pixel" grid)
- // 2) 3-space. (planar-space with z/height aspect)
- // 3) image space (pixels on the screen)
-
- // resolution of the planar-space we're embedding the text code in
- $plane_x = 90;
- $plane_y = 25;
-
- $subdivision_factor = 2;
-
- // $box is the 4 points in img_space that correspond to the corners of the plane in 3-space
- $box = array(array(), array(), array(), array());
-
- // Top left
- $box[0][0] = mt_rand(20, 40);
- $box[0][1] = mt_rand(40, 60);
-
- // Top right
- $box[1][0] = mt_rand($img_x - 80, $img_x - 60);
- $box[1][1] = mt_rand(10, 30);
-
- // Bottom right
- $box[2][0] = mt_rand($img_x - 40, $img_x - 20);
- $box[2][1] = mt_rand($img_y - 50, $img_y - 30);
-
- // Bottom left.
- // because we want to be able to make shortcuts in the 3d->2d,
- // we'll calculate the 4th point so that it forms a proper trapezoid
- $box[3][0] = $box[2][0] + $box[0][0] - $box[1][0];
- $box[3][1] = $box[2][1] + $box[0][1] - $box[1][1];
- $c = new color_manager($img, array(
- 'random' => true,
- 'min_saturation' => 50,
- 'min_value' => 65,
- ));
-
- $r1 = $c->random_color(array(
- 'min_value' => 20,
- 'max_value' => 50,
- ));
- $r2 = $c->random_color(array(
- 'min_value' => 70,
- 'max_value' => 100,
- ));
- $rdata = mt_rand(0,1) ? array(
- $c->colors[$r1],
- $c->colors[$r2],
- ) : array(
- $c->colors[$r2],
- $c->colors[$r1],
- );
-
- $colors = array();
- for ($i = 0; $i < 60; ++$i)
+ preg_match('/[\\d.]+/', $stats['GD Version'], $version);
+ if (version_compare($version[0], '2.0.1', '>='))
{
- $colors[$i - 30] = $c->allocate(array(
- $rdata[0][0],
- (($i * $rdata[0][1]) + ((60 - $i) * $rdata[1][1])) / 60,
- (($i * $rdata[0][2]) + ((60 - $i) * $rdata[1][2])) / 60,
- ));
+ $gd_version = 2;
}
-
- // $img_buffer is the last row of 3-space positions (converted to img-space), cached
- // (using this means we don't need to recalculate all 4 positions for each new polygon,
- // merely the newest point that we're adding, which is then cached.
- $img_buffer = array(array(), array());
-
- // In image-space, the x- and y-offset necessary to move one unit in the x-direction in planar-space
- $dxx = ($box[1][0] - $box[0][0]) / ($subdivision_factor * $plane_x);
- $dxy = ($box[1][1] - $box[0][1]) / ($subdivision_factor * $plane_x);
-
- // In image-space, the x- and y-offset necessary to move one unit in the y-direction in planar-space
- $dyx = ($box[3][0] - $box[0][0]) / ($subdivision_factor * $plane_y);
- $dyy = ($box[3][1] - $box[0][1]) / ($subdivision_factor * $plane_y);
-
- // Initial captcha-letter offset in planar-space
- $plane_offset_x = 2;
- $plane_offset_y = 5;
-
- // character map
- $map = captcha_bitmaps();
-
- // matrix
- $plane = array();
-
- // for each character, we'll silkscreen it into our boolean pixel plane
- for ($c = 0, $code_num = sizeof($code); $c < $code_num; ++$c)
+ else
{
- $letter = $code[$c];
-
- for ($x = $map['width'] - 1; $x >= 0; --$x)
- {
- for ($y = $map['height'] - 1; $y >= 0; --$y)
- {
- if ($map['data'][$letter][$y][$x])
- {
- $plane[$y + $plane_offset_y + (($c & 1) ? 1 : -1)][$x + $plane_offset_x] = true;
- }
- }
- }
- $plane_offset_x += 11;
+ $gd_version = 1;
}
- // calculate our first buffer, we can't actually draw polys with these yet
- // img_pos_prev == screen x,y location to our immediate left.
- // img_pos_cur == current screen x,y location
- // we calculate screen position of our
- // current cell based on the difference from the previous cell
- // rather than recalculating from absolute coordinates
- // What we cache into the $img_buffer contains the raised text coordinates.
- $img_pos_prev = $img_buffer[0][0] = $box[0];
- $cur_height = $prev_height = $this->wave_height(0, 0, $subdivision_factor);
- $full_x = $plane_x * $subdivision_factor;
- $full_y = $plane_y * $subdivision_factor;
+ // set dimension of image
+ $lx = 360;
+ $ly = 96;
- for ($x = 1; $x <= $full_x; ++$x)
+ // create the image, stay compat with older versions of GD
+ if ($gd_version === 2)
{
- $cur_height = $this->wave_height($x, 0, $subdivision_factor);
- $offset = $cur_height - $prev_height;
- $img_pos_cur = array($img_pos_prev[0] + $dxx,
- $img_pos_prev[1] + $dxy + $offset);
-
- $img_buffer[0][$x] = $img_pos_cur;
- $img_pos_prev = $img_pos_cur;
- $prev_height = $cur_height;
+ $func1 = 'imagecreatetruecolor';
+ $func2 = 'imagecolorallocate';
}
-
- for ($y = 1; $y <= $full_y; ++$y)
+ else
{
- // swap buffers
- $buffer_cur = $y & 1;
- $buffer_prev = 1 - $buffer_cur;
-
- $prev_height = $this->wave_height(0, $y, $subdivision_factor);
- $offset = $prev_height - $this->wave_height(0, $y - 1, $subdivision_factor);
- $img_pos_cur = array($img_buffer[$buffer_prev][0][0] + $dyx,
- $img_buffer[$buffer_prev][0][1] + $dyy + $offset);
- $img_pos_prev = $img_pos_cur;
-
- $img_buffer[$buffer_cur][0] = $img_pos_cur;
-
- for ($x = 1; $x <= $full_x; ++$x)
- {
- $cur_height = $this->wave_height($x, $y, $subdivision_factor) + $this->grid_height($x, $y, 1, $x_grid, $y_grid);
-
- //height is a z-factor, not a y-factor
- $offset = $cur_height - $prev_height;
- $img_pos_cur = array($img_pos_prev[0] + $dxx,
- $img_pos_prev[1] + $dxy + $offset);
-
- //(height is float, index it to an int, get closest color)
- $color = $colors[intval($cur_height)];
- $img_pos_prev = $img_pos_cur;
- $prev_height = $cur_height;
-
- $y_index_old = intval(($y - 1) / $subdivision_factor);
- $y_index_new = intval($y / $subdivision_factor);
- $x_index_old = intval(($x - 1) / $subdivision_factor);
- $x_index_new = intval($x / $subdivision_factor);
-
- if (!empty($plane[$y_index_new][$x_index_new]))
- {
- $offset2 = $this->wave_height($x, $y, $subdivision_factor, 1) - 30 - $cur_height;
- $img_pos_cur[1] += $offset2;
- $color = $colors[20];
- }
- $img_buffer[$buffer_cur][$x] = $img_pos_cur;
-
- // Smooth the edges as much as possible by having not more than one low<->high traingle per square
- // Otherwise, just
- $diag_down = (empty($plane[$y_index_old][$x_index_old]) == empty($plane[$y_index_new][$x_index_new]));
- $diag_up = (empty($plane[$y_index_old][$x_index_new]) == empty($plane[$y_index_new][$x_index_old]));
-
- // natural switching
- $mode = ($x + $y) & 1;
-
- // override if it requires it
- if ($diag_down != $diag_up)
- {
- $mode = $diag_up;
- }
-
- if ($mode)
- {
- // +-/ /
- // 1 |/ 2 /|
- // / /-+
- $poly1 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_prev][$x]);
- $poly2 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_cur][$x], $img_buffer[$buffer_prev][$x]);
- }
- else
- {
- // \ \-+
- // 1 |\ 2 \|
- // +-\ \
- $poly1 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_cur][$x]);
- $poly2 = array_merge($img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_prev][$x], $img_buffer[$buffer_cur][$x]);
- }
-
- imagefilledpolygon($img, $poly1, 3, $color);
- imagefilledpolygon($img, $poly2, 3, $color);
- }
+ $func1 = 'imagecreate';
+ $func2 = 'imagecolorclosest';
}
- // Send image on it's merry way
- $this->send_image($img);
- }
+ $image = $func1($lx, $ly);
- /**
- * overlap
- */
- function policy_overlap($code)
- {
- global $config;
- $char_size = 40;
- $overlap_factor = .32;
-
- // Generate image
- $img_x = 250;
- $img_y = 120;
- $img = imagecreatetruecolor($img_x, $img_y);
-
- // Generate colors
- $c = new color_manager($img, array(
- 'random' => true,
- 'min_saturation' => 70,
- 'min_value' => 65,
- ));
-
- $primaries = $c->color_scheme('background', 'triadic', false);
- $text = mt_rand(0, 1);
- $c->name_color('text', $primaries[$text]);
- $noise = $c->mono_range($primaries[1 - $text], 'both', 6, false);
-
- // Generate code characters
- $characters = $bounding_boxes = array();
- $width_avail = $img_x;
-
- // Get the character rendering scheme
- $char_class = $this->captcha_char('char_ttf');
- $code_num = sizeof($code);
-
- for ($i = 0; $i < $code_num; ++$i)
+ if ($bundled)
{
- $characters[$i] = new $char_class($code[$i], array('angle' => 0));
- $box = $characters[$i]->dimensions($char_size);
- $width_avail -= ((1 - $overlap_factor) * ($box[2] - $box[0]));
- $bounding_boxes[$i] = $box;
+ imageantialias($image, true);
}
- // Redistribute leftover x-space
- $offset = mt_rand(0, $width_avail);
-
- // Add some line noise
- if ($config['policy_overlap_noise_line'])
- {
- $this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), array($c->r('text')), $noise);
- }
+ // set background color
+ $back = imagecolorallocate($image, mt_rand(224, 255), mt_rand(224, 255), mt_rand(224, 255));
+ imagefilledrectangle($image, 0, 0, $lx, $ly, $back);
- // Draw the text
- $min = 10 - $bounding_boxes[0][1];
- $max = ($img_y - 10) - $bounding_boxes[0][3];
- $med = ($max + $min) / 2;
-
- $yoffset = mt_rand($med, $max);
- $char_num = sizeof($characters);
-
- imagesetthickness($img, 3);
- for ($i = 0; $i < $char_num; ++$i)
+ // allocates the 216 websafe color palette to the image
+ if ($gd_version === 1)
{
- if ($i)
+ for ($r = 0; $r <= 255; $r += 51)
{
- imageline($img, $old_x + mt_rand(-3, 3), $old_y - 70 + mt_rand(-3, 3), $offset + mt_rand(-3, 3), $yoffset - 70 + mt_rand(-3, 3), $c->r('text'));
- imageline($img, $old_x + mt_rand(-3, 3), $old_y + 30 + mt_rand(-3, 3), $offset + mt_rand(-3, 3), $yoffset + 30 + mt_rand(-3, 3), $c->r('text'));
- }
-
- $dimm = $bounding_boxes[$i];
- $offset -= $dimm[0];
- $characters[$i]->drawchar($char_size, $offset, $yoffset, $img, $c->r('background'), array($c->r('text')));
-
- $old_x = $offset;
- $old_y = $yoffset;
-
- $offset += $dimm[2];
- $offset -= (($dimm[2] - $dimm[0]) * $overlap_factor);
- $yoffset += ($i & 1) ? ((1 - $overlap_factor) * ($dimm[3] - $dimm[1])) : ((1 - $overlap_factor) * ($dimm[1] - $dimm[3]));
- }
-
- imagesetthickness($img, 1);
-
- // Add some medium pixel noise
- if ($config['policy_overlap_noise_pixel'])
- {
- $this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), array($c->r('text')), $noise, $config['policy_overlap_noise_pixel']);
- }
-
- // Send image
- $this->send_image($img);
- }
-
- /**
- * Noise pixel
- */
- function noise_pixel($img, $min_x, $min_y, $max_x, $max_y, $bg, $font, $non_font, $override = false)
- {
- $noise_modules = array('noise_pixel_light', 'noise_pixel_medium', 'noise_pixel_heavy');
-
- if ($override == false)
- {
- $override = array_rand($override);
- }
-
- // Use the module $override, else a random picked one...
- $module = $noise_modules[intval($override) - 1];
-
- switch ($module)
- {
- case 'noise_pixel_light':
-
- for ($x = $min_x; $x < $max_x; $x += mt_rand(9, 18))
+ for ($g = 0; $g <= 255; $g += 51)
{
- for ($y = $min_y; $y < $max_y; $y += mt_rand(4, 9))
+ for ($b = 0; $b <= 255; $b += 51)
{
- imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]);
+ imagecolorallocate($image, $r, $g, $b);
}
}
-
- for ($y = $min_y; $y < $max_y; $y += mt_rand(9, 18))
- {
- for ($x = $min_x; $x < $max_x; $x += mt_rand(4, 9))
- {
- imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]);
- }
- }
-
- break;
-
- case 'noise_pixel_medium':
-
- for ($x = $min_x; $x < $max_x; $x += mt_rand(4, 9))
- {
- for ($y = $min_y; $y < $max_y; $y += mt_rand(2, 5))
- {
- imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]);
- }
- }
-
- for ($y = $min_y; $y < $max_y; $y += mt_rand(4, 9))
- {
- for ($x = $min_x; $x < $max_x; $x += mt_rand(2, 5))
- {
- imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]);
- }
- }
-
- break;
-
- case 'noise_pixel_heavy':
-
- for ($x = $min_x; $x < $max_x; $x += mt_rand(4, 9))
- {
- for ($y = $min_y; $y < $max_y; $y++)
- {
- imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]);
- }
- }
-
- for ($y = $min_y; $y < $max_y; $y+= mt_rand(4, 9))
- {
- for ($x = $min_x; $x < $max_x; $x++)
- {
- imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]);
- }
- }
-
- break;
- }
- }
-
- /**
- * Noise line
- */
- function noise_line($img, $min_x, $min_y, $max_x, $max_y, $bg, $font, $non_font)
- {
- imagesetthickness($img, 2);
- $x1 = $min_x;
- $x2 = $max_x;
- $y1 = $min_y;
- $y2 = $min_y;
-
- do
- {
- $line = array_merge(
- array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]),
- array_fill(0, mt_rand(30, 60), $bg)
- );
-
- imagesetstyle($img, $line);
- imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
-
- $y1 += mt_rand(12, 35);
- $y2 += mt_rand(12, 35);
- }
- while ($y1 < $max_y && $y2 < $max_y);
-
- $x1 = $min_x;
- $x2 = $min_x;
- $y1 = $min_y;
- $y2 = $max_y;
-
- do
- {
- $line = array_merge(
- array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]),
- array_fill(0, mt_rand(30, 60), $bg)
- );
-
- imagesetstyle($img, $line);
- imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
-
- $x1 += mt_rand(12, 35);
- $x2 += mt_rand(12, 35);
- }
- while ($x1 < $max_x && $x2 < $max_x);
- imagesetthickness($img, 1);
- }
-
- /**
- * Randomly determine which char class to use
- * Able to define static one with override
- */
- function captcha_char($override = false)
- {
- static $character_classes = array();
-
- // Some people have GD but no TTF support
- if (sizeof($character_classes) == 0)
- {
- $character_classes = array('char_vector', 'char_hatches', 'char_cube3d', 'char_dots');
-
- if (function_exists('imagettfbbox') && function_exists('imagettftext'))
- {
- $character_classes[] = 'char_ttf';
- }
- }
-
- // Use the module $override, else a random picked one...
- $class = ($override !== false && in_array($override, $character_classes)) ? $override : $character_classes[array_rand($character_classes)];
-
- return $class;
- }
-}
-
-/**
-* @package VC
-*/
-class char_dots
-{
- var $vectors;
- var $space;
- var $radius;
- var $letter;
- var $width_percent;
-
- /**
- * Constuctor
- */
- function char_dots($letter = '', $args = false)
- {
- $width_percent = false;
- if (is_array($args))
- {
- $width_percent = (!empty($args['width_percent'])) ? $args['width_percent'] : false;
- }
-
- $this->vectors = captcha_vectors();
- $this->width_percent = (!empty($width_percent)) ? max(25, min(150, intval($width_percent))) : mt_rand(60, 90);
-
- $this->space = 10;
- $this->radius = 3;
- $this->density = 3;
- $this->letter = $letter;
- }
-
- /**
- * Draw a character
- */
- function drawchar($scale, $xoff, $yoff, $img, $background, $colors)
- {
- $vectorset = $this->vectors[$this->letter];
- $height = $scale;
- $width = (($scale * $this->width_percent) / 100);
- $color = $colors[array_rand($colors)];
-
- if (sizeof($vectorset))
- {
- foreach ($vectorset as $veclist)
- {
- switch ($veclist[0])
- {
- case 'line':
-
- $dx = ($veclist[3] - $veclist[1]) * $width;
- $dy = ($veclist[4] - $veclist[2]) * -$height;
-
- $len = sqrt(($dx * $dx) + ($dy * $dy));
-
- $inv_dx = -($dy / $len);
- $inv_dy = ($dx / $len);
-
- for ($i = 0; $i < $len; ++$i)
- {
- for ($k = 0; $k <= $this->density; ++$k)
- {
- $shift = mt_rand(-$this->radius, $this->radius);
- imagesetpixel($img,
- $xoff + ($veclist[1] * $width) + (($i * $dx) / $len) + ($inv_dx * $shift),
- $yoff + ((1 - $veclist[2]) * $height) + (($i * $dy) / $len) + ($inv_dy * $shift),
- $color);
- }
- }
-
- break;
-
- case 'arc':
-
- $arclengthdeg = $veclist[6] - $veclist[5];
- $arclengthdeg += ( $arclengthdeg < 0 ) ? 360 : 0;
-
- $arclength = ((($veclist[3] * $width) + ($veclist[4] * $height)) * M_PI) / 2;
-
- $arclength = ($arclength * $arclengthdeg) / 360;
-
- $x_c = $veclist[1] * $width;
- $y_c = (1 - $veclist[2]) * $height;
- $increment = ($arclengthdeg / $arclength);
-
- for ($i = 0; $i < $arclengthdeg; $i += $increment)
- {
- $theta = deg2rad(($i + $veclist[5]) % 360);
- $x_o = cos($theta);
- $y_o = sin($theta);
- $pre_width = ($veclist[3] * 0.5 * $width);
- $pre_height = ($veclist[4] * 0.5 * $height);
- for ($k = 0; $k <= $this->density; ++$k)
- {
- $shift = mt_rand(-$this->radius, $this->radius);
- $x_o1 = $x_o * ($pre_width + $shift);
- $y_o1 = $y_o * ($pre_height + $shift);
- imagesetpixel($img,
- $xoff + $x_c + $x_o1,
- $yoff + $y_c + $y_o1,
- $color);
- }
- }
-
- break;
-
- default:
- // Do nothing with bad input
- break;
- }
}
}
- }
-
- /*
- * return a roughly acceptable range of sizes for rendering with this texttype
- */
- function range()
- {
- return array(60, 80);
- }
- /**
- * dimensions
- */
- function dimensions($size)
- {
- return array(-4, -4, (($size * $this->width_percent) / 100) + 4, $size + 4);
- }
-}
-/**
-* @package VC
-*/
-class char_vector
-{
- var $vectors;
- var $width_percent;
- var $letter;
-
- /**
- * Constructor
- */
- function char_vector($letter = '', $args = false)
- {
- $width_percent = false;
- if (is_array($args))
- {
- $width_percent = (!empty($args['width_percent'])) ? $args['width_percent'] : false;
- }
-
- $this->vectors = captcha_vectors();
- $this->width_percent = (!empty($width_percent)) ? max(25, min(150, intval($width_percent))) : mt_rand(60,90);
- $this->letter = $letter;
- }
-
- /**
- * Draw a character
- */
- function drawchar($scale, $xoff, $yoff, $img, $background, $colors)
- {
- $vectorset = $this->vectors[$this->letter];
- $height = $scale;
- $width = (($scale * $this->width_percent) / 100);
- $color = $colors[array_rand($colors)];
-
- if (sizeof($vectorset))
+ // fill with noise or grid
+ if ($config['captcha_gd_noise'])
{
- foreach ($vectorset as $veclist)
+ // random characters in background with random position, angle, color
+ for ($i = 0 ; $i < 72; $i++)
{
- for ($i = 0; $i < 9; ++$i)
- {
- $xp = $i % 3;
- $yp = ($i - $xp) / 3;
- $xp--;
- $yp--;
-
- switch ($veclist[0])
- {
- case 'line':
- imageline($img,
- $xoff + $xp + ($veclist[1] * $width),
- $yoff + $yp + ((1 - $veclist[2]) * $height),
- $xoff + $xp + ($veclist[3] * $width),
- $yoff + $yp + ((1 - $veclist[4]) * $height),
- $color
- );
- break;
-
- case 'arc':
- imagearc($img,
- $xoff + $xp + ($veclist[1] * $width),
- $yoff + $yp + ((1 - $veclist[2]) * $height),
- $veclist[3] * $width,
- $veclist[4] * $height,
- $veclist[5],
- $veclist[6],
- $color
- );
- break;
- }
- }
+ $size = mt_rand(8, 23);
+ $angle = mt_rand(0, 360);
+ $x = mt_rand(0, 360);
+ $y = mt_rand(0, (int)($ly - ($size / 5)));
+ $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224));
+ $text = chr(mt_rand(45, 250));
+ imagettftext($image, $size, $angle, $x, $y, $color, $this->get_font(), $text);
}
}
- }
-
- /*
- * return a roughly acceptable range of sizes for rendering with this texttype
- */
- function range()
- {
- return array(50, 80);
- }
-
- /**
- * dimensions
- */
- function dimensions($size)
- {
- return array(-2, -2, (($size * $this->width_percent) / 100 ) + 2, $size + 2);
- }
-}
-
-/**
-* @package VC
-*/
-class char_ttf
-{
- var $angle = 0;
- var $fontfile = '';
- var $letter = '';
-
- /**
- * Constructor
- */
- function char_ttf($letter = '', $args = false)
- {
- $font = $angle = false;
-
- if (is_array($args))
- {
- $font = (!empty($args['font'])) ? $args['font'] : false;
- $angle = (isset($args['angle'])) ? $args['angle'] : false;
- }
-
- $fonts = captcha_load_ttf_fonts();
-
- if (empty($font) || !isset($fonts[$font]))
- {
- $font = array_rand($fonts);
- }
-
- $this->fontfile = $fonts[$font];
- $this->angle = ($angle !== false) ? intval($angle) : mt_rand(-40, 40);
- $this->letter = $letter;
- }
-
- /**
- * Draw a character
- */
- function drawchar($scale, $xoff, $yoff, $img, $background, $colors)
- {
- $color = $colors[array_rand($colors)];
- imagettftext($img, $scale, $this->angle, $xoff, $yoff, $color, $this->fontfile, $this->letter);
- }
-
- /*
- * return a roughly acceptable range of sizes for rendering with this texttype
- */
- function range()
- {
- return array(36, 150);
- }
-
- /**
- * Dimensions
- */
- function dimensions($scale)
- {
- $data = imagettfbbox($scale, $this->angle, $this->fontfile, $this->letter);
- return ($this->angle > 0) ? array($data[6], $data[5], $data[2], $data[1]) : array($data[0], $data[7], $data[4], $data[3]);
- }
-}
-
-/**
-* @package VC
-*/
-class char_hatches
-{
- var $vectors;
- var $space;
- var $radius;
- var $letter;
-
- /**
- * Constructor
- */
- function char_hatches($letter = '', $args = false)
- {
- $width_percent = false;
- if (is_array($args))
- {
- $width_percent = (!empty($args['width_percent'])) ? $args['width_percent'] : false;
- }
-
- $this->vectors = captcha_vectors();
- $this->width_percent = (!empty($width_percent)) ? max(25, min(150, intval($width_percent))) : mt_rand(60, 90);
-
- $this->space = 10;
- $this->radius = 3;
- $this->letter = $letter;
- }
-
- /**
- * Draw a character
- */
- function drawchar($scale, $xoff, $yoff, $img, $background, $colors)
- {
- $vectorset = $this->vectors[$this->letter];
- $height = $scale;
- $width = (($scale * $this->width_percent) / 100);
- $color = $colors[array_rand($colors)];
-
- if (sizeof($vectorset))
+ else
{
- foreach ($vectorset as $veclist)
+ // generate grid
+ for ($i = 0; $i < $lx; $i += 13)
{
- switch ($veclist[0])
- {
- case 'line':
- $dx = ($veclist[3] - $veclist[1]) * $width;
- $dy = ($veclist[4] - $veclist[2]) * -$height;
-
- $idx = -$dy;
- $idy = $dx;
-
- $length = sqrt(pow($dx, 2) + pow($dy, 2));
-
- $hatches = $length / $this->space;
-
- for ($p = 0; $p <= $hatches; ++$p)
- {
- if (!$p && !mt_rand(0, 9) && ($hatches > 3))
- {
- continue;
- }
-
- $xp = 1;
- $yp = -2;
- for ($i = 0; $i < 9; ++$i)
- {
- $xp += !($i % 3) ? -2 : 1;
- $yp += !($i % 3) ? 1 : 0;
-
- $x_o = ((($p * $veclist[1]) + (($hatches - $p) * $veclist[3])) * $width ) / $hatches;
- $y_o = $height - (((($p * $veclist[2]) + (($hatches - $p) * $veclist[4])) * $height ) / $hatches);
- $x_1 = $xoff + $xp + $x_o;
- $y_1 = $yoff + $yp + $y_o;
-
- $x_d1 = (($dx - $idx) * $this->radius) / $length;
- $y_d1 = (($dy - $idy) * $this->radius) / $length;
-
- $x_d2 = (($dx - $idx) * -$this->radius) / $length;
- $y_d2 = (($dy - $idy) * -$this->radius) / $length;
-
- imageline($img, $x_1 + $x_d1, $y_1 + $y_d1, $x_1 + $x_d2, $y_1 + $y_d2, $color);
- }
- }
- break;
-
- case 'arc':
- $arclengthdeg = $veclist[6] - $veclist[5];
- $arclengthdeg += ( $arclengthdeg < 0 ) ? 360 : 0;
-
- $arclength = ((($veclist[3] * $width) + ($veclist[4] * $height)) * M_PI) / 2;
- $arclength = ($arclength * $arclengthdeg) / 360;
-
- $hatches = $arclength / $this->space;
-
- $hatchdeg = ($arclengthdeg * $this->space) / $arclength;
- $shiftdeg = ($arclengthdeg * $this->radius) / $arclength;
-
- $x_c = $veclist[1] * $width;
- $y_c = (1 - $veclist[2]) * $height;
-
- for ($p = 0; $p <= $arclengthdeg; $p += $hatchdeg)
- {
- if (!mt_rand(0, 9) && ($hatches > 3) && !$p)
- {
- continue;
- }
-
- $theta1 = deg2rad(($p + $veclist[5] - $shiftdeg) % 360);
- $theta2 = deg2rad(($p + $veclist[5] + $shiftdeg) % 360);
- $x_o1 = cos($theta1) * (($veclist[3] * 0.5 * $width) - $this->radius);
- $y_o1 = sin($theta1) * (($veclist[4] * 0.5 * $height) - $this->radius);
- $x_o2 = cos($theta2) * (($veclist[3] * 0.5 * $width) + $this->radius);
- $y_o2 = sin($theta2) * (($veclist[4] * 0.5 * $height) + $this->radius);
-
- $xp = 1;
- $yp = -2;
- for ($i = 0; $i < 9; ++$i)
- {
- $xp += !($i % 3) ? -2 : 1;
- $yp += !($i % 3) ? 1 : 0;
-
- imageline($img,
- $xoff + $xp + $x_c + $x_o1,
- $yoff + $yp + $y_c + $y_o1,
- $xoff + $xp + $x_c + $x_o2,
- $yoff + $yp + $y_c + $y_o2,
- $color
- );
- }
- }
- break;
- }
+ $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224));
+ imageline($image, $i, 0, $i, $ly, $color);
}
- }
- }
-
- /*
- * return a roughly acceptable range of sizes for rendering with this texttype
- */
- function range()
- {
- return array(60, 80);
- }
-
- /**
- * Dimensions
- */
- function dimensions($size)
- {
- return array(-4, -4, (($size * $this->width_percent) / 100) + 4, $size + 4);
- }
-}
-
-/**
-* @package VC
-*/
-class char_cube3d
-{
- // need to abstract out the cube3d from the cubechar
- var $bitmaps;
-
- var $basis_matrix = array(array(1, 0, 0), array(0, 1, 0), array(0, 0, 1));
- var $abs_x = array(1, 0);
- var $abs_y = array(0, 1);
- var $x = 0;
- var $y = 1;
- var $z = 2;
- var $letter = '';
-
- function char_cube3d($letter)
- {
- $this->bitmaps = captcha_bitmaps();
-
- $this->basis_matrix[0][0] = mt_rand(-600, 600);
- $this->basis_matrix[0][1] = mt_rand(-600, 600);
- $this->basis_matrix[0][2] = (mt_rand(0, 1) * 2000) - 1000;
- $this->basis_matrix[1][0] = mt_rand(-1000, 1000);
- $this->basis_matrix[1][1] = mt_rand(-1000, 1000);
- $this->basis_matrix[1][2] = mt_rand(-1000, 1000);
-
- $this->normalize($this->basis_matrix[0]);
- $this->normalize($this->basis_matrix[1]);
- $this->basis_matrix[2] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[1]);
- $this->normalize($this->basis_matrix[2]);
-
- // $this->basis_matrix[1] might not be (probably isn't) orthogonal to $basis_matrix[0]
- $this->basis_matrix[1] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[2]);
- $this->normalize($this->basis_matrix[1]);
-
- // Make sure our cube is facing into the canvas (assuming +z == in)
- for ($i = 0; $i < 3; ++$i)
- {
- if ($this->basis_matrix[$i][2] < 0)
+ for ($i = 0; $i < $ly; $i += 11)
{
- $this->basis_matrix[$i][0] *= -1;
- $this->basis_matrix[$i][1] *= -1;
- $this->basis_matrix[$i][2] *= -1;
+ $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224));
+ imageline($image, 0, $i, $lx, $i, $color);
}
}
- // Force our "z" basis vector to be the one with greatest absolute z value
- $this->x = 0;
- $this->y = 1;
- $this->z = 2;
+ $len = strlen($code);
- // Swap "y" with "z"
- if ($this->basis_matrix[1][2] > $this->basis_matrix[2][2])
+ for ($i = 0, $x = mt_rand(20, 40); $i < $len; $i++)
{
- $this->z = 1;
- $this->y = 2;
- }
+ $text = strtoupper($code[$i]);
+ $angle = mt_rand(-30, 30);
+ $size = mt_rand(20, 40);
+ $y = mt_rand((int)($size * 1.5), (int)($ly - ($size / 7)));
- // Swap "x" with "z"
- if ($this->basis_matrix[0][2] > $this->basis_matrix[$this->z][2])
- {
- $this->x = $this->z;
- $this->z = 0;
- }
+ $color = $func2($image, mt_rand(0, 127), mt_rand(0, 127), mt_rand(0, 127));
+ $shadow = $func2($image, mt_rand(127, 254), mt_rand(127, 254), mt_rand(127, 254));
+ $font = $this->get_font();
- // Still need to determine which of $x,$y are which.
- // wrong orientation if y's y-component is less than it's x-component
- // likewise if x's x-component is less than it's y-component
- // if they disagree, go with the one with the greater weight difference.
- // rotate if positive
- $weight = (abs($this->basis_matrix[$this->x][1]) - abs($this->basis_matrix[$this->x][0])) +
- (abs($this->basis_matrix[$this->y][0]) - abs($this->basis_matrix[$this->y][1]));
+ imagettftext($image, $size, $angle, $x + (int)($size / 15), $y, $shadow, $font, $text);
+ imagettftext($image, $size, $angle, $x, $y - (int)($size / 15), $color, $font, $text);
- // Swap "x" with "y"
- if ($weight > 0)
- {
- list($this->x, $this->y) = array($this->y, $this->x);
+ $x += $size + 4;
}
- $this->abs_x = array($this->basis_matrix[$this->x][0], $this->basis_matrix[$this->x][1]);
- $this->abs_y = array($this->basis_matrix[$this->y][0], $this->basis_matrix[$this->y][1]);
-
- if ($this->abs_x[0] < 0)
- {
- $this->abs_x[0] *= -1;
- $this->abs_x[1] *= -1;
- }
-
- if ($this->abs_y[1] > 0)
- {
- $this->abs_y[0] *= -1;
- $this->abs_y[1] *= -1;
- }
-
- $this->letter = $letter;
- }
-
- /**
- *
- */
- function draw($im, $scale, $xoff, $yoff, $face, $xshadow, $yshadow)
- {
- $origin = array(0, 0, 0);
- $xvec = $this->scale($this->basis_matrix[$this->x], $scale);
- $yvec = $this->scale($this->basis_matrix[$this->y], $scale);
- $face_corner = $this->sum2($xvec, $yvec);
-
- $zvec = $this->scale($this->basis_matrix[$this->z], $scale);
- $x_corner = $this->sum2($xvec, $zvec);
- $y_corner = $this->sum2($yvec, $zvec);
-
- imagefilledpolygon($im, $this->gen_poly($xoff, $yoff, $origin, $xvec, $x_corner, $zvec), 4, $yshadow);
- imagefilledpolygon($im, $this->gen_poly($xoff, $yoff, $origin, $yvec, $y_corner, $zvec), 4, $xshadow);
- imagefilledpolygon($im, $this->gen_poly($xoff, $yoff, $origin, $xvec, $face_corner, $yvec), 4, $face);
- }
-
- /**
- * Draw a character
- */
- function drawchar($scale, $xoff, $yoff, $img, $background, $colors)
- {
- $width = $this->bitmaps['width'];
- $height = $this->bitmaps['height'];
- $bitmap = $this->bitmaps['data'][$this->letter];
-
- $color1 = $colors[array_rand($colors)];
- $color2 = $colors[array_rand($colors)];
-
- $swapx = ($this->basis_matrix[$this->x][0] > 0);
- $swapy = ($this->basis_matrix[$this->y][1] < 0);
-
- for ($y = 0; $y < $height; ++$y)
- {
- for ($x = 0; $x < $width; ++$x)
- {
- $xp = ($swapx) ? ($width - $x - 1) : $x;
- $yp = ($swapy) ? ($height - $y - 1) : $y;
-
- if ($bitmap[$height - $yp - 1][$xp])
- {
- $dx = $this->scale($this->abs_x, ($xp - ($swapx ? ($width / 2) : ($width / 2) - 1)) * $scale);
- $dy = $this->scale($this->abs_y, ($yp - ($swapy ? ($height / 2) : ($height / 2) - 1)) * $scale);
- $xo = $xoff + $dx[0] + $dy[0];
- $yo = $yoff + $dx[1] + $dy[1];
-
- $origin = array(0, 0, 0);
- $xvec = $this->scale($this->basis_matrix[$this->x], $scale);
- $yvec = $this->scale($this->basis_matrix[$this->y], $scale);
- $face_corner = $this->sum2($xvec, $yvec);
-
- $zvec = $this->scale($this->basis_matrix[$this->z], $scale);
- $x_corner = $this->sum2($xvec, $zvec);
- $y_corner = $this->sum2($yvec, $zvec);
-
- imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $xvec, $x_corner,$zvec), 4, $color1);
- imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $yvec, $y_corner,$zvec), 4, $color2);
-
- $face = $this->gen_poly($xo, $yo, $origin, $xvec, $face_corner, $yvec);
-
- imagefilledpolygon($img, $face, 4, $background);
- imagepolygon($img, $face, 4, $color1);
- }
- }
- }
- }
-
- /*
- * return a roughly acceptable range of sizes for rendering with this texttype
- */
- function range()
- {
- return array(5, 10);
- }
-
- /**
- * Vector length
- */
- function vectorlen($vector)
- {
- return sqrt(pow($vector[0], 2) + pow($vector[1], 2) + pow($vector[2], 2));
- }
-
- /**
- * Normalize
- */
- function normalize(&$vector, $length = 1)
- {
- $length = (( $length < 1) ? 1 : $length);
- $length /= $this->vectorlen($vector);
- $vector[0] *= $length;
- $vector[1] *= $length;
- $vector[2] *= $length;
- }
-
- /**
- *
- */
- function cross_product($vector1, $vector2)
- {
- $retval = array(0, 0, 0);
- $retval[0] = (($vector1[1] * $vector2[2]) - ($vector1[2] * $vector2[1]));
- $retval[1] = -(($vector1[0] * $vector2[2]) - ($vector1[2] * $vector2[0]));
- $retval[2] = (($vector1[0] * $vector2[1]) - ($vector1[1] * $vector2[0]));
-
- return $retval;
- }
-
- /**
- *
- */
- function sum($vector1, $vector2)
- {
- return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1], $vector1[2] + $vector2[2]);
- }
-
- /**
- *
- */
- function sum2($vector1, $vector2)
- {
- return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1]);
- }
-
- /**
- *
- */
- function scale($vector, $length)
- {
- if (sizeof($vector) == 2)
- {
- return array($vector[0] * $length, $vector[1] * $length);
- }
-
- return array($vector[0] * $length, $vector[1] * $length, $vector[2] * $length);
- }
-
- /**
- *
- */
- function gen_poly($xoff, $yoff, &$vec1, &$vec2, &$vec3, &$vec4)
- {
- $poly = array();
- $poly[0] = $xoff + $vec1[0];
- $poly[1] = $yoff + $vec1[1];
- $poly[2] = $xoff + $vec2[0];
- $poly[3] = $yoff + $vec2[1];
- $poly[4] = $xoff + $vec3[0];
- $poly[5] = $yoff + $vec3[1];
- $poly[6] = $xoff + $vec4[0];
- $poly[7] = $yoff + $vec4[1];
-
- return $poly;
- }
-
- /**
- * dimensions
- */
- function dimensions($size)
- {
- $xn = $this->scale($this->basis_matrix[$this->x], -($this->bitmaps['width'] / 2) * $size);
- $xp = $this->scale($this->basis_matrix[$this->x], ($this->bitmaps['width'] / 2) * $size);
- $yn = $this->scale($this->basis_matrix[$this->y], -($this->bitmaps['height'] / 2) * $size);
- $yp = $this->scale($this->basis_matrix[$this->y], ($this->bitmaps['height'] / 2) * $size);
-
- $p = array();
- $p[0] = $this->sum2($xn, $yn);
- $p[1] = $this->sum2($xp, $yn);
- $p[2] = $this->sum2($xp, $yp);
- $p[3] = $this->sum2($xn, $yp);
-
- $min_x = $max_x = $p[0][0];
- $min_y = $max_y = $p[0][1];
-
- for ($i = 1; $i < 4; ++$i)
- {
- $min_x = ($min_x > $p[$i][0]) ? $p[$i][0] : $min_x;
- $min_y = ($min_y > $p[$i][1]) ? $p[$i][1] : $min_y;
- $max_x = ($max_x < $p[$i][0]) ? $p[$i][0] : $max_x;
- $max_y = ($max_y < $p[$i][1]) ? $p[$i][1] : $max_y;
- }
-
- return array($min_x, $min_y, $max_x, $max_y);
- }
-}
-
-/**
-* Return bitmaps
-*/
-function captcha_bitmaps()
-{
- return array(
- 'width' => 9,
- 'height' => 15,
- 'data' => array(
- 'A' => array(
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(0,1,1,1,1,1,1,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- ),
- 'B' => array(
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,0),
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,0),
- array(1,1,1,1,1,1,1,0,0),
- ),
- 'C' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- 'D' => array(
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,0),
- array(1,1,1,1,1,1,1,0,0),
- ),
- 'E' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,1,1,1,1,1,1,1,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,1,1,1,1,1,1,1,1),
- ),
- 'F' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- ),
- 'G' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,1,1,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- 'H' => array(
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,1,1,1,1,1,1,1,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- ),
- 'I' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(1,1,1,1,1,1,1,1,1),
- ),
- 'J' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(1,0,0,0,0,1,0,0,0),
- array(1,0,0,0,0,1,0,0,0),
- array(0,1,0,0,1,0,0,0,0),
- array(0,0,1,1,0,0,0,0,0),
- ),
- 'K' => array( // New 'K', supplied by NeoThermic
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,1,0,0),
- array(1,0,0,0,0,1,0,0,0),
- array(1,0,0,0,1,0,0,0,0),
- array(1,0,0,1,0,0,0,0,0),
- array(1,0,1,0,0,0,0,0,0),
- array(1,1,0,0,0,0,0,0,0),
- array(1,0,1,0,0,0,0,0,0),
- array(1,0,0,1,0,0,0,0,0),
- array(1,0,0,0,1,0,0,0,0),
- array(1,0,0,0,0,1,0,0,0),
- array(1,0,0,0,0,0,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- ),
- 'L' => array(
- array(0,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,1,1,1,1,1,1,1,1),
- ),
- 'M' => array(
- array(1,1,0,0,0,0,0,1,1),
- array(1,1,0,0,0,0,0,1,1),
- array(1,0,1,0,0,0,1,0,1),
- array(1,0,1,0,0,0,1,0,1),
- array(1,0,1,0,0,0,1,0,1),
- array(1,0,0,1,0,1,0,0,1),
- array(1,0,0,1,0,1,0,0,1),
- array(1,0,0,1,0,1,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- ),
- 'N' => array(
- array(1,1,0,0,0,0,0,0,1),
- array(1,1,0,0,0,0,0,0,1),
- array(1,0,1,0,0,0,0,0,1),
- array(1,0,1,0,0,0,0,0,1),
- array(1,0,0,1,0,0,0,0,1),
- array(1,0,0,1,0,0,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,0,0,1,0,0,1),
- array(1,0,0,0,0,1,0,0,1),
- array(1,0,0,0,0,0,1,0,1),
- array(1,0,0,0,0,0,1,0,1),
- array(1,0,0,0,0,0,0,1,1),
- array(1,0,0,0,0,0,0,1,1),
- ),
- 'O' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- 'P' => array(
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,0),
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- ),
- 'Q' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,1,0,0,1),
- array(1,0,0,0,0,0,1,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,1),
- ),
- 'R' => array(
- array(1,1,1,1,1,1,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,0),
- array(1,1,1,1,1,1,1,0,0),
- array(1,1,1,0,0,0,0,0,0),
- array(1,0,0,1,0,0,0,0,0),
- array(1,0,0,0,1,0,0,0,0),
- array(1,0,0,0,0,1,0,0,0),
- array(1,0,0,0,0,0,1,0,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- ),
- 'S' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(0,1,0,0,0,0,0,0,0),
- array(0,0,1,1,1,1,1,0,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- 'T' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- ),
- 'U' => array(
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- 'V' => array(
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- ),
- 'W' => array( // New 'W', supplied by MHobbit
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,0,1,0,0,0,1),
- array(1,0,0,1,0,1,0,0,1),
- array(1,0,0,1,0,1,0,0,1),
- array(1,0,0,1,0,1,0,0,1),
- array(1,0,1,0,0,0,1,0,1),
- array(1,0,1,0,0,0,1,0,1),
- array(1,0,1,0,0,0,1,0,1),
- array(1,1,0,0,0,0,0,1,1),
- array(1,1,0,0,0,0,0,1,1),
- ),
- 'X' => array(
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,1,0,0,0,0,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- ),
- 'Y' => array(
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,0,0,1,0,1,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- ),
- 'Z' => array( // New 'Z' supplied by Anon
- array(1,1,1,1,1,1,1,1,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,1,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,1,0,0,0,0,0),
- array(0,0,0,1,0,0,0,0,0),
- array(0,0,1,0,0,0,0,0,0),
- array(0,1,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,1,1,1,1,1,1,1,1),
- ),
- '1' => array(
- array(0,0,0,1,1,0,0,0,0),
- array(0,0,1,0,1,0,0,0,0),
- array(0,1,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,1,1,1,1,1,1,1,0),
- ),
- '2' => array( // New '2' supplied by Anon
- array(0,0,0,1,1,1,0,0,0),
- array(0,0,1,0,0,0,1,0,0),
- array(0,1,0,0,0,0,1,1,0),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,1,1),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,1,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,1,0,0,0,0,0),
- array(0,0,1,0,0,0,0,0,0),
- array(0,1,0,0,0,0,0,0,0),
- array(1,1,1,1,1,1,1,1,1),
- array(0,0,0,0,0,0,0,0,0),
- ),
- '3' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,1,1,0,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- '4' => array(
- array(0,0,0,0,0,0,1,1,0),
- array(0,0,0,0,0,1,0,1,0),
- array(0,0,0,0,1,0,0,1,0),
- array(0,0,0,1,0,0,0,1,0),
- array(0,0,1,0,0,0,0,1,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,1,0),
- array(1,1,1,1,1,1,1,1,1),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,1,0),
- ),
- '5' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(0,1,0,0,0,0,0,0,0),
- array(0,0,1,1,1,1,1,0,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- '6' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,1,1,1,1,0,0),
- array(1,0,1,0,0,0,0,1,0),
- array(1,1,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- '7' => array(
- array(1,1,1,1,1,1,1,1,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,0,1,0),
- array(0,0,0,0,0,0,1,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,0,1,0,0,0),
- array(0,0,0,0,1,0,0,0,0),
- array(0,0,0,1,0,0,0,0,0),
- array(0,0,0,1,0,0,0,0,0),
- array(0,0,1,0,0,0,0,0,0),
- array(0,1,0,0,0,0,0,0,0),
- array(0,1,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- array(1,0,0,0,0,0,0,0,0),
- ),
- '8' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- '9' => array(
- array(0,0,1,1,1,1,1,0,0),
- array(0,1,0,0,0,0,0,1,0),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,1,1),
- array(0,1,0,0,0,0,1,0,1),
- array(0,0,1,1,1,1,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(0,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(1,0,0,0,0,0,0,0,1),
- array(0,1,0,0,0,0,0,1,0),
- array(0,0,1,1,1,1,1,0,0),
- ),
- )
- );
-}
-
-
-/**
-* Load True Type Fonts
-*/
-function captcha_load_ttf_fonts()
-{
- static $load_files = array();
-
- if (sizeof($load_files) > 0)
- {
- return $load_files;
+ // Output image
+ header('Content-Type: image/png');
+ header('Cache-control: no-cache, no-store');
+ imagepng($image);
+ imagedestroy($image);
}
- global $phpbb_root_path;
-
- $dr = opendir($phpbb_root_path . 'includes/captcha/fonts');
- while (false !== ($entry = readdir($dr)))
+ function get_font()
{
- if (strtolower(pathinfo($entry, PATHINFO_EXTENSION)) == 'ttf')
- {
- $load_files[$entry] = $phpbb_root_path . 'includes/captcha/fonts/' . $entry;
- }
- }
- closedir($dr);
-
- return $load_files;
-}
-
-
-/**
-* Return vectors
-*/
-function captcha_vectors()
-{
- return array(
- 'A' => array(
- array('line', 0.00, 0.00, 0.50, 1.00, 1.10714871779, 1.11803398875),
- array('line', 1.00, 0.00, 0.50, 1.00, 2.0344439358, 1.11803398875),
- array('line', 0.25, 0.50, 0.75, 0.50, 0.00, 0.50),
- ),
- 'B' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 1.00, 0.70, 1.00, 0.00, 0.70),
- array('line', 0.00, 0.50, 0.70, 0.50, 0.00, 0.70),
- array('line', 0.00, 0.00, 0.70, 0.00, 0.00, 0.70),
- array('arc', 0.70, 0.75, 0.60, 0.50, 270, 90),
- array('arc', 0.70, 0.25, 0.60, 0.50, 270, 90),
- ),
- 'C' => array(
- array('arc', 0.50, 0.50, 1.00, 1.00, 45, 315),
- ),
- 'D' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 0.00, 0.50, 0.00, 0.00, 0.50),
- array('line', 0.00, 1.00, 0.50, 1.00, 0.00, 0.50),
- array('arc', 0.50, 0.50, 1.00, 1.00, 270, 90),
- ),
- 'E' => array(
- array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00),
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.00, 0.50, 0.50, 0.50, 0.00, 0.50),
- ),
- 'F' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.00, 0.50, 0.50, 0.50, 0.00, 0.50),
- ),
- 'G' => array(
- array('line', 0.50, 0.50, 1.00, 0.50, 0.00, 0.50),
- array('line', 1.00, 0.00, 1.00, 0.50, 1.57079632679, 0.50),
- array('arc', 0.50, 0.50, 1.00, 1.00, 0, 315),
- ),
- 'H' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 1.00, 0.00, 1.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 0.50, 1.00, 0.50, 0.00, 1.00),
- ),
- 'I' => array(
- array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00),
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.50, 0.00, 0.50, 1.00, 1.57079632679, 1.00),
- ),
- 'J' => array(
- array('line', 1.00, 1.00, 1.00, 0.25, -1.57079632679, 0.75),
- array('arc', 0.50, 0.25, 1.00, 0.50, 0, 180),
- ),
- 'K' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 0.50, 1.00, 1.00, 0.463647609001, 1.11803398875),
- array('line', 0.00, 0.50, 1.00, 0.00, -0.463647609001, 1.11803398875),
- ),
- 'L' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00),
- ),
- 'M' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.50, 0.50, 0.00, 1.00, 2.35619449019, 0.707106781187),
- array('line', 0.50, 0.50, 1.00, 1.00, 0.785398163397, 0.707106781187),
- array('line', 1.00, 0.00, 1.00, 1.00, 1.57079632679, 1.00),
- ),
- 'N' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 1.00, 1.00, 0.00, -0.785398163397, 1.41421356237),
- array('line', 1.00, 0.00, 1.00, 1.00, 1.57079632679, 1.00),
- ),
- 'O' => array(
- array('arc', 0.50, 0.50, 1.00, 1.00, 0, 360),
- ),
- 'P' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 1.00, 0.70, 1.00, 0.00, 0.70),
- array('line', 0.00, 0.50, 0.70, 0.50, 0.00, 0.70),
- array('arc', 0.70, 0.75, 0.60, 0.50, 270, 90),
- ),
- 'Q' => array(
- array('line', 0.70, 0.30, 1.00, 0.00, -0.785398163397, 0.424264068712),
- array('arc', 0.50, 0.50, 1.00, 1.00, 0, 360),
- ),
- 'R' => array(
- array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 1.00, 0.70, 1.00, 0.00, 0.70),
- array('line', 0.00, 0.50, 0.70, 0.50, 0.00, 0.70),
- array('line', 0.50, 0.50, 1.00, 0.00, -0.785398163397, 0.707106781187),
- array('arc', 0.70, 0.75, 0.60, 0.50, 270, 90),
- ),
- 'S' => array(
- array('arc', 0.50, 0.75, 1.00, 0.50, 90, 360),
- array('arc', 0.50, 0.25, 1.00, 0.50, 270, 180),
- ),
- 'T' => array(
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.50, 0.00, 0.50, 1.00, 1.57079632679, 1.00),
- ),
- 'U' => array(
- array('line', 0.00, 1.00, 0.00, 0.25, -1.57079632679, 0.75),
- array('line', 1.00, 1.00, 1.00, 0.25, -1.57079632679, 0.75),
- array('arc', 0.50, 0.25, 1.00, 0.50, 0, 180),
- ),
- 'V' => array(
- array('line', 0.00, 1.00, 0.50, 0.00, -1.10714871779, 1.11803398875),
- array('line', 1.00, 1.00, 0.50, 0.00, -2.0344439358, 1.11803398875),
- ),
- 'W' => array(
- array('line', 0.00, 1.00, 0.25, 0.00, -1.32581766367, 1.0307764064),
- array('line', 0.50, 0.50, 0.25, 0.00, -2.0344439358, 0.559016994375),
- array('line', 0.50, 0.50, 0.75, 0.00, -1.10714871779, 0.559016994375),
- array('line', 1.00, 1.00, 0.75, 0.00, -1.81577498992, 1.0307764064),
- ),
- 'X' => array(
- array('line', 0.00, 1.00, 1.00, 0.00, -0.785398163397, 1.41421356237),
- array('line', 0.00, 0.00, 1.00, 1.00, 0.785398163397, 1.41421356237),
- ),
- 'Y' => array(
- array('line', 0.00, 1.00, 0.50, 0.50, -0.785398163397, 0.707106781187),
- array('line', 1.00, 1.00, 0.50, 0.50, -2.35619449019, 0.707106781187),
- array('line', 0.50, 0.50, 0.50, 0.00, -1.57079632679, 0.50),
- ),
- 'Z' => array(
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.00, 0.00, 1.00, 1.00, 0.785398163397, 1.41421356237),
- array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00),
- ),
- '1' => array(
- array('line', 0.00, 0.75, 0.50, 1.00, 0.463647609001, 0.559016994375),
- array('line', 0.50, 0.00, 0.50, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00),
- ),
- '2' => array(
- array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00),
- array('arc', 0.50, 0.70, 1.00, 0.60, 180, 360),
- array('arc', 0.50, 0.70, 1.00, 0.70, 0, 90),
- array('arc', 0.50, 0.00, 1.00, 0.70, 180, 270),
- ),
- '3' => array(
- array('arc', 0.50, 0.75, 1.00, 0.50, 180, 90),
- array('arc', 0.50, 0.25, 1.00, 0.50, 270, 180),
- ),
- '4' => array(
- array('line', 0.70, 0.00, 0.70, 1.00, 1.57079632679, 1.00),
- array('line', 0.00, 0.50, 0.70, 1.00, 0.620249485983, 0.860232526704),
- array('line', 0.00, 0.50, 1.00, 0.50, 0.00, 1.00),
- ),
- '5' => array(
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.00, 1.00, 0.00, 0.60, -1.57079632679, 0.4),
- array('line', 0.00, 0.60, 0.50, 0.60, 0.00, 0.50),
- array('arc', 0.50, 0.30, 1.00, 0.60, 270, 180),
- ),
- '6' => array(
- array('arc', 0.50, 0.50, 1.00, 1.00, 90, 315),
- array('arc', 0.50, 0.30, 0.80, 0.60, 0, 360),
- ),
- '7' => array(
- array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00),
- array('line', 0.50, 0.00, 1.00, 1.00, 1.10714871779, 1.11803398875),
- ),
- '8' => array(
- array('arc', 0.50, 0.75, 1.00, 0.50, 0, 360),
- array('arc', 0.50, 0.25, 1.00, 0.50, 0, 360),
- ),
- '9' => array(
- array('arc', 0.50, 0.50, 1.00, 1.00, 270, 135),
- array('arc', 0.50, 0.70, 0.80, 0.60, 0, 360),
- )
- );
-}
-
-/**
-* @package VC
-*/
-class color_manager
-{
- var $img;
- var $mode;
- var $colors;
- var $named_colors;
- var $named_rgb = array(
- 'red' => array(0xff, 0x00, 0x00),
- 'maroon' => array(0x80, 0x00, 0x00),
- 'yellow' => array(0xff, 0xff, 0x00),
- 'olive' => array(0x80, 0x80, 0x00),
- 'lime' => array(0x00, 0xff, 0x00),
- 'green' => array(0x00, 0x80, 0x00),
- 'aqua' => array(0x00, 0xff, 0xff),
- 'teal' => array(0x00, 0x80, 0x80),
- 'blue' => array(0x00, 0x00, 0xff),
- 'navy' => array(0x00, 0x00, 0x80),
- 'fuchsia' => array(0xff, 0x00, 0xff),
- 'purple' => array(0x80, 0x00, 0x80),
- 'white' => array(0xff, 0xff, 0xff),
- 'silver' => array(0xc0, 0xc0, 0xc0),
- 'gray' => array(0x80, 0x80, 0x80),
- 'black' => array(0x00, 0x00, 0x00),
- );
-
- /**
- * Create the color manager, link it to
- * the image resource
- */
- function color_manager($img, $background = false, $mode = 'ahsv')
- {
- $this->img = $img;
- $this->mode = $mode;
- $this->colors = array();
- $this->named_colors = array();
- if ($background !== false)
- {
- $bg = $this->allocate_named('background', $background);
- imagefill($this->img, 0, 0, $bg);
- }
- }
-
- /**
- * Lookup a named color resource
- */
- function r($named_color)
- {
- if (isset($this->named_colors[$named_color]))
- {
- return $this->named_colors[$named_color];
- }
- if (isset($this->named_rgb[$named_color]))
- {
- return $this->allocate_named($named_color, $this->named_rgb[$named_color], 'rgb');
- }
- return false;
- }
-
- /**
- * Assign a name to a color resource
- */
- function name_color($name, $resource)
- {
- $this->named_colors[$name] = $resource;
- }
-
- /**
- * random color resource
- */
- function r_rand($colors)
- {
- return $colors[array_rand($colors)];
- }
-
- /**
- * names and allocates a color resource
- */
- function allocate_named($name, $color, $mode = false)
- {
- $resource = $this->allocate($color, $mode);
- if ($resource !== false)
- {
- $this->name_color($name, $resource);
- }
- return $resource;
- }
-
- /**
- * allocates a specified color into the image
- */
- function allocate($color, $mode = false)
- {
- if ($mode === false)
- {
- $mode = $this->mode;
- }
- if (!is_array($color))
- {
- if (isset($this->named_rgb[$color]))
- {
- return $this->allocate_named($color, $this->named_rgb[$color], 'rgb');
- }
- if (!is_int($color))
- {
- return false;
- }
- $mode = 'rgb';
- $color = array(
- 255 & ($color >> 16),
- 255 & ($color >> 8),
- 255 & $color,
- );
- }
-
- if (isset($color['mode']))
- {
- $mode = $color['mode'];
- unset($color['mode']);
- }
- if (isset($color['random']))
- {
- unset($color['random']);
- // everything else is params
- return $this->random_color($color, $mode);
- }
-
- $rgb = color_manager::model_convert($color, $mode, 'rgb');
- $store = ($this->mode == 'rgb') ? $rgb : color_manager::model_convert($color, $mode, $this->mode);
- $resource = imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]);
-
- $this->colors[$resource] = $store;
-
- return $resource;
- }
-
- /**
- * randomly generates a color, with optional params
- */
- function random_color($params = array(), $mode = false)
- {
- if ($mode === false)
- {
- $mode = $this->mode;
- }
- switch ($mode)
- {
- case 'rgb':
-
- // @TODO random rgb generation. do we intend to do this, or is it just too tedious?
-
- break;
-
- case 'ahsv':
- case 'hsv':
- default:
-
- $default_params = array(
- 'hue_bias' => false, // degree / 'r'/'g'/'b'/'c'/'m'/'y' /'o'
- 'hue_range' => false, // if hue bias, then difference range +/- from bias
- 'min_saturation' => 30, // 0 - 100
- 'max_saturation' => 100, // 0 - 100
- 'min_value' => 30, // 0 - 100
- 'max_value' => 100, // 0 - 100
- );
-
- $alt = ($mode == 'ahsv');
-
- $params = array_merge($default_params, $params);
-
- $min_hue = 0;
- $max_hue = 359;
- $min_saturation = max(0, $params['min_saturation']);
- $max_saturation = min(100, $params['max_saturation']);
- $min_value = max(0, $params['min_value']);
- $max_value = min(100, $params['max_value']);
-
- if ($params['hue_bias'] !== false)
- {
- if (is_numeric($params['hue_bias']))
- {
- $h = intval($params['hue_bias']) % 360;
- }
- else
- {
- switch ($params['hue_bias'])
- {
- case 'o':
- $h = $alt ? 60 : 30;
- break;
-
- case 'y':
- $h = $alt ? 120 : 60;
- break;
-
- case 'g':
- $h = $alt ? 180 : 120;
- break;
-
- case 'c':
- $h = $alt ? 210 : 180;
- break;
-
- case 'b':
- $h = 240;
- break;
-
- case 'm':
- $h = 300;
- break;
-
- case 'r':
- default:
- $h = 0;
- break;
- }
- }
-
- $min_hue = $h + 360;
- $max_hue = $h + 360;
-
- if ($params['hue_range'])
- {
- $min_hue -= min(180, $params['hue_range']);
- $max_hue += min(180, $params['hue_range']);
- }
- }
-
- $h = mt_rand($min_hue, $max_hue);
- $s = mt_rand($min_saturation, $max_saturation);
- $v = mt_rand($min_value, $max_value);
-
- return $this->allocate(array($h, $s, $v), $mode);
-
- break;
- }
- }
+ static $fonts = array();
- function color_scheme($resource, $scheme, $include_original = true)
- {
- $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'hsv');
- if (($pre = $this->r($resource)) !== false)
- {
- $resource = $pre;
- }
- $color = color_manager::model_convert($this->colors[$resource], $this->mode, $mode);
- $results = $include_original ? array($resource) : array();
-
- switch ($scheme)
+ if (!sizeof($fonts))
{
- case 'complement':
-
- $color2 = $color;
- $color2[0] += 180;
- $results[] = $this->allocate($color2, $mode);
-
- break;
-
- case 'triadic':
-
- $color2 = $color3 = $color;
- $color2[0] += 120;
- $color3[0] += 240;
- $results[] = $this->allocate($color2, $mode);
- $results[] = $this->allocate($color3, $mode);
-
- break;
-
- case 'tetradic':
-
- $color2 = $color3 = $color4 = $color;
- $color2[0] += 30;
- $color3[0] += 180;
- $color4[0] += 210;
- $results[] = $this->allocate($color2, $mode);
- $results[] = $this->allocate($color3, $mode);
- $results[] = $this->allocate($color4, $mode);
-
- break;
-
- case 'analogous':
-
- $color2 = $color3 = $color;
- $color2[0] += 30;
- $color3[0] += 330;
- $results[] = $this->allocate($color2, $mode);
- $results[] = $this->allocate($color3, $mode);
-
- break;
- }
- return $results;
- }
+ global $phpbb_root_path;
- function mono_range($resource, $type = 'both', $count = 5, $include_original = true)
- {
- if (is_array($resource))
- {
- $results = array();
- for ($i = 0, $size = sizeof($resource); $i < $size; ++$i)
+ $dr = opendir($phpbb_root_path . 'includes/captcha/fonts');
+ while (false !== ($entry = readdir($dr)))
{
- $results = array_merge($results, $this->mono_range($resource[$i], $type, $count, $include_original));
- }
- return $results;
- }
- $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv');
- if (($pre = $this->r($resource)) !== false)
- {
- $resource = $pre;
- }
- $color = color_manager::model_convert($this->colors[$resource], $this->mode, $mode);
-
- $results = array();
- if ($include_original)
- {
- $results[] = $resource;
- $count--;
- }
-
- switch ($type)
- {
- case 'saturation':
-
- $pivot = $color[1];
- $num_below = intval(($pivot * $count) / 100);
- $num_above = $count - $num_below;
-
- for ($i = $num_above; $i > 0; --$i)
- {
- $color[1] = (($i * 100) + (($num_above - $i) * $pivot)) / $num_above;
- $results[] = $this->allocate($color, $mode);
- }
-
- ++$num_below;
-
- for ($i = $num_below - 1; $i > 0; --$i)
- {
- $color[1] = ($i * $pivot) / $num_below;;
- $results[] = $this->allocate($color, $mode);
- }
-
- return $results;
-
- break;
-
- case 'value':
-
- $pivot = $color[2];
- $num_below = intval(($pivot * $count) / 100);
- $num_above = $count - $num_below;
-
- for ($i = $num_above; $i > 0; --$i)
- {
- $color[2] = (($i * 100) + (($num_above - $i) * $pivot)) / $num_above;
- $results[] = $this->allocate($color, $mode);
- }
-
- ++$num_below;
-
- for ($i = $num_below - 1; $i > 0; --$i)
- {
- $color[2] = ($i * $pivot) / $num_below;;
- $results[] = $this->allocate($color, $mode);
- }
-
- return $results;
-
- break;
-
- case 'both':
-
- // This is a hard problem. I chicken out and do an even triangle
- // the problem is that it disregards the original saturation and value,
- // and as such a generated result might come arbitrarily close to our original value.
- $length = ceil(sqrt($count * 2));
- for ($i = $length; $i > 0; --$i)
- {
- for ($j = $i; $j > 0; --$j)
- {
- $color[1] = ($i * 100) / $length;
- $color[2] = ($j * 100) / $i;
- $results[] = $this->allocate($color, $mode);
- --$count;
- if (!$count)
- {
- return $results;
- }
- }
- }
-
- return $results;
-
- break;
- }
-
- return false;
- }
-
- function is_dark($resource)
- {
- $color = (($pre = $this->r($resource)) !== false) ? $this->colors[$pre] : $this->colors[$resource];
- switch($this->mode)
- {
- case 'ahsv':
- case 'hsv':
-
- return ($color[2] <= 50);
-
- break;
-
- case 'rgb':
-
- return (max($color[0], $color[1], $color[2]) <= 128);
-
- break;
- }
- return false;
- }
-
- /**
- * Convert from one color model to another
- *
- * note: properly following coding standards here yields unweildly amounts of whitespace, rendering this less than easily readable
- *
- */
- function model_convert($color, $from_model, $to_model)
- {
- if ($from_model == $to_model)
- {
- return $color;
- }
- switch ($to_model)
- {
- case 'hsv':
- switch($from_model)
- {
- case 'ahsv':
- return color_manager::ah2h($color);
- break;
-
- case 'rgb':
- return color_manager::rgb2hsv($color);
- break;
- }
- break;
-
- case 'ahsv':
- switch($from_model)
- {
- case 'hsv':
- return color_manager::h2ah($color);
- break;
-
- case 'rgb':
- return color_manager::h2ah(color_manager::rgb2hsv($color));
- break;
- }
- break;
-
- case 'rgb':
- switch($from_model)
+ if (strtolower(pathinfo($entry, PATHINFO_EXTENSION)) == 'ttf')
{
- case 'hsv':
- return color_manager::hsv2rgb($color);
- break;
-
- case 'ahsv':
- return color_manager::hsv2rgb(color_manager::ah2h($color));
- break;
+ $fonts[] = $phpbb_root_path . 'includes/captcha/fonts/' . $entry;
}
- break;
- }
- return false;
- }
-
- /**
- * Slightly altered from wikipedia's algorithm
- */
- function hsv2rgb($hsv)
- {
- color_manager::normalize_hue($hsv[0]);
- $h = $hsv[0];
- $s = min(1, max(0, $hsv[1] / 100));
- $v = min(1, max(0, $hsv[2] / 100));
-
- $hi = floor($hsv[0] / 60); // calculate hue sector
-
- $p = $v * (1 - $s); // calculate opposite color
- $f = ($h / 60) - $hi; // calculate distance between hex vertices
- if (!($hi & 1)) // coming in or going out?
- {
- $f = 1 - $f;
- }
- $q = $v * (1 - ($f * $s)); // calculate adjacent color
-
- switch ($hi)
- {
- case 0:
- $rgb = array($v, $q, $p);
- break;
-
- case 1:
- $rgb = array($q, $v, $p);
- break;
-
- case 2:
- $rgb = array($p, $v, $q);
- break;
-
- case 3:
- $rgb = array($p, $q, $v);
- break;
-
- case 4:
- $rgb = array($q, $p, $v);
- break;
-
- case 5:
- $rgb = array($v, $p, $q);
- break;
-
- default:
- return array(0, 0, 0);
- break;
- }
- return array(255 * $rgb[0], 255 * $rgb[1], 255 * $rgb[2]);
- }
-
- /**
- * (more than) Slightly altered from wikipedia's algorithm
- */
- function rgb2hsv($rgb)
- {
- $r = min(255, max(0, $rgb[0]));
- $g = min(255, max(0, $rgb[1]));
- $b = min(255, max(0, $rgb[2]));
- $max = max($r, $g, $b);
- $min = min($r, $g, $b);
-
- $v = $max / 255;
- $s = (!$max) ? 0 : 1 - ($min / $max);
- $h = $max - $min; // if max - min is 0, we want hue to be 0 anyway.
- if ($h)
- {
- switch ($max)
- {
- case $g:
- $h = 120 + (60 * ($b - $r) / $h);
- break;
-
- case $b:
- $h = 240 + (60 * ($r - $g) / $h);
- break;
-
- case $r:
- $h = 360 + (60 * ($g - $b) / $h);
- break;
}
+ closedir($dr);
}
- color_manager::normalize_hue($h);
- return array($h, $s * 100, $v * 100);
- }
-
- /**
- * Bleh
- */
- function normalize_hue(&$hue)
- {
- $hue %= 360;
- if ($hue < 0)
- {
- $hue += 360;
- }
- }
-
- /**
- * Alternate hue to hue
- */
- function ah2h($ahue)
- {
- if (is_array($ahue))
- {
- $ahue[0] = color_manager::ah2h($ahue[0]);
- return $ahue;
- }
- color_manager::normalize_hue($ahue);
- if ($ahue >= 240) // blue through red is already ok
- {
- return $ahue;
- }
- if ($ahue >= 180) // ahue green is at 180
- {
- // return (240 - (2 * (240 - $ahue)));
- return (2 * $ahue) - 240; // equivalent
- }
- if ($ahue >= 120) // ahue yellow is at 120 (RYB rather than RGB)
- {
- return $ahue - 60;
- }
- return $ahue / 2;
- }
- /**
- * hue to Alternate hue
- */
- function h2ah($hue)
- {
- if (is_array($hue))
- {
- $hue[0] = color_manager::h2ah($hue[0]);
- return $hue;
- }
- color_manager::normalize_hue($hue);
- if ($hue >= 240) // blue through red is already ok
- {
- return $hue;
- }
- else if ($hue <= 60)
- {
- return $hue * 2;
- }
- else if ($hue <= 120)
- {
- return $hue + 60;
- }
- else
- {
- return ($hue + 240) / 2;
- }
- }
-}
-
-function vector_distance(&$char, $x, $y, $range = 0.1)
-{
- $distance = $range + 1;
- foreach ($char AS $vector)
- {
- $d = $range + 1;
- switch ($vector[0])
- {
- case 'arc':
-
- $dx = $x - $vector[1];
- $dy = -($y - $vector[2]); //because our arcs are upside-down....
- if (abs($dx) > abs($dy))
- {
- $phi = rad2deg(atan(($dy * $vector[3])/($dx * $vector[4])));
- $phi += ($dx < 0) ? 180 : 360;
- $phi %= 360;
- }
- else
- {
- $phi = 90 - rad2deg(atan(($dx * $vector[4])/($dy * $vector[3])));
- $phi += ($dy < 0) ? 180 : 360;
- $phi %= 360;
- }
-
- $internal = $vector[6] > $vector[5]; //external wraps over the 360 point
- $low = $phi >= $vector[5]; //phi is above our low range
- $high = $phi <= $vector[6]; //phi is below our high range.
- if ($internal ? ($low && $high) : ($low || $high)) //if it wraps, it can only be one or the other
- {
- $radphi = deg2rad($phi); // i'm awesome. or not.
- $px = cos($radphi) * 0.5 * $vector[3];
- $py = sin($radphi) * 0.5 * $vector[4];
- $d = sqrt(pow($px - $dx, 2) + pow($py - $dy, 2));
- }
-
- break;
-
- case 'line':
-
- $bx = $x - $vector[1];
- $by = $y - $vector[2];
- $dx = cos($vector[5]);
- $dy = sin($vector[5]);
- $r = ($by * $dx) - ($bx * $dy);
- if ($r < $range && $r > -$range)
- {
- if (abs($dx) > abs($dy))
- {
- $s = (($bx + ($dy * $r)) / $dx);
- }
- else
- {
- $s = (($by + ($dx * $r)) / $dy);
- }
- if ($s > -$range)
- {
- if ($s < 0)
- {
- $d = sqrt(pow($s, 2) + pow($r, 2));
- }
- elseif ($s < $vector[6])
- {
- $d = $r;
- }
- elseif ($s < $vector[6] + $range)
- {
- $d = sqrt(pow($s - $vector[6], 2) + pow($r, 2));
- }
- }
- }
-
- break;
- }
- $distance = min($distance, abs($d));
+ return $fonts[array_rand($fonts)];
}
- return $distance;
}
?> \ No newline at end of file
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index c30bd867ae..1dc036b4b6 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -612,6 +612,14 @@ else
}
}
+if (!function_exists('htmlspecialchars_decode'))
+{
+ function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
+ {
+ return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
+ }
+}
+
// functions used for building option fields
/**
diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php
index 70c5b2bae3..c85802dafb 100644
--- a/phpBB/includes/functions_admin.php
+++ b/phpBB/includes/functions_admin.php
@@ -2430,7 +2430,7 @@ function get_database_size()
{
if ($table_prefix != '')
{
- if (strstr($row['Name'], $table_prefix))
+ if (strpos($row['Name'], $table_prefix) !== false)
{
$database_size += $row['Data_length'] + $row['Index_length'];
}
diff --git a/phpBB/includes/functions_compress.php b/phpBB/includes/functions_compress.php
index cbada8a76d..93a3547e21 100644
--- a/phpBB/includes/functions_compress.php
+++ b/phpBB/includes/functions_compress.php
@@ -478,17 +478,17 @@ class compress_tar extends compress
// Run through the file and grab directory entries
while ($buffer = $fzread($this->fp, 512))
{
- $tmp = unpack("A6magic", substr($buffer, 257, 6));
+ $tmp = unpack('A6magic', substr($buffer, 257, 6));
if (trim($tmp['magic']) == 'ustar')
{
- $tmp = unpack("A100name", $buffer);
+ $tmp = unpack('A100name', $buffer);
$filename = trim($tmp['name']);
- $tmp = unpack("Atype", substr($buffer, 156, 1));
+ $tmp = unpack('Atype', substr($buffer, 156, 1));
$filetype = (int) trim($tmp['type']);
- $tmp = unpack("A12size", substr($buffer, 124, 12));
+ $tmp = unpack('A12size', substr($buffer, 124, 12));
$filesize = octdec((int) trim($tmp['size']));
if ($filetype == 5)
@@ -508,12 +508,12 @@ class compress_tar extends compress
{
trigger_error("Could not create directory $folder");
}
- @chmod("$str", 0777);
+ @chmod($str, 0777);
}
}
}
}
- else if($filesize != 0 && ($filetype == 0 || $filetype == "\0"))
+ else if ($filesize != 0 && ($filetype == 0 || $filetype == "\0"))
{
// Write out the files
if (!($fp = fopen("$dst$filename", 'wb')))
@@ -523,7 +523,7 @@ class compress_tar extends compress
@chmod("$dst$filename", 0777);
// Grab the file contents
- fwrite($fp, $fzread($this->fp, $filesize + 512 - $filesize % 512), $filesize);
+ fwrite($fp, $fzread($this->fp, ($filesize + 511) &~ 511), $filesize);
fclose($fp);
}
}
@@ -542,7 +542,7 @@ class compress_tar extends compress
$fzwrite = ($this->isbz && function_exists('bzwrite')) ? 'bzwrite' : (($this->isgz && @extension_loaded('zlib')) ? 'gzwrite' : 'fwrite');
// Symbolizes that there are no more files
- $fzwrite($this->fp, pack("a512", ""));
+ $fzwrite($this->fp, str_repeat("\0", 512));
}
$fzclose($this->fp);
@@ -560,37 +560,37 @@ class compress_tar extends compress
// This is the header data, it contains all the info we know about the file or folder that we are about to archive
$header = '';
- $header .= pack("a100", $name); // file name
- $header .= pack("a8", sprintf("%07o", $stat[2])); // file mode
- $header .= pack("a8", sprintf("%07o", $stat[4])); // owner id
- $header .= pack("a8", sprintf("%07o", $stat[5])); // group id
- $header .= pack("a12", sprintf("%011o", $stat[7])); // file size
- $header .= pack("a12", sprintf("%011o", $stat[9])); // last mod time
+ $header .= pack('a100', $name); // file name
+ $header .= pack('a8', sprintf("%07o", $stat[2])); // file mode
+ $header .= pack('a8', sprintf("%07o", $stat[4])); // owner id
+ $header .= pack('a8', sprintf("%07o", $stat[5])); // group id
+ $header .= pack('a12', sprintf("%011o", $stat[7])); // file size
+ $header .= pack('a12', sprintf("%011o", $stat[9])); // last mod time
// Checksum
$checksum = 0;
for ($i = 0; $i < 148; $i++)
{
- $checksum += ord(substr($header, $i, 1));
+ $checksum += ord($header[$i]);
}
// We precompute the rest of the hash, this saves us time in the loop and allows us to insert our hash without resorting to string functions
$checksum += 2415 + (($is_dir) ? 53 : 0);
- $header .= pack("a8", sprintf("%07o", $checksum)); // checksum
- $header .= pack("a1", $typeflag); // link indicator
- $header .= pack("a100", ''); // name of linked file
- $header .= pack("a6", 'ustar'); // ustar indicator
- $header .= pack("a2", '00'); // ustar version
- $header .= pack("a32", 'Unknown'); // owner name
- $header .= pack("a32", 'Unknown'); // group name
- $header .= pack("a8", ''); // device major number
- $header .= pack("a8", ''); // device minor number
- $header .= pack("a155", ''); // filename prefix
- $header .= pack("a12", ''); // end
+ $header .= pack('a8', sprintf("%07o", $checksum)); // checksum
+ $header .= pack('a1', $typeflag); // link indicator
+ $header .= pack('a100', ''); // name of linked file
+ $header .= pack('a6', 'ustar'); // ustar indicator
+ $header .= pack('a2', '00'); // ustar version
+ $header .= pack('a32', 'Unknown'); // owner name
+ $header .= pack('a32', 'Unknown'); // group name
+ $header .= pack('a8', ''); // device major number
+ $header .= pack('a8', ''); // device minor number
+ $header .= pack('a155', ''); // filename prefix
+ $header .= pack('a12', ''); // end
// This writes the entire file in one shot. Header, followed by data and then null padded to a multiple of 512
- $fzwrite($this->fp, $header . (($stat[7] !== 0 && !$is_dir) ? $data . (($stat[7] % 512 > 0) ? str_repeat("\0", 512 - $stat[7] % 512) : '') : ''));
+ $fzwrite($this->fp, $header . (($stat[7] !== 0 && !$is_dir) ? $data . str_repeat("\0", (($stat[7] + 511) &~ 511) - $stat[7]) : ''));
unset($data);
}
diff --git a/phpBB/includes/ucp/ucp_confirm.php b/phpBB/includes/ucp/ucp_confirm.php
index e40e89cbf5..087a186fa7 100644
--- a/phpBB/includes/ucp/ucp_confirm.php
+++ b/phpBB/includes/ucp/ucp_confirm.php
@@ -54,46 +54,9 @@ class ucp_confirm
exit;
}
- // Some people might want the olde style CAPTCHA even if they have GD enabled, this also saves us from people who have GD but no TTF
- $policy_modules = array('policy_entropy', 'policy_3dbitmap');
-
- if (function_exists('imagettfbbox') && function_exists('imagettftext'))
- {
- $policy_modules = array_merge($policy_modules, array('policy_overlap', 'policy_shape', 'policy_cells', 'policy_stencil', 'policy_composite'));
- }
-
- foreach ($policy_modules as $key => $name)
- {
- if ($config[$name] === '0')
- {
- unset($policy_modules[$key]);
- }
- }
-
- $policy = '';
- if (@extension_loaded('gd') && sizeof($policy_modules))
+ if ($config['captcha_gd'])
{
- $change_lang = request_var('change_lang', '');
-
- if ($change_lang)
- {
- $change_lang = basename($change_lang);
-
- if (file_exists($phpbb_root_path . 'language/' . $change_lang . '/'))
- {
- $user->lang_name = $lang = $change_lang;
- $user->lang_path = $phpbb_root_path . 'language/' . $lang . '/';
- $user->lang = array();
- $user->add_lang(array('common', 'ucp'));
- }
- else
- {
- $change_lang = '';
- }
- }
-
include($phpbb_root_path . 'includes/captcha/captcha_gd.' . $phpEx);
- $policy = $policy_modules[array_rand($policy_modules)];
}
else
{
@@ -101,7 +64,7 @@ class ucp_confirm
}
$captcha = new captcha();
- $captcha->execute($row['code'], $policy);
+ $captcha->execute($row['code']);
exit;
}
}
diff --git a/phpBB/includes/utf/utf_normalizer.php b/phpBB/includes/utf/utf_normalizer.php
index d8e0ba0fa6..0d1d74539a 100644
--- a/phpBB/includes/utf/utf_normalizer.php
+++ b/phpBB/includes/utf/utf_normalizer.php
@@ -27,1181 +27,1055 @@ define('UTF8_CJK_LAST', "\xE9\xBE\xBB");
define('UTF8_CJK_B_FIRST', "\xF0\xA0\x80\x80");
define('UTF8_CJK_B_LAST', "\xF0\xAA\x9B\x96");
+// Unset global variables
+unset($GLOBALS['utf_jamo_index'], $GLOBALS['utf_jamo_type'], $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_combining_class'], $GLOBALS['utf_canonical_comp'], $GLOBALS['utf_canonical_decomp'], $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']);
+
+// NFC_QC and NFKC_QC values
+define('UNICODE_QC_MAYBE', 0);
+define('UNICODE_QC_NO', 1);
+
+// Contains all the ASCII characters appearing in UTF-8, sorted by frequency
+define('UTF8_ASCII_RANGE', "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F");
+
+// Contains all the tail bytes that can appear in the composition of a UTF-8 char
+define('UTF8_TRAILING_BYTES', "\xA9\xA0\xA8\x80\xAA\x99\xA7\xBB\xAB\x89\x94\x82\xB4\xA2\xAE\x83\xB0\xB9\xB8\x93\xAF\xBC\xB3\x81\xA4\xB2\x9C\xA1\xB5\xBE\xBD\xBA\x98\xAD\xB1\x84\x95\xA6\xB6\x88\x8D\x90\xB7\xBF\x92\x85\xA5\x97\x8C\x86\xA3\x8E\x9F\x8F\x87\x91\x9D\xAC\x9E\x8B\x96\x9B\x8A\x9A");
+
+// Constants used by the Hangul [de]composition algorithms
+define('UNICODE_HANGUL_SBASE', 0xAC00);
+define('UNICODE_HANGUL_LBASE', 0x1100);
+define('UNICODE_HANGUL_VBASE', 0x1161);
+define('UNICODE_HANGUL_TBASE', 0x11A7);
+define('UNICODE_HANGUL_SCOUNT', 11172);
+define('UNICODE_HANGUL_LCOUNT', 19);
+define('UNICODE_HANGUL_VCOUNT', 21);
+define('UNICODE_HANGUL_TCOUNT', 28);
+define('UNICODE_HANGUL_NCOUNT', 588);
+define('UNICODE_JAMO_L', 0);
+define('UNICODE_JAMO_V', 1);
+define('UNICODE_JAMO_T', 2);
-// Wrapper for the utfnormal extension, ICU wrapper
-if (function_exists('utf8_normalize'))
+/**
+* Unicode normalization routines
+*
+* @package phpBB3
+*/
+class utf_normalizer
{
- define('UNORM_NONE', 1);
- define('UNORM_NFD', 2);
- define('UNORM_NFKD', 3);
- define('UNORM_NFC', 4);
- define('UNORM_NFKC', 5);
- define('UNORM_FCD', 6);
- define('UNORM_DEFAULT', UNORM_NFC);
-
/**
- * utf_normalizer class for the utfnormal extension
+ * Validate, cleanup and normalize a string
+ *
+ * The ultimate convenience function! Clean up invalid UTF-8 sequences,
+ * and convert to Normal Form C, canonical composition.
*
- * @ignore
- * @package phpBB3
+ * @param string $str The dirty string
+ * @return string The same string, all shiny and cleaned-up
*/
- class utf_normalizer
+ function cleanup($str)
{
- function cleanup($str)
- {
- /**
- * The string below is the list of all autorized characters, sorted by
- * frequency in latin text
- */
- $pos = strspn($str, "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x0D");
+ // The string below is the list of all autorized characters, sorted by frequency in latin text
+ $pos = strspn($str, "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x0D");
+ $len = strlen($str);
- if (!isset($str[$pos]))
- {
- // ASCII strings with no special chars return immediately
- return $str;
- }
+ if ($pos == $len)
+ {
+ // ASCII strings with no special chars return immediately
+ return $str;
+ }
- // Check if there is potentially a U+FFFE or U+FFFF char (UTF sequence 0xEFBFBE or 0xEFBFBF) and replace them
- // Note: we start searching at position $pos
- if (is_int(strpos($str, "\xEF\xBF", $pos)))
- {
- $str = str_replace(
- array("\xEF\xBF\xBE", "\xEF\xBF\xBF"),
- array(UTF8_REPLACEMENT, UTF8_REPLACEMENT),
- $str
- );
- }
+ // Note: we do not check for $GLOBALS['utf_canonical_decomp']. It is assumed they are always loaded together
+ if (!isset($GLOBALS['utf_nfc_qc']))
+ {
+ global $phpbb_root_path, $phpEx;
+ include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx);
+ }
- // Replace any byte in the range 0x00..0x1F, except for \r, \n and \t
- // We replace those characters with a 0xFF byte, which is illegal in
- // UTF-8 and will in turn be replaced with a Unicode replacement char
- $str = strtr(
+ // Replace any byte in the range 0x00..0x1F, except for \r, \n and \t
+ // We replace those characters with a 0xFF byte, which is illegal in UTF-8 and will in turn be replaced with a UTF replacement char
+ return utf_normalizer::recompose(
+ strtr(
$str,
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- );
+ ),
+ $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp']
+ );
+ }
- // As per the original implementation, "the UnicodeString constructor fails if the string ends with a head byte".
- // Therefore, if the string ends with a leading byte we replace it with 0xFF, which is illegal too and will be
- // replaced with a Unicode replacement character
- if (substr($str, -1) >= "\xC0")
- {
- $str[strlen($str) - 1] = "\xFF";
- }
+ /**
+ * Validate and normalize a UTF string to NFC
+ *
+ * @param string $str Unchecked UTF string
+ * @return string The string, validated and in normal form
+ */
+ function nfc($str)
+ {
+ $pos = strspn($str, UTF8_ASCII_RANGE);
+ $len = strlen($str);
- return utf8_normalize($str, UNORM_NFC);
+ if ($pos == $len)
+ {
+ // ASCII strings return immediately
+ return $str;
}
- function nfc($str)
+ if (!isset($GLOBALS['utf_nfc_qc']))
{
- return utf8_normalize($str, UNORM_NFC);
+ global $phpbb_root_path, $phpEx;
+ include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx);
}
- function nfkc($str)
+ return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp']);
+ }
+
+ /**
+ * Validate and normalize a UTF string to NFKC
+ *
+ * @param string $str Unchecked UTF string
+ * @return string The string, validated and in normal form
+ */
+ function nfkc($str)
+ {
+ $pos = strspn($str, UTF8_ASCII_RANGE);
+ $len = strlen($str);
+
+ if ($pos == $len)
{
- return utf8_normalize($str, UNORM_NFKC);
+ // ASCII strings return immediately
+ return $str;
}
- function nfd($str)
+ if (!isset($GLOBALS['utf_nfkc_qc']))
{
- return utf8_normalize($str, UNORM_NFD);
+ global $phpbb_root_path, $phpEx;
+ include($phpbb_root_path . 'includes/utf/data/utf_nfkc_qc.' . $phpEx);
}
- function nfkd($str)
+ if (!isset($GLOBALS['utf_canonical_comp']))
{
- return utf8_normalize($str, UNORM_NFKD);
+ global $phpbb_root_path, $phpEx;
+ include($phpbb_root_path . 'includes/utf/data/utf_canonical_comp.' . $phpEx);
}
+
+ return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']);
}
-}
-else
-{
- // This block will NOT be loaded if the utfnormal extension is
-
- // Unset global variables
- unset($GLOBALS['utf_jamo_index'], $GLOBALS['utf_jamo_type'], $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_combining_class'], $GLOBALS['utf_canonical_comp'], $GLOBALS['utf_canonical_decomp'], $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']);
-
- // NFC_QC and NFKC_QC values
- define('UNICODE_QC_MAYBE', 0);
- define('UNICODE_QC_NO', 1);
-
- // Contains all the ASCII characters appearing in UTF-8, sorted by frequency
- define('UTF8_ASCII_RANGE', "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F");
-
- // Contains all the tail bytes that can appear in the composition of a UTF-8 char
- define('UTF8_TRAILING_BYTES', "\xA9\xA0\xA8\x80\xAA\x99\xA7\xBB\xAB\x89\x94\x82\xB4\xA2\xAE\x83\xB0\xB9\xB8\x93\xAF\xBC\xB3\x81\xA4\xB2\x9C\xA1\xB5\xBE\xBD\xBA\x98\xAD\xB1\x84\x95\xA6\xB6\x88\x8D\x90\xB7\xBF\x92\x85\xA5\x97\x8C\x86\xA3\x8E\x9F\x8F\x87\x91\x9D\xAC\x9E\x8B\x96\x9B\x8A\x9A");
-
- // Constants used by the Hangul [de]composition algorithms
- define('UNICODE_HANGUL_SBASE', 0xAC00);
- define('UNICODE_HANGUL_LBASE', 0x1100);
- define('UNICODE_HANGUL_VBASE', 0x1161);
- define('UNICODE_HANGUL_TBASE', 0x11A7);
- define('UNICODE_HANGUL_SCOUNT', 11172);
- define('UNICODE_HANGUL_LCOUNT', 19);
- define('UNICODE_HANGUL_VCOUNT', 21);
- define('UNICODE_HANGUL_TCOUNT', 28);
- define('UNICODE_HANGUL_NCOUNT', 588);
- define('UNICODE_JAMO_L', 0);
- define('UNICODE_JAMO_V', 1);
- define('UNICODE_JAMO_T', 2);
/**
- * Unicode normalization routines
+ * Validate and normalize a UTF string to NFD
*
- * @package phpBB3
+ * @param string $str Unchecked UTF string
+ * @return string The string, validated and in normal form
*/
- class utf_normalizer
+ function nfd($str)
{
- /**
- * Validate, cleanup and normalize a string
- *
- * The ultimate convenience function! Clean up invalid UTF-8 sequences,
- * and convert to Normal Form C, canonical composition.
- *
- * @param string $str The dirty string
- * @return string The same string, all shiny and cleaned-up
- */
- function cleanup($str)
- {
- // The string below is the list of all autorized characters, sorted by frequency in latin text
- $pos = strspn($str, "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x0D");
- $len = strlen($str);
-
- if ($pos == $len)
- {
- // ASCII strings with no special chars return immediately
- return $str;
- }
+ $pos = strspn($str, UTF8_ASCII_RANGE);
+ $len = strlen($str);
- // Note: we do not check for $GLOBALS['utf_canonical_decomp']. It is assumed they are always loaded together
- if (!isset($GLOBALS['utf_nfc_qc']))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx);
- }
-
- // Replace any byte in the range 0x00..0x1F, except for \r, \n and \t
- // We replace those characters with a 0xFF byte, which is illegal in UTF-8 and will in turn be replaced with a UTF replacement char
- return utf_normalizer::recompose(
- strtr(
- $str,
- "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- ),
- $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp']
- );
+ if ($pos == $len)
+ {
+ // ASCII strings return immediately
+ return $str;
}
- /**
- * Validate and normalize a UTF string to NFC
- *
- * @param string $str Unchecked UTF string
- * @return string The string, validated and in normal form
- */
- function nfc($str)
+ if (!isset($GLOBALS['utf_canonical_decomp']))
{
- $pos = strspn($str, UTF8_ASCII_RANGE);
- $len = strlen($str);
+ global $phpbb_root_path, $phpEx;
+ include($phpbb_root_path . 'includes/utf/data/utf_canonical_decomp.' . $phpEx);
+ }
- if ($pos == $len)
- {
- // ASCII strings return immediately
- return $str;
- }
+ return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_canonical_decomp']);
+ }
- if (!isset($GLOBALS['utf_nfc_qc']))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx);
- }
+ /**
+ * Validate and normalize a UTF string to NFKD
+ *
+ * @param string $str Unchecked UTF string
+ * @return string The string, validated and in normal form
+ */
+ function nfkd($str)
+ {
+ $pos = strspn($str, UTF8_ASCII_RANGE);
+ $len = strlen($str);
- return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp']);
+ if ($pos == $len)
+ {
+ // ASCII strings return immediately
+ return $str;
}
- /**
- * Validate and normalize a UTF string to NFKC
- *
- * @param string $str Unchecked UTF string
- * @return string The string, validated and in normal form
- */
- function nfkc($str)
+ if (!isset($GLOBALS['utf_compatibility_decomp']))
{
- $pos = strspn($str, UTF8_ASCII_RANGE);
- $len = strlen($str);
-
- if ($pos == $len)
- {
- // ASCII strings return immediately
- return $str;
- }
+ global $phpbb_root_path, $phpEx;
+ include($phpbb_root_path . 'includes/utf/data/utf_compatibility_decomp.' . $phpEx);
+ }
- if (!isset($GLOBALS['utf_nfkc_qc']))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/utf/data/utf_nfkc_qc.' . $phpEx);
- }
+ return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_compatibility_decomp']);
+ }
- if (!isset($GLOBALS['utf_canonical_comp']))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/utf/data/utf_canonical_comp.' . $phpEx);
- }
- return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']);
- }
+ /**
+ * Recompose a UTF string
+ *
+ * @param string $str Unchecked UTF string
+ * @param integer $pos Position of the first UTF char (in bytes)
+ * @param integer $len Length of the string (in bytes)
+ * @param array $qc Quick-check array, passed by reference but never modified
+ * @param array $decomp_map Decomposition mapping, passed by reference but never modified
+ * @return string The string, validated and recomposed
+ *
+ * @access private
+ */
+ function recompose($str, $pos, $len, &$qc, &$decomp_map)
+ {
+ global $utf_combining_class, $utf_canonical_comp, $utf_jamo_type, $utf_jamo_index;
- /**
- * Validate and normalize a UTF string to NFD
- *
- * @param string $str Unchecked UTF string
- * @return string The string, validated and in normal form
- */
- function nfd($str)
+ // Load some commonly-used tables
+ if (!isset($utf_jamo_index, $utf_jamo_type, $utf_combining_class))
{
- $pos = strspn($str, UTF8_ASCII_RANGE);
- $len = strlen($str);
-
- if ($pos == $len)
- {
- // ASCII strings return immediately
- return $str;
- }
+ global $phpbb_root_path;
+ include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php');
+ }
- if (!isset($GLOBALS['utf_canonical_decomp']))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/utf/data/utf_canonical_decomp.' . $phpEx);
- }
+ // Buffer the last ASCII char before the UTF-8 stuff if applicable
+ $tmp = '';
+ $i = $tmp_pos = $last_cc = 0;
- return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_canonical_decomp']);
+ if ($pos)
+ {
+ $buffer = array(++$i => $str[$pos - 1]);
}
-
- /**
- * Validate and normalize a UTF string to NFKD
- *
- * @param string $str Unchecked UTF string
- * @return string The string, validated and in normal form
- */
- function nfkd($str)
+ else
{
- $pos = strspn($str, UTF8_ASCII_RANGE);
- $len = strlen($str);
-
- if ($pos == $len)
- {
- // ASCII strings return immediately
- return $str;
- }
-
- if (!isset($GLOBALS['utf_compatibility_decomp']))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/utf/data/utf_compatibility_decomp.' . $phpEx);
- }
-
- return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_compatibility_decomp']);
+ $buffer = array();
}
-
- /**
- * Recompose a UTF string
- *
- * @param string $str Unchecked UTF string
- * @param integer $pos Position of the first UTF char (in bytes)
- * @param integer $len Length of the string (in bytes)
- * @param array $qc Quick-check array, passed by reference but never modified
- * @param array $decomp_map Decomposition mapping, passed by reference but never modified
- * @return string The string, validated and recomposed
- *
- * @access private
- */
- function recompose($str, $pos, $len, &$qc, &$decomp_map)
+ // UTF char length array
+ // This array is used to determine the length of a UTF character.
+ // Be $c the result of ($str[$pos] & "\xF0") --where $str is the string we're operating on and $pos
+ // the position of the cursor--, if $utf_len_mask[$c] does not exist, the byte is an ASCII char.
+ // Otherwise, if $utf_len_mask[$c] is greater than 0, we have a the leading byte of a multibyte character
+ // whose length is $utf_len_mask[$c] and if it is equal to 0, the byte is a trailing byte.
+ $utf_len_mask = array(
+ // Leading bytes masks
+ "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4,
+ // Trailing bytes masks
+ "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0
+ );
+
+ $extra_check = array(
+ "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1,
+ "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1,
+ "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1
+ );
+
+ $utf_validation_mask = array(
+ 2 => "\xE0\xC0",
+ 3 => "\xF0\xC0\xC0",
+ 4 => "\xF8\xC0\xC0\xC0"
+ );
+
+ $utf_validation_check = array(
+ 2 => "\xC0\x80",
+ 3 => "\xE0\x80\x80",
+ 4 => "\xF0\x80\x80\x80"
+ );
+
+ // Main loop
+ do
{
- global $utf_combining_class, $utf_canonical_comp, $utf_jamo_type, $utf_jamo_index;
-
- // Load some commonly-used tables
- if (!isset($utf_jamo_index, $utf_jamo_type, $utf_combining_class))
- {
- global $phpbb_root_path;
- include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php');
- }
+ // STEP 0: Capture the current char and buffer it
+ $c = $str[$pos];
+ $c_mask = $c & "\xF0";
- // Buffer the last ASCII char before the UTF-8 stuff if applicable
- $tmp = '';
- $i = $tmp_pos = $last_cc = 0;
-
- if ($pos)
- {
- $buffer = array(++$i => $str[$pos - 1]);
- }
- else
- {
- $buffer = array();
- }
-
- // UTF char length array
- // This array is used to determine the length of a UTF character.
- // Be $c the result of ($str[$pos] & "\xF0") --where $str is the string we're operating on and $pos
- // the position of the cursor--, if $utf_len_mask[$c] does not exist, the byte is an ASCII char.
- // Otherwise, if $utf_len_mask[$c] is greater than 0, we have a the leading byte of a multibyte character
- // whose length is $utf_len_mask[$c] and if it is equal to 0, the byte is a trailing byte.
- $utf_len_mask = array(
- // Leading bytes masks
- "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4,
- // Trailing bytes masks
- "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0
- );
-
- $extra_check = array(
- "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1,
- "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1,
- "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1
- );
-
- $utf_validation_mask = array(
- 2 => "\xE0\xC0",
- 3 => "\xF0\xC0\xC0",
- 4 => "\xF8\xC0\xC0\xC0"
- );
-
- $utf_validation_check = array(
- 2 => "\xC0\x80",
- 3 => "\xE0\x80\x80",
- 4 => "\xF0\x80\x80\x80"
- );
-
- // Main loop
- do
+ if (isset($utf_len_mask[$c_mask]))
{
- // STEP 0: Capture the current char and buffer it
- $c = $str[$pos];
- $c_mask = $c & "\xF0";
-
- if (isset($utf_len_mask[$c_mask]))
+ // Byte at $pos is either a leading byte or a missplaced trailing byte
+ if ($utf_len = $utf_len_mask[$c_mask])
{
- // Byte at $pos is either a leading byte or a missplaced trailing byte
- if ($utf_len = $utf_len_mask[$c_mask])
- {
- // Capture the char
- $buffer[++$i & 7] = $utf_char = substr($str, $pos, $utf_len);
+ // Capture the char
+ $buffer[++$i & 7] = $utf_char = substr($str, $pos, $utf_len);
- // Let's find out if a thorough check is needed
- if (isset($qc[$utf_char]))
+ // Let's find out if a thorough check is needed
+ if (isset($qc[$utf_char]))
+ {
+ // If the UTF char is in the qc array then it may not be in normal form. We do nothing here, the actual processing is below this "if" block
+ }
+ else if (isset($utf_combining_class[$utf_char]))
+ {
+ if ($utf_combining_class[$utf_char] < $last_cc)
{
- // If the UTF char is in the qc array then it may not be in normal form. We do nothing here, the actual processing is below this "if" block
+ // A combining character that is NOT canonically ordered
}
- else if (isset($utf_combining_class[$utf_char]))
+ else
+ {
+ // A combining character that IS canonically ordered, skip to the next char
+ $last_cc = $utf_combining_class[$utf_char];
+
+ $pos += $utf_len;
+ continue;
+ }
+ }
+ else
+ {
+ // At this point, $utf_char holds a UTF char that we know is not a NF[K]C_QC and is not a combining character.
+ // It can be a singleton, a canonical composite, a replacement char or an even an ill-formed bunch of bytes. Let's find out
+ $last_cc = 0;
+
+ // Check that we have the correct number of trailing bytes
+ if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len])
{
- if ($utf_combining_class[$utf_char] < $last_cc)
+ // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char
+ // has been encoded in a five- or six- byte sequence
+ if ($utf_char[0] >= "\xF8")
{
- // A combining character that is NOT canonically ordered
+ if ($utf_char[0] < "\xF8")
+ {
+ $trailing_bytes = 3;
+ }
+ else if ($utf_char[0] < "\xFC")
+ {
+ $trailing_bytes = 4;
+ }
+
+ if ($utf_char[0] > "\xFD")
+ {
+ $trailing_bytes = 0;
+ }
+ else
+ {
+ $trailing_bytes = 5;
+ }
}
else
{
- // A combining character that IS canonically ordered, skip to the next char
- $last_cc = $utf_combining_class[$utf_char];
-
- $pos += $utf_len;
- continue;
+ $trailing_bytes = $utf_len - 1;
}
+
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes);
+ $tmp_pos = $pos;
+
+ continue;
}
- else
- {
- // At this point, $utf_char holds a UTF char that we know is not a NF[K]C_QC and is not a combining character.
- // It can be a singleton, a canonical composite, a replacement char or an even an ill-formed bunch of bytes. Let's find out
- $last_cc = 0;
- // Check that we have the correct number of trailing bytes
- if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len])
+ if (isset($extra_check[$c]))
+ {
+ switch ($c)
{
- // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char
- // has been encoded in a five- or six- byte sequence
- if ($utf_char[0] >= "\xF8")
- {
- if ($utf_char[0] < "\xF8")
+ // Note: 0xED is quite common in Korean
+ case "\xED":
+ if ($utf_char >= "\xED\xA0\x80")
{
- $trailing_bytes = 3;
+ // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF)
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += $utf_len;
+ $tmp_pos = $pos;
+ continue 2;
}
- else if ($utf_char[0] < "\xFC")
+ break;
+
+ // Note: 0xEF is quite common in Japanese
+ case "\xEF":
+ if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF")
{
- $trailing_bytes = 4;
+ // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF)
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += $utf_len;
+ $tmp_pos = $pos;
+ continue 2;
}
+ break;
- if ($utf_char[0] > "\xFD")
+ case "\xC0":
+ case "\xC1":
+ if ($utf_char <= "\xC1\xBF")
{
- $trailing_bytes = 0;
+ // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += $utf_len;
+ $tmp_pos = $pos;
+ continue 2;
}
- else
+ break;
+
+ case "\xE0":
+ if ($utf_char <= "\xE0\x9F\xBF")
{
- $trailing_bytes = 5;
+ // Unicode char U+0000..U+07FF encoded in 3 bytes
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += $utf_len;
+ $tmp_pos = $pos;
+ continue 2;
}
- }
- else
- {
- $trailing_bytes = $utf_len - 1;
- }
-
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes);
- $tmp_pos = $pos;
+ break;
- continue;
- }
+ case "\xF0":
+ if ($utf_char <= "\xF0\x8F\xBF\xBF")
+ {
+ // Unicode char U+0000..U+FFFF encoded in 4 bytes
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += $utf_len;
+ $tmp_pos = $pos;
+ continue 2;
+ }
+ break;
- if (isset($extra_check[$c]))
- {
- switch ($c)
- {
- // Note: 0xED is quite common in Korean
- case "\xED":
- if ($utf_char >= "\xED\xA0\x80")
+ default:
+ // Five- and six- byte sequences do not need being checked for here anymore
+ if ($utf_char > UTF8_MAX)
+ {
+ // Out of the Unicode range
+ if ($utf_char[0] < "\xF8")
{
- // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF)
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += $utf_len;
- $tmp_pos = $pos;
- continue 2;
+ $trailing_bytes = 3;
}
- break;
-
- // Note: 0xEF is quite common in Japanese
- case "\xEF":
- if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF")
+ else if ($utf_char[0] < "\xFC")
{
- // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF)
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += $utf_len;
- $tmp_pos = $pos;
- continue 2;
+ $trailing_bytes = 4;
}
- break;
-
- case "\xC0":
- case "\xC1":
- if ($utf_char <= "\xC1\xBF")
+ else if ($utf_char[0] > "\xFD")
{
- // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += $utf_len;
- $tmp_pos = $pos;
- continue 2;
+ $trailing_bytes = 0;
}
- break;
-
- case "\xE0":
- if ($utf_char <= "\xE0\x9F\xBF")
- {
- // Unicode char U+0000..U+07FF encoded in 3 bytes
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += $utf_len;
- $tmp_pos = $pos;
- continue 2;
- }
- break;
-
- case "\xF0":
- if ($utf_char <= "\xF0\x8F\xBF\xBF")
+ else
{
- // Unicode char U+0000..U+FFFF encoded in 4 bytes
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += $utf_len;
- $tmp_pos = $pos;
- continue 2;
+ $trailing_bytes = 5;
}
- break;
-
- default:
- // Five- and six- byte sequences do not need being checked for here anymore
- if ($utf_char > UTF8_MAX)
- {
- // Out of the Unicode range
- if ($utf_char[0] < "\xF8")
- {
- $trailing_bytes = 3;
- }
- else if ($utf_char[0] < "\xFC")
- {
- $trailing_bytes = 4;
- }
- else if ($utf_char[0] > "\xFD")
- {
- $trailing_bytes = 0;
- }
- else
- {
- $trailing_bytes = 5;
- }
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
- $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes);
- $tmp_pos = $pos;
- continue 2;
- }
- break;
- }
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT;
+ $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes);
+ $tmp_pos = $pos;
+ continue 2;
+ }
+ break;
}
-
- // The char is a valid starter, move the cursor and go on
- $pos += $utf_len;
- continue;
}
- }
- else
- {
- // A trailing byte came out of nowhere, we will advance the cursor and treat the this byte and all following trailing bytes as if
- // each of them was a Unicode replacement char
- $spn = strspn($str, UTF8_TRAILING_BYTES, $pos);
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn);
- $pos += $spn;
- $tmp_pos = $pos;
+ // The char is a valid starter, move the cursor and go on
+ $pos += $utf_len;
continue;
}
+ }
+ else
+ {
+ // A trailing byte came out of nowhere, we will advance the cursor and treat the this byte and all following trailing bytes as if
+ // each of them was a Unicode replacement char
+ $spn = strspn($str, UTF8_TRAILING_BYTES, $pos);
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn);
+ $pos += $spn;
+ $tmp_pos = $pos;
+ continue;
+ }
- // STEP 1: Decompose current char
- // We have found a character that is either:
- // - in the NFC_QC/NFKC_QC list
- // - a non-starter char that is not canonically ordered
- //
- // We are going to capture the shortest UTF sequence that satisfies these two conditions:
- //
- // 1 - If the sequence does not start at the begginning of the string, it must begin with a starter,
- // and that starter must not have the NF[K]C_QC property equal to "MAYBE"
- //
- // 2 - If the sequence does not end at the end of the string, it must end with a non-starter and be
- // immediately followed by a starter that is not on the QC list
- //
- $utf_seq = array();
- $last_cc = 0;
- $lpos = $pos;
- $pos += $utf_len;
+ // STEP 1: Decompose current char
+
+ // We have found a character that is either:
+ // - in the NFC_QC/NFKC_QC list
+ // - a non-starter char that is not canonically ordered
+ //
+ // We are going to capture the shortest UTF sequence that satisfies these two conditions:
+ //
+ // 1 - If the sequence does not start at the begginning of the string, it must begin with a starter,
+ // and that starter must not have the NF[K]C_QC property equal to "MAYBE"
+ //
+ // 2 - If the sequence does not end at the end of the string, it must end with a non-starter and be
+ // immediately followed by a starter that is not on the QC list
+ //
+ $utf_seq = array();
+ $last_cc = 0;
+ $lpos = $pos;
+ $pos += $utf_len;
+
+ if (isset($decomp_map[$utf_char]))
+ {
+ $_pos = 0;
+ $_len = strlen($decomp_map[$utf_char]);
- if (isset($decomp_map[$utf_char]))
+ do
{
- $_pos = 0;
- $_len = strlen($decomp_map[$utf_char]);
+ $_utf_len =& $utf_len_mask[$decomp_map[$utf_char][$_pos] & "\xF0"];
- do
+ if (isset($_utf_len))
{
- $_utf_len =& $utf_len_mask[$decomp_map[$utf_char][$_pos] & "\xF0"];
-
- if (isset($_utf_len))
- {
- $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
- $_pos += $_utf_len;
- }
- else
- {
- $utf_seq[] = $decomp_map[$utf_char][$_pos];
- ++$_pos;
- }
+ $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
+ $_pos += $_utf_len;
+ }
+ else
+ {
+ $utf_seq[] = $decomp_map[$utf_char][$_pos];
+ ++$_pos;
}
- while ($_pos < $_len);
- }
- else
- {
- // The char is not decomposable
- $utf_seq = array($utf_char);
}
+ while ($_pos < $_len);
+ }
+ else
+ {
+ // The char is not decomposable
+ $utf_seq = array($utf_char);
+ }
- // STEP 2: Capture the starter
+ // STEP 2: Capture the starter
- // Check out the combining class of the first character of the UTF sequence
- $k = 0;
- if (isset($utf_combining_class[$utf_seq[0]]) || $qc[$utf_char] == UNICODE_QC_MAYBE)
+ // Check out the combining class of the first character of the UTF sequence
+ $k = 0;
+ if (isset($utf_combining_class[$utf_seq[0]]) || $qc[$utf_char] == UNICODE_QC_MAYBE)
+ {
+ // Not a starter, inspect previous characters
+ // The last 8 characters are kept in a buffer so that we don't have to capture them everytime.
+ // This is enough for all real-life strings but even if it wasn't, we can capture characters in backward mode,
+ // although it is slower than this method.
+ //
+ // In the following loop, $j starts at the previous buffered character ($i - 1, because current character is
+ // at offset $i) and process them in backward mode until we find a starter.
+ //
+ // $k is the index on each UTF character inside of our UTF sequence. At this time, $utf_seq contains one or more
+ // characters numbered 0 to n. $k starts at 0 and for each char we prepend we pre-decrement it and for numbering
+ $starter_found = 0;
+ $j_min = max(1, $i - 7);
+
+ for ($j = $i - 1; $j >= $j_min && $lpos > $tmp_pos; --$j)
{
- // Not a starter, inspect previous characters
- // The last 8 characters are kept in a buffer so that we don't have to capture them everytime.
- // This is enough for all real-life strings but even if it wasn't, we can capture characters in backward mode,
- // although it is slower than this method.
- //
- // In the following loop, $j starts at the previous buffered character ($i - 1, because current character is
- // at offset $i) and process them in backward mode until we find a starter.
- //
- // $k is the index on each UTF character inside of our UTF sequence. At this time, $utf_seq contains one or more
- // characters numbered 0 to n. $k starts at 0 and for each char we prepend we pre-decrement it and for numbering
- $starter_found = 0;
- $j_min = max(1, $i - 7);
+ $utf_char = $buffer[$j & 7];
+ $lpos -= strlen($utf_char);
- for ($j = $i - 1; $j >= $j_min && $lpos > $tmp_pos; --$j)
+ if (isset($decomp_map[$utf_char]))
{
- $utf_char = $buffer[$j & 7];
- $lpos -= strlen($utf_char);
+ // The char is a composite, decompose for storage
+ $decomp_seq = array();
+ $_pos = 0;
+ $_len = strlen($decomp_map[$utf_char]);
- if (isset($decomp_map[$utf_char]))
+ do
{
- // The char is a composite, decompose for storage
- $decomp_seq = array();
- $_pos = 0;
- $_len = strlen($decomp_map[$utf_char]);
+ $c = $decomp_map[$utf_char][$_pos];
+ $_utf_len =& $utf_len_mask[$c & "\xF0"];
- do
+ if (isset($_utf_len))
{
- $c = $decomp_map[$utf_char][$_pos];
- $_utf_len =& $utf_len_mask[$c & "\xF0"];
-
- if (isset($_utf_len))
- {
- $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
- $_pos += $_utf_len;
- }
- else
- {
- $decomp_seq[] = $c;
- ++$_pos;
- }
+ $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
+ $_pos += $_utf_len;
}
- while ($_pos < $_len);
-
- // Prepend the UTF sequence with our decomposed sequence
- if (isset($decomp_seq[1]))
+ else
{
- // The char expanded into several chars
- $decomp_cnt = sizeof($decomp_seq);
-
- foreach ($decomp_seq as $decomp_i => $decomp_char)
- {
- $utf_seq[$k + $decomp_i - $decomp_cnt] = $decomp_char;
- }
- $k -= $decomp_cnt;
+ $decomp_seq[] = $c;
+ ++$_pos;
}
- else
+ }
+ while ($_pos < $_len);
+
+ // Prepend the UTF sequence with our decomposed sequence
+ if (isset($decomp_seq[1]))
+ {
+ // The char expanded into several chars
+ $decomp_cnt = sizeof($decomp_seq);
+
+ foreach ($decomp_seq as $decomp_i => $decomp_char)
{
- // Decomposed to a single char, easier to prepend
- $utf_seq[--$k] = $decomp_seq[0];
+ $utf_seq[$k + $decomp_i - $decomp_cnt] = $decomp_char;
}
+ $k -= $decomp_cnt;
}
else
{
- $utf_seq[--$k] = $utf_char;
+ // Decomposed to a single char, easier to prepend
+ $utf_seq[--$k] = $decomp_seq[0];
}
+ }
+ else
+ {
+ $utf_seq[--$k] = $utf_char;
+ }
- if (!isset($utf_combining_class[$utf_seq[$k]]))
- {
- // We have found our starter
- $starter_found = 1;
- break;
- }
+ if (!isset($utf_combining_class[$utf_seq[$k]]))
+ {
+ // We have found our starter
+ $starter_found = 1;
+ break;
}
+ }
- if (!$starter_found && $lpos > $tmp_pos)
+ if (!$starter_found && $lpos > $tmp_pos)
+ {
+ // The starter was not found in the buffer, let's rewind some more
+ do
{
- // The starter was not found in the buffer, let's rewind some more
- do
- {
- // $utf_len_mask contains the masks of both leading bytes and trailing bytes. If $utf_en > 0 then it's a leading byte, otherwise it's a trailing byte.
- $c = $str[--$lpos];
- $c_mask = $c & "\xF0";
+ // $utf_len_mask contains the masks of both leading bytes and trailing bytes. If $utf_en > 0 then it's a leading byte, otherwise it's a trailing byte.
+ $c = $str[--$lpos];
+ $c_mask = $c & "\xF0";
- if (isset($utf_len_mask[$c_mask]))
+ if (isset($utf_len_mask[$c_mask]))
+ {
+ // UTF byte
+ if ($utf_len = $utf_len_mask[$c_mask])
{
- // UTF byte
- if ($utf_len = $utf_len_mask[$c_mask])
+ // UTF *leading* byte
+ $utf_char = substr($str, $lpos, $utf_len);
+
+ if (isset($decomp_map[$utf_char]))
{
- // UTF *leading* byte
- $utf_char = substr($str, $lpos, $utf_len);
+ // Decompose the character
+ $decomp_seq = array();
+ $_pos = 0;
+ $_len = strlen($decomp_map[$utf_char]);
- if (isset($decomp_map[$utf_char]))
+ do
{
- // Decompose the character
- $decomp_seq = array();
- $_pos = 0;
- $_len = strlen($decomp_map[$utf_char]);
+ $c = $decomp_map[$utf_char][$_pos];
+ $_utf_len =& $utf_len_mask[$c & "\xF0"];
- do
+ if (isset($_utf_len))
{
- $c = $decomp_map[$utf_char][$_pos];
- $_utf_len =& $utf_len_mask[$c & "\xF0"];
-
- if (isset($_utf_len))
- {
- $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
- $_pos += $_utf_len;
- }
- else
- {
- $decomp_seq[] = $c;
- ++$_pos;
- }
+ $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
+ $_pos += $_utf_len;
}
- while ($_pos < $_len);
-
- // Prepend the UTF sequence with our decomposed sequence
- if (isset($decomp_seq[1]))
+ else
{
- // The char expanded into several chars
- $decomp_cnt = sizeof($decomp_seq);
- foreach ($decomp_seq as $decomp_i => $utf_char)
- {
- $utf_seq[$k + $decomp_i - $decomp_cnt] = $utf_char;
- }
- $k -= $decomp_cnt;
+ $decomp_seq[] = $c;
+ ++$_pos;
}
- else
+ }
+ while ($_pos < $_len);
+
+ // Prepend the UTF sequence with our decomposed sequence
+ if (isset($decomp_seq[1]))
+ {
+ // The char expanded into several chars
+ $decomp_cnt = sizeof($decomp_seq);
+ foreach ($decomp_seq as $decomp_i => $utf_char)
{
- // Decomposed to a single char, easier to prepend
- $utf_seq[--$k] = $decomp_seq[0];
+ $utf_seq[$k + $decomp_i - $decomp_cnt] = $utf_char;
}
+ $k -= $decomp_cnt;
}
else
{
- $utf_seq[--$k] = $utf_char;
+ // Decomposed to a single char, easier to prepend
+ $utf_seq[--$k] = $decomp_seq[0];
}
}
+ else
+ {
+ $utf_seq[--$k] = $utf_char;
+ }
}
- else
- {
- // ASCII char
- $utf_seq[--$k] = $c;
- }
}
- while ($lpos > $tmp_pos);
+ else
+ {
+ // ASCII char
+ $utf_seq[--$k] = $c;
+ }
}
+ while ($lpos > $tmp_pos);
}
+ }
- // STEP 3: Capture following combining modifiers
+ // STEP 3: Capture following combining modifiers
- while ($pos < $len)
+ while ($pos < $len)
+ {
+ $c_mask = $str[$pos] & "\xF0";
+
+ if (isset($utf_len_mask[$c_mask]))
{
- $c_mask = $str[$pos] & "\xF0";
+ if ($utf_len = $utf_len_mask[$c_mask])
+ {
+ $utf_char = substr($str, $pos, $utf_len);
+ }
+ else
+ {
+ // A trailing byte came out of nowhere
+ // Trailing bytes are replaced with Unicode replacement chars, we will just ignore it for now, break out of the loop
+ // as if it was a starter (replacement chars ARE starters) and let the next loop replace it
+ break;
+ }
- if (isset($utf_len_mask[$c_mask]))
+ if (isset($utf_combining_class[$utf_char]) || isset($qc[$utf_char]))
{
- if ($utf_len = $utf_len_mask[$c_mask])
- {
- $utf_char = substr($str, $pos, $utf_len);
- }
- else
+ // Combining character, add it to the sequence and move the cursor
+ if (isset($decomp_map[$utf_char]))
{
- // A trailing byte came out of nowhere
- // Trailing bytes are replaced with Unicode replacement chars, we will just ignore it for now, break out of the loop
- // as if it was a starter (replacement chars ARE starters) and let the next loop replace it
- break;
- }
+ // Decompose the character
+ $_pos = 0;
+ $_len = strlen($decomp_map[$utf_char]);
- if (isset($utf_combining_class[$utf_char]) || isset($qc[$utf_char]))
- {
- // Combining character, add it to the sequence and move the cursor
- if (isset($decomp_map[$utf_char]))
+ do
{
- // Decompose the character
- $_pos = 0;
- $_len = strlen($decomp_map[$utf_char]);
+ $c = $decomp_map[$utf_char][$_pos];
+ $_utf_len =& $utf_len_mask[$c & "\xF0"];
- do
+ if (isset($_utf_len))
{
- $c = $decomp_map[$utf_char][$_pos];
- $_utf_len =& $utf_len_mask[$c & "\xF0"];
-
- if (isset($_utf_len))
- {
- $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
- $_pos += $_utf_len;
- }
- else
- {
- $utf_seq[] = $c;
- ++$_pos;
- }
+ $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len);
+ $_pos += $_utf_len;
+ }
+ else
+ {
+ $utf_seq[] = $c;
+ ++$_pos;
}
- while ($_pos < $_len);
- }
- else
- {
- $utf_seq[] = $utf_char;
}
-
- $pos += $utf_len;
+ while ($_pos < $_len);
}
else
{
- // Combining class 0 and no QC, break out of the loop
- // Note: we do not know if that character is valid. If it's not, the next iteration will replace it
- break;
+ $utf_seq[] = $utf_char;
}
+
+ $pos += $utf_len;
}
else
{
- // ASCII chars are starters
+ // Combining class 0 and no QC, break out of the loop
+ // Note: we do not know if that character is valid. If it's not, the next iteration will replace it
break;
}
}
+ else
+ {
+ // ASCII chars are starters
+ break;
+ }
+ }
- // STEP 4: Sort and combine
+ // STEP 4: Sort and combine
- // Here we sort...
- $k_max = $k + sizeof($utf_seq);
+ // Here we sort...
+ $k_max = $k + sizeof($utf_seq);
- if (!$k && $k_max == 1)
- {
- // There is only one char in the UTF sequence, add it then jump to the next iteration of main loop
+ if (!$k && $k_max == 1)
+ {
+ // There is only one char in the UTF sequence, add it then jump to the next iteration of main loop
// Note: the two commented lines below can be enabled under PHP5 for a very small performance gain in most cases
// if (substr_compare($str, $utf_seq[0], $lpos, $pos - $lpos))
// {
- $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $utf_seq[0];
- $tmp_pos = $pos;
+ $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $utf_seq[0];
+ $tmp_pos = $pos;
// }
- continue;
- }
+ continue;
+ }
+
+ // ...there we combine
+ if (isset($utf_combining_class[$utf_seq[$k]]))
+ {
+ $starter = $nf_seq = '';
+ }
+ else
+ {
+ $starter = $utf_seq[$k++];
+ $nf_seq = '';
+ }
+ $utf_sort = array();
+
+ // We add an empty char at the end of the UTF char sequence. It will act as a starter and trigger the sort/combine routine
+ // at the end of the string without altering it
+ $utf_seq[] = '';
+
+ do
+ {
+ $utf_char = $utf_seq[$k++];
- // ...there we combine
- if (isset($utf_combining_class[$utf_seq[$k]]))
+ if (isset($utf_combining_class[$utf_char]))
{
- $starter = $nf_seq = '';
+ $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char;
}
else
{
- $starter = $utf_seq[$k++];
- $nf_seq = '';
- }
- $utf_sort = array();
-
- // We add an empty char at the end of the UTF char sequence. It will act as a starter and trigger the sort/combine routine
- // at the end of the string without altering it
- $utf_seq[] = '';
-
- do
- {
- $utf_char = $utf_seq[$k++];
-
- if (isset($utf_combining_class[$utf_char]))
- {
- $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char;
- }
- else
+ if (empty($utf_sort))
{
- if (empty($utf_sort))
+ // No combining characters... check for a composite of the two starters
+ if (isset($utf_canonical_comp[$starter . $utf_char]))
{
- // No combining characters... check for a composite of the two starters
- if (isset($utf_canonical_comp[$starter . $utf_char]))
- {
- // Good ol' composite character
- $starter = $utf_canonical_comp[$starter . $utf_char];
- }
- else if (isset($utf_jamo_type[$utf_char]))
+ // Good ol' composite character
+ $starter = $utf_canonical_comp[$starter . $utf_char];
+ }
+ else if (isset($utf_jamo_type[$utf_char]))
+ {
+ // Current char is a composable jamo
+ if (isset($utf_jamo_type[$starter]) && $utf_jamo_type[$starter] == UNICODE_JAMO_L && $utf_jamo_type[$utf_char] == UNICODE_JAMO_V)
{
- // Current char is a composable jamo
- if (isset($utf_jamo_type[$starter]) && $utf_jamo_type[$starter] == UNICODE_JAMO_L && $utf_jamo_type[$utf_char] == UNICODE_JAMO_V)
+ // We have a L jamo followed by a V jamo, we are going to prefetch the next char to see if it's a T jamo
+ if (isset($utf_jamo_type[$utf_seq[$k]]) && $utf_jamo_type[$utf_seq[$k]] == UNICODE_JAMO_T)
{
- // We have a L jamo followed by a V jamo, we are going to prefetch the next char to see if it's a T jamo
- if (isset($utf_jamo_type[$utf_seq[$k]]) && $utf_jamo_type[$utf_seq[$k]] == UNICODE_JAMO_T)
- {
- // L+V+T jamos, combine to a LVT Hangul syllable ($k is incremented)
- $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char] + $utf_jamo_index[$utf_seq[$k]];
- ++$k;
- }
- else
- {
- // L+V jamos, combine to a LV Hangul syllable
- $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char];
- }
-
- $starter = chr(0xE0 | ($cp >> 12)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F));
+ // L+V+T jamos, combine to a LVT Hangul syllable ($k is incremented)
+ $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char] + $utf_jamo_index[$utf_seq[$k]];
+ ++$k;
}
else
{
- // Non-composable jamo, just add it to the sequence
- $nf_seq .= $starter;
- $starter = $utf_char;
+ // L+V jamos, combine to a LV Hangul syllable
+ $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char];
}
+
+ $starter = chr(0xE0 | ($cp >> 12)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F));
}
else
{
- // No composite, just add the first starter to the sequence then continue with the other one
+ // Non-composable jamo, just add it to the sequence
$nf_seq .= $starter;
$starter = $utf_char;
}
}
else
{
- ksort($utf_sort);
+ // No composite, just add the first starter to the sequence then continue with the other one
+ $nf_seq .= $starter;
+ $starter = $utf_char;
+ }
+ }
+ else
+ {
+ ksort($utf_sort);
- // For each class of combining characters
- foreach ($utf_sort as $cc => $utf_chars)
- {
- $j = 0;
+ // For each class of combining characters
+ foreach ($utf_sort as $cc => $utf_chars)
+ {
+ $j = 0;
- do
+ do
+ {
+ // Look for a composite
+ if (isset($utf_canonical_comp[$starter . $utf_chars[$j]]))
{
- // Look for a composite
- if (isset($utf_canonical_comp[$starter . $utf_chars[$j]]))
- {
- // Found a composite, replace the starter
- $starter = $utf_canonical_comp[$starter . $utf_chars[$j]];
- unset($utf_sort[$cc][$j]);
- }
- else
- {
- // No composite, all following characters in that class are blocked
- break;
- }
+ // Found a composite, replace the starter
+ $starter = $utf_canonical_comp[$starter . $utf_chars[$j]];
+ unset($utf_sort[$cc][$j]);
+ }
+ else
+ {
+ // No composite, all following characters in that class are blocked
+ break;
}
- while (isset($utf_sort[$cc][++$j]));
}
+ while (isset($utf_sort[$cc][++$j]));
+ }
- // Add the starter to the normalized sequence, followed by non-starters in canonical order
- $nf_seq .= $starter;
+ // Add the starter to the normalized sequence, followed by non-starters in canonical order
+ $nf_seq .= $starter;
- foreach ($utf_sort as $utf_chars)
+ foreach ($utf_sort as $utf_chars)
+ {
+ if (!empty($utf_chars))
{
- if (!empty($utf_chars))
- {
- $nf_seq .= implode('', $utf_chars);
- }
+ $nf_seq .= implode('', $utf_chars);
}
-
- // Reset the array and go on
- $utf_sort = array();
- $starter = $utf_char;
}
+
+ // Reset the array and go on
+ $utf_sort = array();
+ $starter = $utf_char;
}
}
- while ($k <= $k_max);
-
- $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $nf_seq;
- $tmp_pos = $pos;
}
- else
+ while ($k <= $k_max);
+
+ $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $nf_seq;
+ $tmp_pos = $pos;
+ }
+ else
+ {
+ // Only a ASCII char can make the program get here
+ //
+ // First we skip the current byte with ++$pos, then we quickly skip following ASCII chars with strspn().
+ //
+ // The first two "if"'s here can be removed, with the consequences of being faster on latin text (lots of ASCII) and slower on
+ // multi-byte text (where the only ASCII chars are spaces and punctuation)
+ if (++$pos != $len)
{
- // Only a ASCII char can make the program get here
- //
- // First we skip the current byte with ++$pos, then we quickly skip following ASCII chars with strspn().
- //
- // The first two "if"'s here can be removed, with the consequences of being faster on latin text (lots of ASCII) and slower on
- // multi-byte text (where the only ASCII chars are spaces and punctuation)
- if (++$pos != $len)
+ if ($str[$pos] < "\x80")
{
- if ($str[$pos] < "\x80")
- {
- $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos);
- $buffer[++$i & 7] = $str[$pos - 1];
- }
- else
- {
- $buffer[++$i & 7] = $c;
- }
+ $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos);
+ $buffer[++$i & 7] = $str[$pos - 1];
+ }
+ else
+ {
+ $buffer[++$i & 7] = $c;
}
}
}
- while ($pos < $len);
+ }
+ while ($pos < $len);
- // Now is time to return the string
- if ($tmp_pos)
+ // Now is time to return the string
+ if ($tmp_pos)
+ {
+ // If the $tmp_pos cursor is not at the beggining of the string then at least one character was not in normal form. Replace $str with the fixed version
+ if ($tmp_pos == $len)
{
- // If the $tmp_pos cursor is not at the beggining of the string then at least one character was not in normal form. Replace $str with the fixed version
- if ($tmp_pos == $len)
- {
- // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str
- return $tmp;
- }
- else
- {
- // The rightmost chunk of $str has not been appended to $tmp yet
- return $tmp . substr($str, $tmp_pos);
- }
+ // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str
+ return $tmp;
+ }
+ else
+ {
+ // The rightmost chunk of $str has not been appended to $tmp yet
+ return $tmp . substr($str, $tmp_pos);
}
-
- // The string was already in normal form
- return $str;
}
- /**
- * Decompose a UTF string
- *
- * @param string $str UTF string
- * @param integer $pos Position of the first UTF char (in bytes)
- * @param integer $len Length of the string (in bytes)
- * @param array $decomp_map Decomposition mapping, passed by reference but never modified
- * @return string The string, decomposed and sorted canonically
- *
- * @access private
- */
- function decompose($str, $pos, $len, &$decomp_map)
+ // The string was already in normal form
+ return $str;
+ }
+
+ /**
+ * Decompose a UTF string
+ *
+ * @param string $str UTF string
+ * @param integer $pos Position of the first UTF char (in bytes)
+ * @param integer $len Length of the string (in bytes)
+ * @param array $decomp_map Decomposition mapping, passed by reference but never modified
+ * @return string The string, decomposed and sorted canonically
+ *
+ * @access private
+ */
+ function decompose($str, $pos, $len, &$decomp_map)
+ {
+ global $utf_combining_class, $utf_canonical_decomp, $phpbb_root_path;
+
+ // Load some commonly-used tables
+ if (!isset($utf_combining_class))
{
- global $utf_combining_class, $utf_canonical_decomp, $phpbb_root_path;
+ include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php');
+ }
- // Load some commonly-used tables
- if (!isset($utf_combining_class))
- {
- include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php');
- }
+ // UTF char length array
+ $utf_len_mask = array(
+ // Leading bytes masks
+ "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4,
+ // Trailing bytes masks
+ "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0
+ );
+
+ // Some extra checks are triggered on the first byte of a UTF sequence
+ $extra_check = array(
+ "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1,
+ "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1,
+ "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1
+ );
+
+ // These masks are used to check if a UTF sequence is well formed. Here are the only 3 lengths we acknowledge:
+ // - 2-byte: 110? ???? 10?? ????
+ // - 3-byte: 1110 ???? 10?? ???? 10?? ????
+ // - 4-byte: 1111 0??? 10?? ???? 10?? ???? 10?? ????
+ // Note that 5- and 6- byte sequences are automatically discarded
+ $utf_validation_mask = array(
+ 2 => "\xE0\xC0",
+ 3 => "\xF0\xC0\xC0",
+ 4 => "\xF8\xC0\xC0\xC0"
+ );
+
+ $utf_validation_check = array(
+ 2 => "\xC0\x80",
+ 3 => "\xE0\x80\x80",
+ 4 => "\xF0\x80\x80\x80"
+ );
+
+ $tmp = '';
+ $starter_pos = $pos;
+ $tmp_pos = $last_cc = $sort = $dump = 0;
+ $utf_sort = array();
+
+
+ // Main loop
+ do
+ {
+ // STEP 0: Capture the current char
- // UTF char length array
- $utf_len_mask = array(
- // Leading bytes masks
- "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4,
- // Trailing bytes masks
- "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0
- );
-
- // Some extra checks are triggered on the first byte of a UTF sequence
- $extra_check = array(
- "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1,
- "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1,
- "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1
- );
-
- // These masks are used to check if a UTF sequence is well formed. Here are the only 3 lengths we acknowledge:
- // - 2-byte: 110? ???? 10?? ????
- // - 3-byte: 1110 ???? 10?? ???? 10?? ????
- // - 4-byte: 1111 0??? 10?? ???? 10?? ???? 10?? ????
- // Note that 5- and 6- byte sequences are automatically discarded
- $utf_validation_mask = array(
- 2 => "\xE0\xC0",
- 3 => "\xF0\xC0\xC0",
- 4 => "\xF8\xC0\xC0\xC0"
- );
-
- $utf_validation_check = array(
- 2 => "\xC0\x80",
- 3 => "\xE0\x80\x80",
- 4 => "\xF0\x80\x80\x80"
- );
-
- $tmp = '';
- $starter_pos = $pos;
- $tmp_pos = $last_cc = $sort = $dump = 0;
- $utf_sort = array();
-
-
- // Main loop
- do
+ $cur_mask = $str[$pos] & "\xF0";
+ if (isset($utf_len_mask[$cur_mask]))
{
- // STEP 0: Capture the current char
-
- $cur_mask = $str[$pos] & "\xF0";
- if (isset($utf_len_mask[$cur_mask]))
+ if ($utf_len = $utf_len_mask[$cur_mask])
{
- if ($utf_len = $utf_len_mask[$cur_mask])
- {
- // Multibyte char
- $utf_char = substr($str, $pos, $utf_len);
- $pos += $utf_len;
- }
- else
+ // Multibyte char
+ $utf_char = substr($str, $pos, $utf_len);
+ $pos += $utf_len;
+ }
+ else
+ {
+ // A trailing byte came out of nowhere, we will treat it and all following trailing bytes as if each of them was a Unicode
+ // replacement char and we will advance the cursor
+ $spn = strspn($str, UTF8_TRAILING_BYTES, $pos);
+
+ if ($dump)
{
- // A trailing byte came out of nowhere, we will treat it and all following trailing bytes as if each of them was a Unicode
- // replacement char and we will advance the cursor
- $spn = strspn($str, UTF8_TRAILING_BYTES, $pos);
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
- if ($dump)
+ // Dump combiners
+ if (!empty($utf_sort))
{
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
-
- // Dump combiners
- if (!empty($utf_sort))
+ if ($sort)
{
- if ($sort)
- {
- ksort($utf_sort);
- }
-
- foreach($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
+ ksort($utf_sort);
}
- $tmp .= str_repeat(UTF8_REPLACEMENT, $spn);
- $dump = $sort = 0;
- }
- else
- {
- $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn);
+ foreach($utf_sort as $utf_chars)
+ {
+ $tmp .= implode('', $utf_chars);
+ }
}
- $pos += $spn;
- $tmp_pos = $starter_pos = $pos;
+ $tmp .= str_repeat(UTF8_REPLACEMENT, $spn);
+ $dump = $sort = 0;
+ }
+ else
+ {
+ $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn);
+ }
- $utf_sort = array();
- $last_cc = 0;
+ $pos += $spn;
+ $tmp_pos = $starter_pos = $pos;
+
+ $utf_sort = array();
+ $last_cc = 0;
+
+ continue;
+ }
- continue;
- }
+ // STEP 1: Decide what to do with current char
- // STEP 1: Decide what to do with current char
+ // Now, in that order:
+ // - check if that character is decomposable
+ // - check if that character is a non-starter
+ // - check if that character requires extra checks to be performed
+ if (isset($decomp_map[$utf_char]))
+ {
+ // Decompose the char
+ $_pos = 0;
+ $_len = strlen($decomp_map[$utf_char]);
- // Now, in that order:
- // - check if that character is decomposable
- // - check if that character is a non-starter
- // - check if that character requires extra checks to be performed
- if (isset($decomp_map[$utf_char]))
+ do
{
- // Decompose the char
- $_pos = 0;
- $_len = strlen($decomp_map[$utf_char]);
+ $c = $decomp_map[$utf_char][$_pos];
+ $_utf_len =& $utf_len_mask[$c & "\xF0"];
- do
+ if (isset($_utf_len))
{
- $c = $decomp_map[$utf_char][$_pos];
- $_utf_len =& $utf_len_mask[$c & "\xF0"];
+ $_utf_char = substr($decomp_map[$utf_char], $_pos, $_utf_len);
+ $_pos += $_utf_len;
- if (isset($_utf_len))
+ if (isset($utf_combining_class[$_utf_char]))
{
- $_utf_char = substr($decomp_map[$utf_char], $_pos, $_utf_len);
- $_pos += $_utf_len;
+ // The character decomposed to a non-starter, buffer it for sorting
+ $utf_sort[$utf_combining_class[$_utf_char]][] = $_utf_char;
- if (isset($utf_combining_class[$_utf_char]))
+ if ($utf_combining_class[$_utf_char] < $last_cc)
{
- // The character decomposed to a non-starter, buffer it for sorting
- $utf_sort[$utf_combining_class[$_utf_char]][] = $_utf_char;
-
- if ($utf_combining_class[$_utf_char] < $last_cc)
- {
- // Not canonically ordered, will require sorting
- $sort = $dump = 1;
- }
- else
- {
- $dump = 1;
- $last_cc = $utf_combining_class[$_utf_char];
- }
+ // Not canonically ordered, will require sorting
+ $sort = $dump = 1;
}
else
{
- // This character decomposition contains a starter, dump the buffer and continue
- if ($dump)
- {
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
-
- // Dump combiners
- if (!empty($utf_sort))
- {
- if ($sort)
- {
- ksort($utf_sort);
- }
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- }
-
- $tmp .= $_utf_char;
- $dump = $sort = 0;
- }
- else
- {
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos) . $_utf_char;
- }
-
- $tmp_pos = $starter_pos = $pos;
- $utf_sort = array();
- $last_cc = 0;
+ $dump = 1;
+ $last_cc = $utf_combining_class[$_utf_char];
}
}
else
{
- // This character decomposition contains an ASCII char, which is a starter. Dump the buffer and continue
- ++$_pos;
-
+ // This character decomposition contains a starter, dump the buffer and continue
if ($dump)
{
$tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
@@ -1220,12 +1094,12 @@ else
}
}
- $tmp .= $c;
+ $tmp .= $_utf_char;
$dump = $sort = 0;
}
else
{
- $tmp .= substr($str, $tmp_pos, $pos - $utf_len - $tmp_pos) . $c;
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos) . $_utf_char;
}
$tmp_pos = $starter_pos = $pos;
@@ -1233,285 +1107,290 @@ else
$last_cc = 0;
}
}
- while ($_pos < $_len);
- }
- else if (isset($utf_combining_class[$utf_char]))
- {
- // Combining character
- if ($utf_combining_class[$utf_char] < $last_cc)
- {
- // Not in canonical order
- $sort = $dump = 1;
- }
else
{
- $last_cc = $utf_combining_class[$utf_char];
- }
+ // This character decomposition contains an ASCII char, which is a starter. Dump the buffer and continue
+ ++$_pos;
- $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char;
- }
- else
- {
- // Non-decomposable starter, check out if it's a Hangul syllable
- if ($utf_char < UTF8_HANGUL_FIRST || $utf_char > UTF8_HANGUL_LAST)
- {
- // Nope, regular UTF char, check that we have the correct number of trailing bytes
- if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len])
+ if ($dump)
{
- // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char
- // has been encoded in a five- or six- byte sequence.
- // Move the cursor back to its original position then advance it to the position it should really be at
- $pos -= $utf_len;
$tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ // Dump combiners
if (!empty($utf_sort))
{
- ksort($utf_sort);
+ if ($sort)
+ {
+ ksort($utf_sort);
+ }
foreach ($utf_sort as $utf_chars)
{
$tmp .= implode('', $utf_chars);
}
- $utf_sort = array();
}
- // Add a replacement char then another replacement char for every trailing byte.
- //
- // @todo I'm not entirely sure that's how we're supposed to mark invalidated byte sequences, check this
- $spn = strspn($str, UTF8_TRAILING_BYTES, ++$pos);
- $tmp .= str_repeat(UTF8_REPLACEMENT, $spn + 1);
-
+ $tmp .= $c;
$dump = $sort = 0;
-
- $pos += $spn;
- $tmp_pos = $pos;
- continue;
}
+ else
+ {
+ $tmp .= substr($str, $tmp_pos, $pos - $utf_len - $tmp_pos) . $c;
+ }
+
+ $tmp_pos = $starter_pos = $pos;
+ $utf_sort = array();
+ $last_cc = 0;
+ }
+ }
+ while ($_pos < $_len);
+ }
+ else if (isset($utf_combining_class[$utf_char]))
+ {
+ // Combining character
+ if ($utf_combining_class[$utf_char] < $last_cc)
+ {
+ // Not in canonical order
+ $sort = $dump = 1;
+ }
+ else
+ {
+ $last_cc = $utf_combining_class[$utf_char];
+ }
- if (isset($extra_check[$utf_char[0]]))
+ $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char;
+ }
+ else
+ {
+ // Non-decomposable starter, check out if it's a Hangul syllable
+ if ($utf_char < UTF8_HANGUL_FIRST || $utf_char > UTF8_HANGUL_LAST)
+ {
+ // Nope, regular UTF char, check that we have the correct number of trailing bytes
+ if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len])
+ {
+ // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char
+ // has been encoded in a five- or six- byte sequence.
+ // Move the cursor back to its original position then advance it to the position it should really be at
+ $pos -= $utf_len;
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+
+ if (!empty($utf_sort))
{
- switch ($utf_char[0])
+ ksort($utf_sort);
+
+ foreach ($utf_sort as $utf_chars)
{
- // Note: 0xED is quite common in Korean
- case "\xED":
- if ($utf_char >= "\xED\xA0\x80")
+ $tmp .= implode('', $utf_chars);
+ }
+ $utf_sort = array();
+ }
+
+ // Add a replacement char then another replacement char for every trailing byte.
+ //
+ // @todo I'm not entirely sure that's how we're supposed to mark invalidated byte sequences, check this
+ $spn = strspn($str, UTF8_TRAILING_BYTES, ++$pos);
+ $tmp .= str_repeat(UTF8_REPLACEMENT, $spn + 1);
+
+ $dump = $sort = 0;
+
+ $pos += $spn;
+ $tmp_pos = $pos;
+ continue;
+ }
+
+ if (isset($extra_check[$utf_char[0]]))
+ {
+ switch ($utf_char[0])
+ {
+ // Note: 0xED is quite common in Korean
+ case "\xED":
+ if ($utf_char >= "\xED\xA0\x80")
+ {
+ // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF)
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+
+ if (!empty($utf_sort))
{
- // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF)
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ ksort($utf_sort);
- if (!empty($utf_sort))
+ foreach ($utf_sort as $utf_chars)
{
- ksort($utf_sort);
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- $utf_sort = array();
+ $tmp .= implode('', $utf_chars);
}
+ $utf_sort = array();
+ }
- $tmp .= UTF8_REPLACEMENT;
- $dump = $sort = 0;
+ $tmp .= UTF8_REPLACEMENT;
+ $dump = $sort = 0;
- $tmp_pos = $starter_pos = $pos;
- continue 2;
- }
- break;
+ $tmp_pos = $starter_pos = $pos;
+ continue 2;
+ }
+ break;
+
+ // Note: 0xEF is quite common in Japanese
+ case "\xEF":
+ if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF")
+ {
+ // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF)
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
- // Note: 0xEF is quite common in Japanese
- case "\xEF":
- if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF")
+ if (!empty($utf_sort))
{
- // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF)
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ ksort($utf_sort);
- if (!empty($utf_sort))
+ foreach ($utf_sort as $utf_chars)
{
- ksort($utf_sort);
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- $utf_sort = array();
+ $tmp .= implode('', $utf_chars);
}
+ $utf_sort = array();
+ }
- $tmp .= UTF8_REPLACEMENT;
- $dump = $sort = 0;
+ $tmp .= UTF8_REPLACEMENT;
+ $dump = $sort = 0;
- $tmp_pos = $starter_pos = $pos;
- continue 2;
- }
- break;
+ $tmp_pos = $starter_pos = $pos;
+ continue 2;
+ }
+ break;
+
+ case "\xC0":
+ case "\xC1":
+ if ($utf_char <= "\xC1\xBF")
+ {
+ // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
- case "\xC0":
- case "\xC1":
- if ($utf_char <= "\xC1\xBF")
+ if (!empty($utf_sort))
{
- // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ ksort($utf_sort);
- if (!empty($utf_sort))
+ foreach ($utf_sort as $utf_chars)
{
- ksort($utf_sort);
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- $utf_sort = array();
+ $tmp .= implode('', $utf_chars);
}
+ $utf_sort = array();
+ }
- $tmp .= UTF8_REPLACEMENT;
- $dump = $sort = 0;
+ $tmp .= UTF8_REPLACEMENT;
+ $dump = $sort = 0;
- $tmp_pos = $starter_pos = $pos;
- continue 2;
- }
- break;
+ $tmp_pos = $starter_pos = $pos;
+ continue 2;
+ }
+ break;
- case "\xE0":
- if ($utf_char <= "\xE0\x9F\xBF")
+ case "\xE0":
+ if ($utf_char <= "\xE0\x9F\xBF")
+ {
+ // Unicode char U+0000..U+07FF encoded in 3 bytes
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+
+ if (!empty($utf_sort))
{
- // Unicode char U+0000..U+07FF encoded in 3 bytes
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ ksort($utf_sort);
- if (!empty($utf_sort))
+ foreach ($utf_sort as $utf_chars)
{
- ksort($utf_sort);
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- $utf_sort = array();
+ $tmp .= implode('', $utf_chars);
}
+ $utf_sort = array();
+ }
- $tmp .= UTF8_REPLACEMENT;
- $dump = $sort = 0;
+ $tmp .= UTF8_REPLACEMENT;
+ $dump = $sort = 0;
- $tmp_pos = $starter_pos = $pos;
- continue 2;
- }
- break;
+ $tmp_pos = $starter_pos = $pos;
+ continue 2;
+ }
+ break;
- case "\xF0":
- if ($utf_char <= "\xF0\x8F\xBF\xBF")
+ case "\xF0":
+ if ($utf_char <= "\xF0\x8F\xBF\xBF")
+ {
+ // Unicode char U+0000..U+FFFF encoded in 4 bytes
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+
+ if (!empty($utf_sort))
{
- // Unicode char U+0000..U+FFFF encoded in 4 bytes
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ ksort($utf_sort);
- if (!empty($utf_sort))
+ foreach ($utf_sort as $utf_chars)
{
- ksort($utf_sort);
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- $utf_sort = array();
+ $tmp .= implode('', $utf_chars);
}
+ $utf_sort = array();
+ }
- $tmp .= UTF8_REPLACEMENT;
- $dump = $sort = 0;
+ $tmp .= UTF8_REPLACEMENT;
+ $dump = $sort = 0;
- $tmp_pos = $starter_pos = $pos;
- continue 2;
- }
- break;
+ $tmp_pos = $starter_pos = $pos;
+ continue 2;
+ }
+ break;
- default:
- if ($utf_char > UTF8_MAX)
+ default:
+ if ($utf_char > UTF8_MAX)
+ {
+ // Out of the Unicode range
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+
+ if (!empty($utf_sort))
{
- // Out of the Unicode range
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+ ksort($utf_sort);
- if (!empty($utf_sort))
+ foreach ($utf_sort as $utf_chars)
{
- ksort($utf_sort);
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- $utf_sort = array();
+ $tmp .= implode('', $utf_chars);
}
+ $utf_sort = array();
+ }
- $tmp .= UTF8_REPLACEMENT;
- $dump = $sort = 0;
+ $tmp .= UTF8_REPLACEMENT;
+ $dump = $sort = 0;
- $tmp_pos = $starter_pos = $pos;
- continue 2;
- }
- break;
- }
+ $tmp_pos = $starter_pos = $pos;
+ continue 2;
+ }
+ break;
}
}
- else
- {
- // Hangul syllable
- $idx = (((ord($utf_char[0]) & 0x0F) << 12) | ((ord($utf_char[1]) & 0x3F) << 6) | (ord($utf_char[2]) & 0x3F)) - UNICODE_HANGUL_SBASE;
+ }
+ else
+ {
+ // Hangul syllable
+ $idx = (((ord($utf_char[0]) & 0x0F) << 12) | ((ord($utf_char[1]) & 0x3F) << 6) | (ord($utf_char[2]) & 0x3F)) - UNICODE_HANGUL_SBASE;
- // LIndex can only range from 0 to 18, therefore it cannot influence the first two bytes of the L Jamo, which allows us to hardcode them (based on LBase).
- //
- // The same goes for VIndex, but for TIndex there's a catch: the value of the third byte could exceed 0xBF and we would have to increment the second byte
- if ($tIndex = $idx % UNICODE_HANGUL_TCOUNT)
+ // LIndex can only range from 0 to 18, therefore it cannot influence the first two bytes of the L Jamo, which allows us to hardcode them (based on LBase).
+ //
+ // The same goes for VIndex, but for TIndex there's a catch: the value of the third byte could exceed 0xBF and we would have to increment the second byte
+ if ($tIndex = $idx % UNICODE_HANGUL_TCOUNT)
+ {
+ if ($tIndex < 25)
{
- if ($tIndex < 25)
- {
- $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x86\x00";
- $utf_char[8] = chr(0xA7 + $tIndex);
- }
- else
- {
- $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x87\x00";
- $utf_char[8] = chr(0x67 + $tIndex);
- }
+ $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x86\x00";
+ $utf_char[8] = chr(0xA7 + $tIndex);
}
else
{
- $utf_char = "\xE1\x84\x00\xE1\x85\x00";
+ $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x87\x00";
+ $utf_char[8] = chr(0x67 + $tIndex);
}
-
- $utf_char[2] = chr(0x80 + (int) ($idx / UNICODE_HANGUL_NCOUNT));
- $utf_char[5] = chr(0xA1 + (int) (($idx % UNICODE_HANGUL_NCOUNT) / UNICODE_HANGUL_TCOUNT));
-
- // Just like other decompositions, the resulting Jamos must be dumped to the tmp string
- $dump = 1;
}
-
- // Do we need to dump stuff to the tmp string?
- if ($dump)
+ else
{
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
-
- // Dump combiners
- if (!empty($utf_sort))
- {
- if ($sort)
- {
- ksort($utf_sort);
- }
-
- foreach ($utf_sort as $utf_chars)
- {
- $tmp .= implode('', $utf_chars);
- }
- }
-
- $tmp .= $utf_char;
- $dump = $sort = 0;
- $tmp_pos = $pos;
+ $utf_char = "\xE1\x84\x00\xE1\x85\x00";
}
- $last_cc = 0;
- $utf_sort = array();
- $starter_pos = $pos;
+ $utf_char[2] = chr(0x80 + (int) ($idx / UNICODE_HANGUL_NCOUNT));
+ $utf_char[5] = chr(0xA1 + (int) (($idx % UNICODE_HANGUL_NCOUNT) / UNICODE_HANGUL_TCOUNT));
+
+ // Just like other decompositions, the resulting Jamos must be dumped to the tmp string
+ $dump = 1;
}
- }
- else
- {
- // ASCII char, which happens to be a starter (as any other ASCII char)
+
+ // Do we need to dump stuff to the tmp string?
if ($dump)
{
$tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
@@ -1530,15 +1409,9 @@ else
}
}
- $tmp .= $str[$pos];
+ $tmp .= $utf_char;
$dump = $sort = 0;
- $tmp_pos = ++$pos;
-
- $pos += strspn($str, UTF8_ASCII_RANGE, $pos);
- }
- else
- {
- $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos);
+ $tmp_pos = $pos;
}
$last_cc = 0;
@@ -1546,48 +1419,84 @@ else
$starter_pos = $pos;
}
}
- while ($pos < $len);
-
- // Now is time to return the string
- if ($dump)
+ else
{
- $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
-
- // Dump combiners
- if (!empty($utf_sort))
+ // ASCII char, which happens to be a starter (as any other ASCII char)
+ if ($dump)
{
- if ($sort)
- {
- ksort($utf_sort);
- }
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
- foreach ($utf_sort as $utf_chars)
+ // Dump combiners
+ if (!empty($utf_sort))
{
- $tmp .= implode('', $utf_chars);
+ if ($sort)
+ {
+ ksort($utf_sort);
+ }
+
+ foreach ($utf_sort as $utf_chars)
+ {
+ $tmp .= implode('', $utf_chars);
+ }
}
- }
- return $tmp;
+ $tmp .= $str[$pos];
+ $dump = $sort = 0;
+ $tmp_pos = ++$pos;
+ $pos += strspn($str, UTF8_ASCII_RANGE, $pos);
+ }
+ else
+ {
+ $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos);
+ }
+
+ $last_cc = 0;
+ $utf_sort = array();
+ $starter_pos = $pos;
}
- else if ($tmp_pos)
+ }
+ while ($pos < $len);
+
+ // Now is time to return the string
+ if ($dump)
+ {
+ $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos);
+
+ // Dump combiners
+ if (!empty($utf_sort))
{
- // If the $tmp_pos cursor was moved then at least one character was not in normal form. Replace $str with the fixed version
- if ($tmp_pos == $len)
+ if ($sort)
{
- // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str
- return $tmp;
+ ksort($utf_sort);
}
- else
+
+ foreach ($utf_sort as $utf_chars)
{
- // The rightmost chunk of $str has not been appended to $tmp yet
- return $tmp . substr($str, $tmp_pos);
+ $tmp .= implode('', $utf_chars);
}
}
- // The string was already in normal form
- return $str;
+ return $tmp;
+
}
+ else if ($tmp_pos)
+ {
+ // If the $tmp_pos cursor was moved then at least one character was not in normal form. Replace $str with the fixed version
+ if ($tmp_pos == $len)
+ {
+ // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str
+ return $tmp;
+ }
+ else
+ {
+ // The rightmost chunk of $str has not been appended to $tmp yet
+ return $tmp . substr($str, $tmp_pos);
+ }
+ }
+
+ // The string was already in normal form
+ return $str;
}
}
diff --git a/phpBB/includes/utf/utf_tools.php b/phpBB/includes/utf/utf_tools.php
index aa29159d5e..7a2b536e97 100644
--- a/phpBB/includes/utf/utf_tools.php
+++ b/phpBB/includes/utf/utf_tools.php
@@ -781,7 +781,7 @@ function utf8_recode($string, $encoding)
*/
function utf8_encode_ncr($text)
{
- return preg_replace_callback('#[\\xC2-\\xF4][\\x80-\\xBF]?[\\x80-\\xBF]?[\\x80-\\xBF]+#', 'utf8_encode_ncr_callback', $text);
+ return preg_replace_callback('#[\\xC2-\\xF4][\\x80-\\xBF]{1,3}#', 'utf8_encode_ncr_callback', $text);
}
/**
@@ -824,7 +824,7 @@ function utf8_ord($chr)
break;
default:
- return $m;
+ return $chr;
}
}