| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | Copyright CiviCRM LLC. All rights reserved. | |
| 5 | | | |
| 6 | | This work is published under the GNU AGPLv3 license with some | |
| 7 | | permitted exceptions and without any warranty. For full license | |
| 8 | | and copyright information, see https://civicrm.org/licensing | |
| 9 | +--------------------------------------------------------------------+ |
| 10 | */ |
| 11 | |
| 12 | /** |
| 13 | * |
| 14 | * @package CRM |
| 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
| 16 | */ |
| 17 | |
| 18 | /** |
| 19 | * Static utility functions for working with colors |
| 20 | */ |
| 21 | class CRM_Utils_Color { |
| 22 | |
| 23 | const COLOR_FILE = '[civicrm.root]/bower_components/css-color-names/css-color-names.json'; |
| 24 | |
| 25 | /** |
| 26 | * Determine the appropriate text color for a given background. |
| 27 | * |
| 28 | * Based on YIQ value. |
| 29 | * |
| 30 | * @param string $color |
| 31 | * @param string $black |
| 32 | * @param string $white |
| 33 | * @return string |
| 34 | */ |
| 35 | public static function getContrast($color, $black = 'black', $white = 'white') { |
| 36 | list($r, $g, $b) = self::getRgb($color); |
| 37 | $yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000; |
| 38 | return ($yiq >= 128) ? $black : $white; |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Parse any color string into rgb decimal values |
| 43 | * |
| 44 | * Accepted formats: |
| 45 | * Full hex: "#ffffff" |
| 46 | * Short hex: "#fff" |
| 47 | * Color name "white" |
| 48 | * RGB notation: "rgb(255, 255, 255)" |
| 49 | * |
| 50 | * @param string $color |
| 51 | * @return int[]|null |
| 52 | * [red, green, blue] |
| 53 | */ |
| 54 | public static function getRgb($color) { |
| 55 | $color = str_replace(' ', '', $color); |
| 56 | $color = self::nameToHex($color) ?? $color; |
| 57 | if (strpos($color, 'rgb(') === 0) { |
| 58 | return explode(',', substr($color, 4, strpos($color, ')') - 4)); |
| 59 | } |
| 60 | $color = ltrim($color, '#'); |
| 61 | if (strlen($color) === 3) { |
| 62 | $color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2]; |
| 63 | } |
| 64 | if (!CRM_Utils_Rule::color('#' . $color)) { |
| 65 | return NULL; |
| 66 | } |
| 67 | return [ |
| 68 | hexdec(substr($color, 0, 2)), |
| 69 | hexdec(substr($color, 2, 2)), |
| 70 | hexdec(substr($color, 4, 2)), |
| 71 | ]; |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Calculate a highlight color from a base color |
| 76 | * |
| 77 | * @param $color |
| 78 | * @return string |
| 79 | */ |
| 80 | public static function getHighlight($color) { |
| 81 | $rgb = self::getRgb($color); |
| 82 | $avg = array_sum($rgb) / 3; |
| 83 | foreach ($rgb as &$v) { |
| 84 | if ($avg > 242) { |
| 85 | // For very bright values, lower the brightness |
| 86 | $v -= 50; |
| 87 | } |
| 88 | else { |
| 89 | // Bump up brightness on a nonlinear curve - darker colors get more of a boost |
| 90 | $v = min(255, intval((-.0035 * ($v - 242) ** 2) + 260)); |
| 91 | } |
| 92 | } |
| 93 | return self::rgbToHex($rgb); |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * Convert named color (e.g. springgreen) to hex |
| 98 | * |
| 99 | * @param $colorName |
| 100 | * @return string|null |
| 101 | */ |
| 102 | public static function nameToHex($colorName) { |
| 103 | if (strpos($colorName, '#') !== FALSE || strpos($colorName, '(') !== FALSE) { |
| 104 | return NULL; |
| 105 | } |
| 106 | if (empty(Civi::$statics[__CLASS__]['names'])) { |
| 107 | Civi::$statics[__CLASS__]['names'] = json_decode(file_get_contents(Civi::paths()->getPath(self::COLOR_FILE)), TRUE); |
| 108 | } |
| 109 | return Civi::$statics[__CLASS__]['names'][strtolower($colorName)] ?? NULL; |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Converts rgb array to hex string |
| 114 | * |
| 115 | * @param int[] $rgb |
| 116 | * @return string |
| 117 | */ |
| 118 | public static function rgbToHex($rgb) { |
| 119 | $ret = '#'; |
| 120 | foreach ($rgb as $dec) { |
| 121 | $ret .= str_pad(dechex($dec), 2, '0', STR_PAD_LEFT); |
| 122 | } |
| 123 | return $ret; |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Validate color input and convert it to standard hex notation |
| 128 | * |
| 129 | * @param string $color |
| 130 | * @return bool |
| 131 | */ |
| 132 | public static function normalize(&$color) { |
| 133 | $rgb = self::getRgb($color); |
| 134 | if ($rgb) { |
| 135 | $color = self::rgbToHex($rgb); |
| 136 | return TRUE; |
| 137 | } |
| 138 | return FALSE; |
| 139 | } |
| 140 | |
| 141 | } |