diff -r be944660c56a -r 3d72ae0968f4 wp/wp-includes/block-supports/duotone.php --- a/wp/wp-includes/block-supports/duotone.php Wed Sep 21 18:19:35 2022 +0200 +++ b/wp/wp-includes/block-supports/duotone.php Tue Sep 27 16:37:53 2022 +0200 @@ -45,7 +45,6 @@ * * @param mixed $n Number of unknown type. * @param int $max Upper value of the range to bound to. - * * @return float Value in the range [0, 1]. */ function wp_tinycolor_bound01( $n, $max ) { @@ -70,7 +69,29 @@ } /** - * Round and convert values of an RGB object. + * Direct port of tinycolor's boundAlpha function to maintain consistency with + * how tinycolor works. + * + * @see https://github.com/bgrins/TinyColor + * + * @since 5.9.0 + * @access private + * + * @param mixed $n Number of unknown type. + * @return float Value in the range [0,1]. + */ +function _wp_tinycolor_bound_alpha( $n ) { + if ( is_numeric( $n ) ) { + $n = (float) $n; + if ( $n >= 0 && $n <= 1 ) { + return $n; + } + } + return 1; +} + +/** + * Rounds and converts values of an RGB object. * * Direct port of TinyColor's function, lightly simplified to maintain * consistency with TinyColor. @@ -81,7 +102,6 @@ * @access private * * @param array $rgb_color RGB object. - * * @return array Rounded and converted RGB object. */ function wp_tinycolor_rgb_to_rgb( $rgb_color ) { @@ -106,7 +126,6 @@ * @param float $p first component. * @param float $q second component. * @param float $t third component. - * * @return float R, G, or B component. */ function wp_tinycolor_hue_to_rgb( $p, $q, $t ) { @@ -129,7 +148,7 @@ } /** - * Convert an HSL object to an RGB object with converted and rounded values. + * Converts an HSL object to an RGB object with converted and rounded values. * * Direct port of TinyColor's function, lightly simplified to maintain * consistency with TinyColor. @@ -140,7 +159,6 @@ * @access private * * @param array $hsl_color HSL object. - * * @return array Rounded and converted RGB object. */ function wp_tinycolor_hsl_to_rgb( $hsl_color ) { @@ -170,8 +188,7 @@ /** * Parses hex, hsl, and rgb CSS strings using the same regex as TinyColor v1.4.2 - * used in the JavaScript. Only colors output from react-color are implemented - * and the alpha value is ignored as it is not used in duotone. + * used in the JavaScript. Only colors output from react-color are implemented. * * Direct port of TinyColor's function, lightly simplified to maintain * consistency with TinyColor. @@ -180,10 +197,10 @@ * @see https://github.com/casesandberg/react-color/ * * @since 5.8.0 + * @since 5.9.0 Added alpha processing. * @access private * * @param string $color_str CSS color string. - * * @return array RGB object. */ function wp_tinycolor_string_to_rgb( $color_str ) { @@ -199,93 +216,254 @@ $rgb_regexp = '/^rgb' . $permissive_match3 . '$/'; if ( preg_match( $rgb_regexp, $color_str, $match ) ) { - return wp_tinycolor_rgb_to_rgb( + $rgb = wp_tinycolor_rgb_to_rgb( + array( + 'r' => $match[1], + 'g' => $match[2], + 'b' => $match[3], + ) + ); + + $rgb['a'] = 1; + + return $rgb; + } + + $rgba_regexp = '/^rgba' . $permissive_match4 . '$/'; + if ( preg_match( $rgba_regexp, $color_str, $match ) ) { + $rgb = wp_tinycolor_rgb_to_rgb( array( 'r' => $match[1], 'g' => $match[2], 'b' => $match[3], ) ); - } - $rgba_regexp = '/^rgba' . $permissive_match4 . '$/'; - if ( preg_match( $rgba_regexp, $color_str, $match ) ) { - return wp_tinycolor_rgb_to_rgb( - array( - 'r' => $match[1], - 'g' => $match[2], - 'b' => $match[3], - ) - ); + $rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] ); + + return $rgb; } $hsl_regexp = '/^hsl' . $permissive_match3 . '$/'; if ( preg_match( $hsl_regexp, $color_str, $match ) ) { - return wp_tinycolor_hsl_to_rgb( + $rgb = wp_tinycolor_hsl_to_rgb( array( 'h' => $match[1], 's' => $match[2], 'l' => $match[3], ) ); + + $rgb['a'] = 1; + + return $rgb; } $hsla_regexp = '/^hsla' . $permissive_match4 . '$/'; if ( preg_match( $hsla_regexp, $color_str, $match ) ) { - return wp_tinycolor_hsl_to_rgb( + $rgb = wp_tinycolor_hsl_to_rgb( array( 'h' => $match[1], 's' => $match[2], 'l' => $match[3], ) ); + + $rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] ); + + return $rgb; } $hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/'; if ( preg_match( $hex8_regexp, $color_str, $match ) ) { - return wp_tinycolor_rgb_to_rgb( + $rgb = wp_tinycolor_rgb_to_rgb( array( 'r' => base_convert( $match[1], 16, 10 ), 'g' => base_convert( $match[2], 16, 10 ), 'b' => base_convert( $match[3], 16, 10 ), ) ); + + $rgb['a'] = _wp_tinycolor_bound_alpha( + base_convert( $match[4], 16, 10 ) / 255 + ); + + return $rgb; } $hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/'; if ( preg_match( $hex6_regexp, $color_str, $match ) ) { - return wp_tinycolor_rgb_to_rgb( + $rgb = wp_tinycolor_rgb_to_rgb( array( 'r' => base_convert( $match[1], 16, 10 ), 'g' => base_convert( $match[2], 16, 10 ), 'b' => base_convert( $match[3], 16, 10 ), ) ); + + $rgb['a'] = 1; + + return $rgb; } $hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/'; if ( preg_match( $hex4_regexp, $color_str, $match ) ) { - return wp_tinycolor_rgb_to_rgb( + $rgb = wp_tinycolor_rgb_to_rgb( + array( + 'r' => base_convert( $match[1] . $match[1], 16, 10 ), + 'g' => base_convert( $match[2] . $match[2], 16, 10 ), + 'b' => base_convert( $match[3] . $match[3], 16, 10 ), + ) + ); + + $rgb['a'] = _wp_tinycolor_bound_alpha( + base_convert( $match[4] . $match[4], 16, 10 ) / 255 + ); + + return $rgb; + } + + $hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/'; + if ( preg_match( $hex3_regexp, $color_str, $match ) ) { + $rgb = wp_tinycolor_rgb_to_rgb( array( 'r' => base_convert( $match[1] . $match[1], 16, 10 ), 'g' => base_convert( $match[2] . $match[2], 16, 10 ), 'b' => base_convert( $match[3] . $match[3], 16, 10 ), ) ); + + $rgb['a'] = 1; + + return $rgb; } - $hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/'; - if ( preg_match( $hex3_regexp, $color_str, $match ) ) { - return wp_tinycolor_rgb_to_rgb( - array( - 'r' => base_convert( $match[1] . $match[1], 16, 10 ), - 'g' => base_convert( $match[2] . $match[2], 16, 10 ), - 'b' => base_convert( $match[3] . $match[3], 16, 10 ), - ) + /* + * The JS color picker considers the string "transparent" to be a hex value, + * so we need to handle it here as a special case. + */ + if ( 'transparent' === $color_str ) { + return array( + 'r' => 0, + 'g' => 0, + 'b' => 0, + 'a' => 0, ); } } +/** + * Returns the prefixed id for the duotone filter for use as a CSS id. + * + * @since 5.9.1 + * @access private + * + * @param array $preset Duotone preset value as seen in theme.json. + * @return string Duotone filter CSS id. + */ +function wp_get_duotone_filter_id( $preset ) { + if ( ! isset( $preset['slug'] ) ) { + return ''; + } + + return 'wp-duotone-' . $preset['slug']; +} + +/** + * Returns the CSS filter property url to reference the rendered SVG. + * + * @since 5.9.0 + * @access private + * + * @param array $preset Duotone preset value as seen in theme.json. + * @return string Duotone CSS filter property url value. + */ +function wp_get_duotone_filter_property( $preset ) { + $filter_id = wp_get_duotone_filter_id( $preset ); + return "url('#" . $filter_id . "')"; +} + +/** + * Returns the duotone filter SVG string for the preset. + * + * @since 5.9.1 + * @access private + * + * @param array $preset Duotone preset value as seen in theme.json. + * @return string Duotone SVG filter. + */ +function wp_get_duotone_filter_svg( $preset ) { + $filter_id = wp_get_duotone_filter_id( $preset ); + + $duotone_values = array( + 'r' => array(), + 'g' => array(), + 'b' => array(), + 'a' => array(), + ); + + if ( ! isset( $preset['colors'] ) || ! is_array( $preset['colors'] ) ) { + $preset['colors'] = array(); + } + + foreach ( $preset['colors'] as $color_str ) { + $color = wp_tinycolor_string_to_rgb( $color_str ); + + $duotone_values['r'][] = $color['r'] / 255; + $duotone_values['g'][] = $color['g'] / 255; + $duotone_values['b'][] = $color['b'] / 255; + $duotone_values['a'][] = $color['a']; + } + + ob_start(); + + ?> + + + + + + + + + + + + + + + + + <', $svg ); + $svg = trim( $svg ); + } + + return $svg; +} /** * Registers the style and colors block attributes for block types that support it. @@ -322,7 +500,6 @@ * * @param string $block_content Rendered block content. * @param array $block Block object. - * * @return string Filtered block content. */ function wp_render_duotone_support( $block_content, $block ) { @@ -342,84 +519,61 @@ return $block_content; } - $duotone_colors = $block['attrs']['style']['color']['duotone']; - - $duotone_values = array( - 'r' => array(), - 'g' => array(), - 'b' => array(), + $filter_preset = array( + 'slug' => wp_unique_id( sanitize_key( implode( '-', $block['attrs']['style']['color']['duotone'] ) . '-' ) ), + 'colors' => $block['attrs']['style']['color']['duotone'], ); - foreach ( $duotone_colors as $color_str ) { - $color = wp_tinycolor_string_to_rgb( $color_str ); + $filter_property = wp_get_duotone_filter_property( $filter_preset ); + $filter_id = wp_get_duotone_filter_id( $filter_preset ); + $filter_svg = wp_get_duotone_filter_svg( $filter_preset ); - $duotone_values['r'][] = $color['r'] / 255; - $duotone_values['g'][] = $color['g'] / 255; - $duotone_values['b'][] = $color['b'] / 255; + $scope = '.' . $filter_id; + $selectors = explode( ',', $duotone_support ); + $scoped = array(); + foreach ( $selectors as $sel ) { + $scoped[] = $scope . ' ' . trim( $sel ); } + $selector = implode( ', ', $scoped ); - $duotone_id = 'wp-duotone-filter-' . uniqid(); + // !important is needed because these styles render before global styles, + // and they should be overriding the duotone filters set by global styles. + $filter_style = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG + ? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n" + : $selector . '{filter:' . $filter_property . ' !important;}'; - $selectors = explode( ',', $duotone_support ); - $selectors_scoped = array_map( - function ( $selector ) use ( $duotone_id ) { - return '.' . $duotone_id . ' ' . trim( $selector ); - }, - $selectors - ); - $selectors_group = implode( ', ', $selectors_scoped ); + wp_register_style( $filter_id, false, array(), true, true ); + wp_add_inline_style( $filter_id, $filter_style ); + wp_enqueue_style( $filter_id ); + + add_action( + 'wp_footer', + static function () use ( $filter_svg, $selector ) { + echo $filter_svg; - ob_start(); - - ?> - - - - - - - - values=".299 .587 .114 0 0 - .299 .587 .114 0 0 - .299 .587 .114 0 0 - 0 0 0 1 0" - - /> - - - - - - - - - -