See Release Notes
Long Term Support Release
Differences Between: [Versions 311 and 401] [Versions 400 and 401]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace tool_brickfield\local\htmlchecker\common; 18 19 /** 20 * Helper test base for tests dealing with color difference and luminosity. 21 * 22 * @package tool_brickfield 23 * @copyright 2020 onward: Brickfield Education Labs, www.brickfield.ie 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 class brickfield_accessibility_color_test extends brickfield_accessibility_test { 27 28 /** @var string[] Define colour codes. */ 29 public $colornames = [ 30 'aliceblue' => 'f0f8ff', 31 'antiquewhite' => 'faebd7', 32 'aqua' => '00ffff', 33 'aquamarine' => '7fffd4', 34 'azure' => 'f0ffff', 35 'beige' => 'f5f5dc', 36 'bisque' => 'ffe4c4', 37 'black' => '000000', 38 'blanchedalmond' => 'ffebcd', 39 'blue' => '0000ff', 40 'blueviolet' => '8a2be2', 41 'brown' => 'a52a2a', 42 'burlywood' => 'deb887', 43 'cadetblue' => '5f9ea0', 44 'chartreuse' => '7fff00', 45 'chocolate' => 'd2691e', 46 'coral' => 'ff7f50', 47 'cornflowerblue' => '6495ed', 48 'cornsilk' => 'fff8dc', 49 'crimson' => 'dc143c', 50 'cyan' => '00ffff', 51 'darkblue' => '00008b', 52 'darkcyan' => '008b8b', 53 'darkgoldenrod' => 'b8860b', 54 'darkgray' => 'a9a9a9', 55 'darkgreen' => '006400', 56 'darkkhaki' => 'bdb76b', 57 'darkmagenta' => '8b008b', 58 'darkolivegreen' => '556b2f', 59 'darkorange' => 'ff8c00', 60 'darkorchid' => '9932cc', 61 'darkred' => '8b0000', 62 'darksalmon' => 'e9967a', 63 'darkseagreen' => '8fbc8f', 64 'darkslateblue' => '483d8b', 65 'darkslategray' => '2f4f4f', 66 'darkturquoise' => '00ced1', 67 'darkviolet' => '9400d3', 68 'deeppink' => 'ff1493', 69 'deepskyblue' => '00bfff', 70 'dimgray' => '696969', 71 'dodgerblue' => '1e90ff', 72 'firebrick' => 'b22222', 73 'floralwhite' => 'fffaf0', 74 'forestgreen' => '228b22', 75 'fuchsia' => 'ff00ff', 76 'gainsboro' => 'dcdcdc', 77 'ghostwhite' => 'f8f8ff', 78 'gold' => 'ffd700', 79 'goldenrod' => 'daa520', 80 'gray' => '808080', 81 'green' => '008000', 82 'greenyellow' => 'adff2f', 83 'grey' => '808080', 84 'honeydew' => 'f0fff0', 85 'hotpink' => 'ff69b4', 86 'indianred' => 'cd5c5c', 87 'indigo' => '4b0082', 88 'ivory' => 'fffff0', 89 'khaki' => 'f0e68c', 90 'lavender' => 'e6e6fa', 91 'lavenderblush' => 'fff0f5', 92 'lawngreen' => '7cfc00', 93 'lemonchiffon' => 'fffacd', 94 'lightblue' => 'add8e6', 95 'lightcoral' => 'f08080', 96 'lightcyan' => 'e0ffff', 97 'lightgoldenrodyellow' => 'fafad2', 98 'lightgrey' => 'd3d3d3', 99 'lightgreen' => '90ee90', 100 'lightpink' => 'ffb6c1', 101 'lightsalmon' => 'ffa07a', 102 'lightseagreen' => '20b2aa', 103 'lightskyblue' => '87cefa', 104 'lightslategray' => '778899', 105 'lightsteelblue' => 'b0c4de', 106 'lightyellow' => 'ffffe0', 107 'lime' => '00ff00', 108 'limegreen' => '32cd32', 109 'linen' => 'faf0e6', 110 'magenta' => 'ff00ff', 111 'maroon' => '800000', 112 'mediumaquamarine' => '66cdaa', 113 'mediumblue' => '0000cd', 114 'mediumorchid' => 'ba55d3', 115 'mediumpurple' => '9370d8', 116 'mediumseagreen' => '3cb371', 117 'mediumslateblue' => '7b68ee', 118 'mediumspringgreen' => '00fa9a', 119 'mediumturquoise' => '48d1cc', 120 'mediumvioletred' => 'c71585', 121 'midnightblue' => '191970', 122 'mintcream' => 'f5fffa', 123 'mistyrose' => 'ffe4e1', 124 'moccasin' => 'ffe4b5', 125 'navajowhite' => 'ffdead', 126 'navy' => '000080', 127 'oldlace' => 'fdf5e6', 128 'olive' => '808000', 129 'olivedrab' => '6b8e23', 130 'orange' => 'ffa500', 131 'orangered' => 'ff4500', 132 'orchid' => 'da70d6', 133 'palegoldenrod' => 'eee8aa', 134 'palegreen' => '98fb98', 135 'paleturquoise' => 'afeeee', 136 'palevioletred' => 'd87093', 137 'papayawhip' => 'ffefd5', 138 'peachpuff' => 'ffdab9', 139 'peru' => 'cd853f', 140 'pink' => 'ffc0cb', 141 'plum' => 'dda0dd', 142 'powderblue' => 'b0e0e6', 143 'purple' => '800080', 144 'red' => 'ff0000', 145 'rosybrown' => 'bc8f8f', 146 'royalblue' => '4169e1', 147 'saddlebrown' => '8b4513', 148 'salmon' => 'fa8072', 149 'sandybrown' => 'f4a460', 150 'seagreen' => '2e8b57', 151 'seashell' => 'fff5ee', 152 'sienna' => 'a0522d', 153 'silver' => 'c0c0c0', 154 'skyblue' => '87ceeb', 155 'slateblue' => '6a5acd', 156 'slategray' => '708090', 157 'snow' => 'fffafa', 158 'springgreen' => '00ff7f', 159 'steelblue' => '4682b4', 160 'tan' => 'd2b48c', 161 'teal' => '008080', 162 'thistle' => 'd8bfd8', 163 'tomato' => 'ff6347', 164 'turquoise' => '40e0d0', 165 'violet' => 'ee82ee', 166 'wheat' => 'f5deb3', 167 'white' => 'ffffff', 168 'whitesmoke' => 'f5f5f5', 169 'yellow' => 'ffff00', 170 'yellowgreen' => '9acd32' 171 ]; 172 173 /** @var string[] Define estimated relative font-size codes to pt values. */ 174 public $fontsizenames = [ 175 'xx-small' => 9, 176 'x-small' => 10, 177 'small' => 11, 178 'smaller' => 11, 179 'medium' => 12, 180 'large' => 14, 181 'larger' => 14, 182 'x-large' => 18, 183 'xx-large' => 24, 184 ]; 185 186 /** 187 * Helper method that finds the luminosity between the provided 188 * foreground and background parameters. 189 * @param string $foreground The HEX value of the foreground color 190 * @param string $background The HEX value of the background color 191 * @return float The luminosity contrast ratio between the colors 192 */ 193 public function get_luminosity(string $foreground, string $background): float { 194 if ($foreground == $background) { 195 return 0; 196 } 197 $forergb = $this->get_rgb($foreground); 198 $backrgb = $this->get_rgb($background); 199 200 // If get_rgb returns null for either, return 0. 201 if ($forergb === null || $backrgb === null) { 202 return 0; 203 } 204 205 return $this->luminosity($forergb['r'], $backrgb['r'], 206 $forergb['g'], $backrgb['g'], 207 $forergb['b'], $backrgb['b']); 208 } 209 210 /** 211 * Returns the luminosity between two colors 212 * @param string $r The first Red value 213 * @param string $r2 The second Red value 214 * @param string $g The first Green value 215 * @param string $g2 The second Green value 216 * @param string $b The first Blue value 217 * @param string $b2 The second Blue value 218 * @return float The luminosity contrast ratio between the colors 219 */ 220 public function luminosity(string $r, string $r2, string $g, string $g2, string $b, string $b2): float { 221 $rsrgb = $r / 255; 222 $gsrgb = $g / 255; 223 $bsrgb = $b / 255; 224 $r3 = ($rsrgb <= 0.03928) ? $rsrgb / 12.92 : pow(($rsrgb + 0.055) / 1.055, 2.4); 225 $g3 = ($gsrgb <= 0.03928) ? $gsrgb / 12.92 : pow(($gsrgb + 0.055) / 1.055, 2.4); 226 $b3 = ($bsrgb <= 0.03928) ? $bsrgb / 12.92 : pow(($bsrgb + 0.055) / 1.055, 2.4); 227 228 $rsrgb2 = $r2 / 255; 229 $gsrgb2 = $g2 / 255; 230 $bsrgb2 = $b2 / 255; 231 $r4 = ($rsrgb2 <= 0.03928) ? $rsrgb2 / 12.92 : pow(($rsrgb2 + 0.055) / 1.055, 2.4); 232 $g4 = ($gsrgb2 <= 0.03928) ? $gsrgb2 / 12.92 : pow(($gsrgb2 + 0.055) / 1.055, 2.4); 233 $b4 = ($bsrgb2 <= 0.03928) ? $bsrgb2 / 12.92 : pow(($bsrgb2 + 0.055) / 1.055, 2.4); 234 235 if ($r + $g + $b <= $r2 + $g2 + $b2) { 236 $l2 = (.2126 * $r3 + 0.7152 * $g3 + 0.0722 * $b3); 237 $l1 = (.2126 * $r4 + 0.7152 * $g4 + 0.0722 * $b4); 238 } else { 239 $l1 = (.2126 * $r3 + 0.7152 * $g3 + 0.0722 * $b3); 240 $l2 = (.2126 * $r4 + 0.7152 * $g4 + 0.0722 * $b4); 241 } 242 243 // Increase round to 4 to avoid a 4.49 contrast being round up to a false pass of 4.5. 244 $luminosity = round(($l1 + 0.05) / ($l2 + 0.05), 4); 245 return $luminosity; 246 } 247 248 249 /** 250 * Returns the decimal equivalents for a HEX color. Returns null if it cannot be determined. 251 * @param string $color The hex color value 252 * @return array|null An array where 'r' is the Red value, 'g' is Green, and 'b' is Blue 253 */ 254 public function get_rgb(string $color): ?array { 255 $color = $this->convert_color($color); 256 $c = str_split($color, 2); 257 if (count($c) != 3) { 258 return null; 259 } 260 $results = ['r' => hexdec($c[0]), 'g' => hexdec($c[1]), 'b' => hexdec($c[2])]; 261 return $results; 262 } 263 264 /** 265 * Converts multiple color or background styles into a simple hex string 266 * @param string $color The color attribute to convert (this can also be a multi-value css background value) 267 * @return string A standard CSS hex value for the color 268 */ 269 public function convert_color(string $color): string { 270 $color = trim($color); 271 if (strpos($color, ' ') !== false) { 272 $colors = explode(' ', $color); 273 foreach ($colors as $backgroundpart) { 274 if (substr(trim($backgroundpart), 0, 1) == '#' || 275 in_array(trim($backgroundpart), array_keys($this->colornames)) || 276 strtolower(substr(trim($backgroundpart), 0, 3)) == 'rgb') { 277 $color = $backgroundpart; 278 } 279 } 280 } 281 // Normal hex color. 282 if (substr($color, 0, 1) == '#') { 283 if (strlen($color) == 7) { 284 return str_replace('#', '', $color); 285 } else if (strlen($color) == 4) { 286 return substr($color, 1, 1) . substr($color, 1, 1) . 287 substr($color, 2, 1) . substr($color, 2, 1) . 288 substr($color, 3, 1) . substr($color, 3, 1); 289 } else { 290 return "000000"; 291 } 292 } 293 // Named Color. 294 if (in_array($color, array_keys($this->colornames))) { 295 return $this->colornames[$color]; 296 } 297 // RGB values. 298 if (strtolower(substr($color, 0, 3)) == 'rgb') { 299 $colors = explode(',', trim(str_replace('rgb(', '', $color), '()')); 300 if (count($colors) != 3) { 301 return false; 302 } 303 $r = intval($colors[0]); 304 $g = intval($colors[1]); 305 $b = intval($colors[2]); 306 307 $r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r)); 308 $g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g)); 309 $b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b)); 310 311 $color = (strlen($r) < 2 ? '0' : '') . $r; 312 $color .= (strlen($g) < 2 ? '0' : '') . $g; 313 $color .= (strlen($b) < 2 ? '0' : '') . $b; 314 return $color; 315 } 316 317 return ''; 318 } 319 320 /** 321 * Returns the WAIERT contrast between two colors 322 * @param string $foreground 323 * @param string $background 324 * @return array 325 * @see get_luminosity 326 */ 327 public function get_wai_ert_contrast(string $foreground, string $background): array { 328 $forergb = $this->get_rgb($foreground); 329 $backrgb = $this->get_rgb($background); 330 331 // If get_rgb returns null for either, return 0. 332 if ($forergb === null || $backrgb === null) { 333 return []; 334 } 335 336 $diffs = $this->get_wai_diffs($forergb, $backrgb); 337 338 return $diffs['red'] + $diffs['green'] + $diffs['blue']; 339 } 340 341 /** 342 * Returns the WAI ERT Brightness between two colors 343 * @param string $foreground 344 * @param string $background 345 * @return float|int 346 */ 347 public function get_wai_ert_brightness(string $foreground, string $background): float { 348 $forergb = $this->get_rgb($foreground); 349 $backrgb = $this->get_rgb($background); 350 351 // If get_rgb returns null for either, return 0. 352 if ($forergb === null || $backrgb === null) { 353 return 0; 354 } 355 356 $color = $this->get_wai_diffs($forergb, $backrgb); 357 return (($color['red'] * 299) + ($color['green'] * 587) + ($color['blue'] * 114)) / 1000; 358 } 359 360 /** 361 * Get the wai differences. 362 * @param array $forergb 363 * @param array $backrgb 364 * @return array 365 */ 366 public function get_wai_diffs(array $forergb, array $backrgb): array { 367 $reddiff = ($forergb['r'] > $backrgb['r']) 368 ? $forergb['r'] - $backrgb['r'] 369 : $backrgb['r'] - $forergb['r']; 370 $greendiff = ($forergb['g'] > $backrgb['g']) 371 ? $forergb['g'] - $backrgb['g'] 372 : $backrgb['g'] - $forergb['g']; 373 374 $bluediff = ($forergb['b'] > $backrgb['b']) 375 ? $forergb['b'] - $backrgb['b'] 376 : $backrgb['b'] - $forergb['b']; 377 return ['red' => $reddiff, 'green' => $greendiff, 'blue' => $bluediff]; 378 } 379 380 /** 381 * Helper method that finds the estimated font-size for the provided 382 * string font-size parameter. 383 * @param string $fontsize The css font-size, in various formats 384 * @return int The estimated font-size 385 */ 386 public function get_fontsize(string $fontsize): int { 387 $newfontsize = 12; // Default value, in pt, equivalent to 16px. 388 389 // Search for rem, em, and px initially, typical font-size values. 390 $pos1 = stripos($fontsize, 'rem'); 391 $pos2 = stripos($fontsize, 'em'); 392 $pos3 = stripos($fontsize, 'px'); 393 if ($pos1 !== false) { 394 $rem = substr($fontsize, 0, -3); 395 $newfontsize = $newfontsize * $rem; 396 } else if ($pos2 !== false) { 397 $em = substr($fontsize, 0, -2); 398 $newfontsize = $newfontsize * $em; 399 } else if ($pos3 !== false) { 400 $px = substr($fontsize, 0, -2); 401 $newfontsize = 0.75 * $px; 402 } else if (in_array($fontsize, array_keys($this->fontsizenames))) { 403 $newfontsize = $this->fontsizenames[$fontsize]; 404 } else { 405 preg_match_all('!\d+!', $fontsize, $matches); 406 $newfontsize = $matches[0][0] ?? $newfontsize; 407 } 408 return (int) $newfontsize; 409 } 410 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body