wp/wp-includes/block-supports/duotone.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    30  *
    30  *
    31  * @package WordPress
    31  * @package WordPress
    32  * @since 5.8.0
    32  * @since 5.8.0
    33  */
    33  */
    34 
    34 
    35 /**
       
    36  * Takes input from [0, n] and returns it as [0, 1].
       
    37  *
       
    38  * Direct port of TinyColor's function, lightly simplified to maintain
       
    39  * consistency with TinyColor.
       
    40  *
       
    41  * @see https://github.com/bgrins/TinyColor
       
    42  *
       
    43  * @since 5.8.0
       
    44  * @access private
       
    45  *
       
    46  * @param mixed $n   Number of unknown type.
       
    47  * @param int   $max Upper value of the range to bound to.
       
    48  * @return float Value in the range [0, 1].
       
    49  */
       
    50 function wp_tinycolor_bound01( $n, $max ) {
       
    51 	if ( 'string' === gettype( $n ) && false !== strpos( $n, '.' ) && 1 === (float) $n ) {
       
    52 		$n = '100%';
       
    53 	}
       
    54 
       
    55 	$n = min( $max, max( 0, (float) $n ) );
       
    56 
       
    57 	// Automatically convert percentage into number.
       
    58 	if ( 'string' === gettype( $n ) && false !== strpos( $n, '%' ) ) {
       
    59 		$n = (int) ( $n * $max ) / 100;
       
    60 	}
       
    61 
       
    62 	// Handle floating point rounding errors.
       
    63 	if ( ( abs( $n - $max ) < 0.000001 ) ) {
       
    64 		return 1.0;
       
    65 	}
       
    66 
       
    67 	// Convert into [0, 1] range if it isn't already.
       
    68 	return ( $n % $max ) / (float) $max;
       
    69 }
       
    70 
       
    71 /**
       
    72  * Direct port of tinycolor's boundAlpha function to maintain consistency with
       
    73  * how tinycolor works.
       
    74  *
       
    75  * @see https://github.com/bgrins/TinyColor
       
    76  *
       
    77  * @since 5.9.0
       
    78  * @access private
       
    79  *
       
    80  * @param mixed $n Number of unknown type.
       
    81  * @return float Value in the range [0,1].
       
    82  */
       
    83 function _wp_tinycolor_bound_alpha( $n ) {
       
    84 	if ( is_numeric( $n ) ) {
       
    85 		$n = (float) $n;
       
    86 		if ( $n >= 0 && $n <= 1 ) {
       
    87 			return $n;
       
    88 		}
       
    89 	}
       
    90 	return 1;
       
    91 }
       
    92 
       
    93 /**
       
    94  * Rounds and converts values of an RGB object.
       
    95  *
       
    96  * Direct port of TinyColor's function, lightly simplified to maintain
       
    97  * consistency with TinyColor.
       
    98  *
       
    99  * @see https://github.com/bgrins/TinyColor
       
   100  *
       
   101  * @since 5.8.0
       
   102  * @access private
       
   103  *
       
   104  * @param array $rgb_color RGB object.
       
   105  * @return array Rounded and converted RGB object.
       
   106  */
       
   107 function wp_tinycolor_rgb_to_rgb( $rgb_color ) {
       
   108 	return array(
       
   109 		'r' => wp_tinycolor_bound01( $rgb_color['r'], 255 ) * 255,
       
   110 		'g' => wp_tinycolor_bound01( $rgb_color['g'], 255 ) * 255,
       
   111 		'b' => wp_tinycolor_bound01( $rgb_color['b'], 255 ) * 255,
       
   112 	);
       
   113 }
       
   114 
       
   115 /**
       
   116  * Helper function for hsl to rgb conversion.
       
   117  *
       
   118  * Direct port of TinyColor's function, lightly simplified to maintain
       
   119  * consistency with TinyColor.
       
   120  *
       
   121  * @see https://github.com/bgrins/TinyColor
       
   122  *
       
   123  * @since 5.8.0
       
   124  * @access private
       
   125  *
       
   126  * @param float $p first component.
       
   127  * @param float $q second component.
       
   128  * @param float $t third component.
       
   129  * @return float R, G, or B component.
       
   130  */
       
   131 function wp_tinycolor_hue_to_rgb( $p, $q, $t ) {
       
   132 	if ( $t < 0 ) {
       
   133 		$t += 1;
       
   134 	}
       
   135 	if ( $t > 1 ) {
       
   136 		$t -= 1;
       
   137 	}
       
   138 	if ( $t < 1 / 6 ) {
       
   139 		return $p + ( $q - $p ) * 6 * $t;
       
   140 	}
       
   141 	if ( $t < 1 / 2 ) {
       
   142 		return $q;
       
   143 	}
       
   144 	if ( $t < 2 / 3 ) {
       
   145 		return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
       
   146 	}
       
   147 	return $p;
       
   148 }
       
   149 
       
   150 /**
       
   151  * Converts an HSL object to an RGB object with converted and rounded values.
       
   152  *
       
   153  * Direct port of TinyColor's function, lightly simplified to maintain
       
   154  * consistency with TinyColor.
       
   155  *
       
   156  * @see https://github.com/bgrins/TinyColor
       
   157  *
       
   158  * @since 5.8.0
       
   159  * @access private
       
   160  *
       
   161  * @param array $hsl_color HSL object.
       
   162  * @return array Rounded and converted RGB object.
       
   163  */
       
   164 function wp_tinycolor_hsl_to_rgb( $hsl_color ) {
       
   165 	$h = wp_tinycolor_bound01( $hsl_color['h'], 360 );
       
   166 	$s = wp_tinycolor_bound01( $hsl_color['s'], 100 );
       
   167 	$l = wp_tinycolor_bound01( $hsl_color['l'], 100 );
       
   168 
       
   169 	if ( 0 === $s ) {
       
   170 		// Achromatic.
       
   171 		$r = $l;
       
   172 		$g = $l;
       
   173 		$b = $l;
       
   174 	} else {
       
   175 		$q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
       
   176 		$p = 2 * $l - $q;
       
   177 		$r = wp_tinycolor_hue_to_rgb( $p, $q, $h + 1 / 3 );
       
   178 		$g = wp_tinycolor_hue_to_rgb( $p, $q, $h );
       
   179 		$b = wp_tinycolor_hue_to_rgb( $p, $q, $h - 1 / 3 );
       
   180 	}
       
   181 
       
   182 	return array(
       
   183 		'r' => $r * 255,
       
   184 		'g' => $g * 255,
       
   185 		'b' => $b * 255,
       
   186 	);
       
   187 }
       
   188 
       
   189 /**
       
   190  * Parses hex, hsl, and rgb CSS strings using the same regex as TinyColor v1.4.2
       
   191  * used in the JavaScript. Only colors output from react-color are implemented.
       
   192  *
       
   193  * Direct port of TinyColor's function, lightly simplified to maintain
       
   194  * consistency with TinyColor.
       
   195  *
       
   196  * @see https://github.com/bgrins/TinyColor
       
   197  * @see https://github.com/casesandberg/react-color/
       
   198  *
       
   199  * @since 5.8.0
       
   200  * @since 5.9.0 Added alpha processing.
       
   201  * @access private
       
   202  *
       
   203  * @param string $color_str CSS color string.
       
   204  * @return array RGB object.
       
   205  */
       
   206 function wp_tinycolor_string_to_rgb( $color_str ) {
       
   207 	$color_str = strtolower( trim( $color_str ) );
       
   208 
       
   209 	$css_integer = '[-\\+]?\\d+%?';
       
   210 	$css_number  = '[-\\+]?\\d*\\.\\d+%?';
       
   211 
       
   212 	$css_unit = '(?:' . $css_number . ')|(?:' . $css_integer . ')';
       
   213 
       
   214 	$permissive_match3 = '[\\s|\\(]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')\\s*\\)?';
       
   215 	$permissive_match4 = '[\\s|\\(]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')\\s*\\)?';
       
   216 
       
   217 	$rgb_regexp = '/^rgb' . $permissive_match3 . '$/';
       
   218 	if ( preg_match( $rgb_regexp, $color_str, $match ) ) {
       
   219 		$rgb = wp_tinycolor_rgb_to_rgb(
       
   220 			array(
       
   221 				'r' => $match[1],
       
   222 				'g' => $match[2],
       
   223 				'b' => $match[3],
       
   224 			)
       
   225 		);
       
   226 
       
   227 		$rgb['a'] = 1;
       
   228 
       
   229 		return $rgb;
       
   230 	}
       
   231 
       
   232 	$rgba_regexp = '/^rgba' . $permissive_match4 . '$/';
       
   233 	if ( preg_match( $rgba_regexp, $color_str, $match ) ) {
       
   234 		$rgb = wp_tinycolor_rgb_to_rgb(
       
   235 			array(
       
   236 				'r' => $match[1],
       
   237 				'g' => $match[2],
       
   238 				'b' => $match[3],
       
   239 			)
       
   240 		);
       
   241 
       
   242 		$rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] );
       
   243 
       
   244 		return $rgb;
       
   245 	}
       
   246 
       
   247 	$hsl_regexp = '/^hsl' . $permissive_match3 . '$/';
       
   248 	if ( preg_match( $hsl_regexp, $color_str, $match ) ) {
       
   249 		$rgb = wp_tinycolor_hsl_to_rgb(
       
   250 			array(
       
   251 				'h' => $match[1],
       
   252 				's' => $match[2],
       
   253 				'l' => $match[3],
       
   254 			)
       
   255 		);
       
   256 
       
   257 		$rgb['a'] = 1;
       
   258 
       
   259 		return $rgb;
       
   260 	}
       
   261 
       
   262 	$hsla_regexp = '/^hsla' . $permissive_match4 . '$/';
       
   263 	if ( preg_match( $hsla_regexp, $color_str, $match ) ) {
       
   264 		$rgb = wp_tinycolor_hsl_to_rgb(
       
   265 			array(
       
   266 				'h' => $match[1],
       
   267 				's' => $match[2],
       
   268 				'l' => $match[3],
       
   269 			)
       
   270 		);
       
   271 
       
   272 		$rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] );
       
   273 
       
   274 		return $rgb;
       
   275 	}
       
   276 
       
   277 	$hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
       
   278 	if ( preg_match( $hex8_regexp, $color_str, $match ) ) {
       
   279 		$rgb = wp_tinycolor_rgb_to_rgb(
       
   280 			array(
       
   281 				'r' => base_convert( $match[1], 16, 10 ),
       
   282 				'g' => base_convert( $match[2], 16, 10 ),
       
   283 				'b' => base_convert( $match[3], 16, 10 ),
       
   284 			)
       
   285 		);
       
   286 
       
   287 		$rgb['a'] = _wp_tinycolor_bound_alpha(
       
   288 			base_convert( $match[4], 16, 10 ) / 255
       
   289 		);
       
   290 
       
   291 		return $rgb;
       
   292 	}
       
   293 
       
   294 	$hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
       
   295 	if ( preg_match( $hex6_regexp, $color_str, $match ) ) {
       
   296 		$rgb = wp_tinycolor_rgb_to_rgb(
       
   297 			array(
       
   298 				'r' => base_convert( $match[1], 16, 10 ),
       
   299 				'g' => base_convert( $match[2], 16, 10 ),
       
   300 				'b' => base_convert( $match[3], 16, 10 ),
       
   301 			)
       
   302 		);
       
   303 
       
   304 		$rgb['a'] = 1;
       
   305 
       
   306 		return $rgb;
       
   307 	}
       
   308 
       
   309 	$hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
       
   310 	if ( preg_match( $hex4_regexp, $color_str, $match ) ) {
       
   311 		$rgb = wp_tinycolor_rgb_to_rgb(
       
   312 			array(
       
   313 				'r' => base_convert( $match[1] . $match[1], 16, 10 ),
       
   314 				'g' => base_convert( $match[2] . $match[2], 16, 10 ),
       
   315 				'b' => base_convert( $match[3] . $match[3], 16, 10 ),
       
   316 			)
       
   317 		);
       
   318 
       
   319 		$rgb['a'] = _wp_tinycolor_bound_alpha(
       
   320 			base_convert( $match[4] . $match[4], 16, 10 ) / 255
       
   321 		);
       
   322 
       
   323 		return $rgb;
       
   324 	}
       
   325 
       
   326 	$hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
       
   327 	if ( preg_match( $hex3_regexp, $color_str, $match ) ) {
       
   328 		$rgb = wp_tinycolor_rgb_to_rgb(
       
   329 			array(
       
   330 				'r' => base_convert( $match[1] . $match[1], 16, 10 ),
       
   331 				'g' => base_convert( $match[2] . $match[2], 16, 10 ),
       
   332 				'b' => base_convert( $match[3] . $match[3], 16, 10 ),
       
   333 			)
       
   334 		);
       
   335 
       
   336 		$rgb['a'] = 1;
       
   337 
       
   338 		return $rgb;
       
   339 	}
       
   340 
       
   341 	/*
       
   342 	 * The JS color picker considers the string "transparent" to be a hex value,
       
   343 	 * so we need to handle it here as a special case.
       
   344 	 */
       
   345 	if ( 'transparent' === $color_str ) {
       
   346 		return array(
       
   347 			'r' => 0,
       
   348 			'g' => 0,
       
   349 			'b' => 0,
       
   350 			'a' => 0,
       
   351 		);
       
   352 	}
       
   353 }
       
   354 
       
   355 /**
       
   356  * Returns the prefixed id for the duotone filter for use as a CSS id.
       
   357  *
       
   358  * @since 5.9.1
       
   359  * @access private
       
   360  *
       
   361  * @param array $preset Duotone preset value as seen in theme.json.
       
   362  * @return string Duotone filter CSS id.
       
   363  */
       
   364 function wp_get_duotone_filter_id( $preset ) {
       
   365 	if ( ! isset( $preset['slug'] ) ) {
       
   366 		return '';
       
   367 	}
       
   368 
       
   369 	return 'wp-duotone-' . $preset['slug'];
       
   370 }
       
   371 
       
   372 /**
       
   373  * Returns the CSS filter property url to reference the rendered SVG.
       
   374  *
       
   375  * @since 5.9.0
       
   376  * @access private
       
   377  *
       
   378  * @param array $preset Duotone preset value as seen in theme.json.
       
   379  * @return string Duotone CSS filter property url value.
       
   380  */
       
   381 function wp_get_duotone_filter_property( $preset ) {
       
   382 	$filter_id = wp_get_duotone_filter_id( $preset );
       
   383 	return "url('#" . $filter_id . "')";
       
   384 }
       
   385 
       
   386 /**
       
   387  * Returns the duotone filter SVG string for the preset.
       
   388  *
       
   389  * @since 5.9.1
       
   390  * @access private
       
   391  *
       
   392  * @param array $preset Duotone preset value as seen in theme.json.
       
   393  * @return string Duotone SVG filter.
       
   394  */
       
   395 function wp_get_duotone_filter_svg( $preset ) {
       
   396 	$filter_id = wp_get_duotone_filter_id( $preset );
       
   397 
       
   398 	$duotone_values = array(
       
   399 		'r' => array(),
       
   400 		'g' => array(),
       
   401 		'b' => array(),
       
   402 		'a' => array(),
       
   403 	);
       
   404 
       
   405 	if ( ! isset( $preset['colors'] ) || ! is_array( $preset['colors'] ) ) {
       
   406 		$preset['colors'] = array();
       
   407 	}
       
   408 
       
   409 	foreach ( $preset['colors'] as $color_str ) {
       
   410 		$color = wp_tinycolor_string_to_rgb( $color_str );
       
   411 
       
   412 		$duotone_values['r'][] = $color['r'] / 255;
       
   413 		$duotone_values['g'][] = $color['g'] / 255;
       
   414 		$duotone_values['b'][] = $color['b'] / 255;
       
   415 		$duotone_values['a'][] = $color['a'];
       
   416 	}
       
   417 
       
   418 	ob_start();
       
   419 
       
   420 	?>
       
   421 
       
   422 	<svg
       
   423 		xmlns="http://www.w3.org/2000/svg"
       
   424 		viewBox="0 0 0 0"
       
   425 		width="0"
       
   426 		height="0"
       
   427 		focusable="false"
       
   428 		role="none"
       
   429 		style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
       
   430 	>
       
   431 		<defs>
       
   432 			<filter id="<?php echo esc_attr( $filter_id ); ?>">
       
   433 				<feColorMatrix
       
   434 					color-interpolation-filters="sRGB"
       
   435 					type="matrix"
       
   436 					values="
       
   437 						.299 .587 .114 0 0
       
   438 						.299 .587 .114 0 0
       
   439 						.299 .587 .114 0 0
       
   440 						.299 .587 .114 0 0
       
   441 					"
       
   442 				/>
       
   443 				<feComponentTransfer color-interpolation-filters="sRGB" >
       
   444 					<feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
       
   445 					<feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" />
       
   446 					<feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" />
       
   447 					<feFuncA type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['a'] ) ); ?>" />
       
   448 				</feComponentTransfer>
       
   449 				<feComposite in2="SourceGraphic" operator="in" />
       
   450 			</filter>
       
   451 		</defs>
       
   452 	</svg>
       
   453 
       
   454 	<?php
       
   455 
       
   456 	$svg = ob_get_clean();
       
   457 
       
   458 	if ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) {
       
   459 		// Clean up the whitespace.
       
   460 		$svg = preg_replace( "/[\r\n\t ]+/", ' ', $svg );
       
   461 		$svg = preg_replace( '/> </', '><', $svg );
       
   462 		$svg = trim( $svg );
       
   463 	}
       
   464 
       
   465 	return $svg;
       
   466 }
       
   467 
       
   468 /**
       
   469  * Registers the style and colors block attributes for block types that support it.
       
   470  *
       
   471  * @since 5.8.0
       
   472  * @access private
       
   473  *
       
   474  * @param WP_Block_Type $block_type Block Type.
       
   475  */
       
   476 function wp_register_duotone_support( $block_type ) {
       
   477 	$has_duotone_support = false;
       
   478 	if ( property_exists( $block_type, 'supports' ) ) {
       
   479 		$has_duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
       
   480 	}
       
   481 
       
   482 	if ( $has_duotone_support ) {
       
   483 		if ( ! $block_type->attributes ) {
       
   484 			$block_type->attributes = array();
       
   485 		}
       
   486 
       
   487 		if ( ! array_key_exists( 'style', $block_type->attributes ) ) {
       
   488 			$block_type->attributes['style'] = array(
       
   489 				'type' => 'object',
       
   490 			);
       
   491 		}
       
   492 	}
       
   493 }
       
   494 
       
   495 /**
       
   496  * Render out the duotone stylesheet and SVG.
       
   497  *
       
   498  * @since 5.8.0
       
   499  * @access private
       
   500  *
       
   501  * @param string $block_content Rendered block content.
       
   502  * @param array  $block         Block object.
       
   503  * @return string Filtered block content.
       
   504  */
       
   505 function wp_render_duotone_support( $block_content, $block ) {
       
   506 	$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
       
   507 
       
   508 	$duotone_support = false;
       
   509 	if ( $block_type && property_exists( $block_type, 'supports' ) ) {
       
   510 		$duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
       
   511 	}
       
   512 
       
   513 	$has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );
       
   514 
       
   515 	if (
       
   516 		! $duotone_support ||
       
   517 		! $has_duotone_attribute
       
   518 	) {
       
   519 		return $block_content;
       
   520 	}
       
   521 
       
   522 	$filter_preset   = array(
       
   523 		'slug'   => wp_unique_id( sanitize_key( implode( '-', $block['attrs']['style']['color']['duotone'] ) . '-' ) ),
       
   524 		'colors' => $block['attrs']['style']['color']['duotone'],
       
   525 	);
       
   526 	$filter_property = wp_get_duotone_filter_property( $filter_preset );
       
   527 	$filter_id       = wp_get_duotone_filter_id( $filter_preset );
       
   528 	$filter_svg      = wp_get_duotone_filter_svg( $filter_preset );
       
   529 
       
   530 	$scope     = '.' . $filter_id;
       
   531 	$selectors = explode( ',', $duotone_support );
       
   532 	$scoped    = array();
       
   533 	foreach ( $selectors as $sel ) {
       
   534 		$scoped[] = $scope . ' ' . trim( $sel );
       
   535 	}
       
   536 	$selector = implode( ', ', $scoped );
       
   537 
       
   538 	// !important is needed because these styles render before global styles,
       
   539 	// and they should be overriding the duotone filters set by global styles.
       
   540 	$filter_style = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG
       
   541 		? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n"
       
   542 		: $selector . '{filter:' . $filter_property . ' !important;}';
       
   543 
       
   544 	wp_register_style( $filter_id, false, array(), true, true );
       
   545 	wp_add_inline_style( $filter_id, $filter_style );
       
   546 	wp_enqueue_style( $filter_id );
       
   547 
       
   548 	add_action(
       
   549 		'wp_footer',
       
   550 		static function () use ( $filter_svg, $selector ) {
       
   551 			echo $filter_svg;
       
   552 
       
   553 			/*
       
   554 			 * Safari renders elements incorrectly on first paint when the SVG
       
   555 			 * filter comes after the content that it is filtering, so we force
       
   556 			 * a repaint with a WebKit hack which solves the issue.
       
   557 			 */
       
   558 			global $is_safari;
       
   559 			if ( $is_safari ) {
       
   560 				printf(
       
   561 					// Simply accessing el.offsetHeight flushes layout and style
       
   562 					// changes in WebKit without having to wait for setTimeout.
       
   563 					'<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>',
       
   564 					wp_json_encode( $selector )
       
   565 				);
       
   566 			}
       
   567 		}
       
   568 	);
       
   569 
       
   570 	// Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
       
   571 	return preg_replace(
       
   572 		'/' . preg_quote( 'class="', '/' ) . '/',
       
   573 		'class="' . $filter_id . ' ',
       
   574 		$block_content,
       
   575 		1
       
   576 	);
       
   577 }
       
   578 
       
   579 // Register the block support.
    35 // Register the block support.
   580 WP_Block_Supports::get_instance()->register(
    36 WP_Block_Supports::get_instance()->register(
   581 	'duotone',
    37 	'duotone',
   582 	array(
    38 	array(
   583 		'register_attribute' => 'wp_register_duotone_support',
    39 		'register_attribute' => array( 'WP_Duotone', 'register_duotone_support' ),
   584 	)
    40 	)
   585 );
    41 );
   586 add_filter( 'render_block', 'wp_render_duotone_support', 10, 2 );
    42 
       
    43 // Add classnames to blocks using duotone support.
       
    44 add_filter( 'render_block', array( 'WP_Duotone', 'render_duotone_support' ), 10, 3 );
       
    45 add_filter( 'render_block_core/image', array( 'WP_Duotone', 'restore_image_outer_container' ), 10, 1 );
       
    46 
       
    47 // Enqueue styles.
       
    48 // Block styles (core-block-supports-inline-css) before the style engine (wp_enqueue_stored_styles).
       
    49 // Global styles (global-styles-inline-css) after the other global styles (wp_enqueue_global_styles).
       
    50 add_action( 'wp_enqueue_scripts', array( 'WP_Duotone', 'output_block_styles' ), 9 );
       
    51 add_action( 'wp_enqueue_scripts', array( 'WP_Duotone', 'output_global_styles' ), 11 );
       
    52 
       
    53 // Add SVG filters to the footer. Also, for classic themes, output block styles (core-block-supports-inline-css).
       
    54 add_action( 'wp_footer', array( 'WP_Duotone', 'output_footer_assets' ), 10 );
       
    55 
       
    56 // Add styles and SVGs for use in the editor via the EditorStyles component.
       
    57 add_filter( 'block_editor_settings_all', array( 'WP_Duotone', 'add_editor_settings' ), 10 );
       
    58 
       
    59 // Migrate the old experimental duotone support flag.
       
    60 add_filter( 'block_type_metadata_settings', array( 'WP_Duotone', 'migrate_experimental_duotone_support_flag' ), 10, 2 );