[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/block-supports/ -> duotone.php (source)

   1  <?php
   2  /**
   3   * Duotone block support flag.
   4   *
   5   * Parts of this source were derived and modified from TinyColor,
   6   * released under the MIT license.
   7   *
   8   * https://github.com/bgrins/TinyColor
   9   *
  10   * Copyright (c), Brian Grinstead, http://briangrinstead.com
  11   *
  12   * Permission is hereby granted, free of charge, to any person obtaining
  13   * a copy of this software and associated documentation files (the
  14   * "Software"), to deal in the Software without restriction, including
  15   * without limitation the rights to use, copy, modify, merge, publish,
  16   * distribute, sublicense, and/or sell copies of the Software, and to
  17   * permit persons to whom the Software is furnished to do so, subject to
  18   * the following conditions:
  19   *
  20   * The above copyright notice and this permission notice shall be
  21   * included in all copies or substantial portions of the Software.
  22   *
  23   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28   * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30   *
  31   * @package WordPress
  32   * @since 5.8.0
  33   */
  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   *
  49   * @return float Value in the range [0, 1].
  50   */
  51  function wp_tinycolor_bound01( $n, $max ) {
  52      if ( 'string' === gettype( $n ) && false !== strpos( $n, '.' ) && 1 === (float) $n ) {
  53          $n = '100%';
  54      }
  55  
  56      $n = min( $max, max( 0, (float) $n ) );
  57  
  58      // Automatically convert percentage into number.
  59      if ( 'string' === gettype( $n ) && false !== strpos( $n, '%' ) ) {
  60          $n = (int) ( $n * $max ) / 100;
  61      }
  62  
  63      // Handle floating point rounding errors.
  64      if ( ( abs( $n - $max ) < 0.000001 ) ) {
  65          return 1.0;
  66      }
  67  
  68      // Convert into [0, 1] range if it isn't already.
  69      return ( $n % $max ) / (float) $max;
  70  }
  71  
  72  /**
  73   * Round and convert values of an RGB object.
  74   *
  75   * Direct port of TinyColor's function, lightly simplified to maintain
  76   * consistency with TinyColor.
  77   *
  78   * @see https://github.com/bgrins/TinyColor
  79   *
  80   * @since 5.8.0
  81   * @access private
  82   *
  83   * @param array $rgb_color RGB object.
  84   *
  85   * @return array Rounded and converted RGB object.
  86   */
  87  function wp_tinycolor_rgb_to_rgb( $rgb_color ) {
  88      return array(
  89          'r' => wp_tinycolor_bound01( $rgb_color['r'], 255 ) * 255,
  90          'g' => wp_tinycolor_bound01( $rgb_color['g'], 255 ) * 255,
  91          'b' => wp_tinycolor_bound01( $rgb_color['b'], 255 ) * 255,
  92      );
  93  }
  94  
  95  /**
  96   * Helper function for hsl to rgb conversion.
  97   *
  98   * Direct port of TinyColor's function, lightly simplified to maintain
  99   * consistency with TinyColor.
 100   *
 101   * @see https://github.com/bgrins/TinyColor
 102   *
 103   * @since 5.8.0
 104   * @access private
 105   *
 106   * @param float $p first component.
 107   * @param float $q second component.
 108   * @param float $t third component.
 109   *
 110   * @return float R, G, or B component.
 111   */
 112  function wp_tinycolor_hue_to_rgb( $p, $q, $t ) {
 113      if ( $t < 0 ) {
 114          $t += 1;
 115      }
 116      if ( $t > 1 ) {
 117          $t -= 1;
 118      }
 119      if ( $t < 1 / 6 ) {
 120          return $p + ( $q - $p ) * 6 * $t;
 121      }
 122      if ( $t < 1 / 2 ) {
 123          return $q;
 124      }
 125      if ( $t < 2 / 3 ) {
 126          return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
 127      }
 128      return $p;
 129  }
 130  
 131  /**
 132   * Convert an HSL object to an RGB object with converted and rounded values.
 133   *
 134   * Direct port of TinyColor's function, lightly simplified to maintain
 135   * consistency with TinyColor.
 136   *
 137   * @see https://github.com/bgrins/TinyColor
 138   *
 139   * @since 5.8.0
 140   * @access private
 141   *
 142   * @param array $hsl_color HSL object.
 143   *
 144   * @return array Rounded and converted RGB object.
 145   */
 146  function wp_tinycolor_hsl_to_rgb( $hsl_color ) {
 147      $h = wp_tinycolor_bound01( $hsl_color['h'], 360 );
 148      $s = wp_tinycolor_bound01( $hsl_color['s'], 100 );
 149      $l = wp_tinycolor_bound01( $hsl_color['l'], 100 );
 150  
 151      if ( 0 === $s ) {
 152          // Achromatic.
 153          $r = $l;
 154          $g = $l;
 155          $b = $l;
 156      } else {
 157          $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
 158          $p = 2 * $l - $q;
 159          $r = wp_tinycolor_hue_to_rgb( $p, $q, $h + 1 / 3 );
 160          $g = wp_tinycolor_hue_to_rgb( $p, $q, $h );
 161          $b = wp_tinycolor_hue_to_rgb( $p, $q, $h - 1 / 3 );
 162      }
 163  
 164      return array(
 165          'r' => $r * 255,
 166          'g' => $g * 255,
 167          'b' => $b * 255,
 168      );
 169  }
 170  
 171  /**
 172   * Parses hex, hsl, and rgb CSS strings using the same regex as TinyColor v1.4.2
 173   * used in the JavaScript. Only colors output from react-color are implemented
 174   * and the alpha value is ignored as it is not used in duotone.
 175   *
 176   * Direct port of TinyColor's function, lightly simplified to maintain
 177   * consistency with TinyColor.
 178   *
 179   * @see https://github.com/bgrins/TinyColor
 180   * @see https://github.com/casesandberg/react-color/
 181   *
 182   * @since 5.8.0
 183   * @access private
 184   *
 185   * @param string $color_str CSS color string.
 186   *
 187   * @return array RGB object.
 188   */
 189  function wp_tinycolor_string_to_rgb( $color_str ) {
 190      $color_str = strtolower( trim( $color_str ) );
 191  
 192      $css_integer = '[-\\+]?\\d+%?';
 193      $css_number  = '[-\\+]?\\d*\\.\\d+%?';
 194  
 195      $css_unit = '(?:' . $css_number . ')|(?:' . $css_integer . ')';
 196  
 197      $permissive_match3 = '[\\s|\\(]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')\\s*\\)?';
 198      $permissive_match4 = '[\\s|\\(]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')[,|\\s]+(' . $css_unit . ')\\s*\\)?';
 199  
 200      $rgb_regexp = '/^rgb' . $permissive_match3 . '$/';
 201      if ( preg_match( $rgb_regexp, $color_str, $match ) ) {
 202          return wp_tinycolor_rgb_to_rgb(
 203              array(
 204                  'r' => $match[1],
 205                  'g' => $match[2],
 206                  'b' => $match[3],
 207              )
 208          );
 209      }
 210  
 211      $rgba_regexp = '/^rgba' . $permissive_match4 . '$/';
 212      if ( preg_match( $rgba_regexp, $color_str, $match ) ) {
 213          return wp_tinycolor_rgb_to_rgb(
 214              array(
 215                  'r' => $match[1],
 216                  'g' => $match[2],
 217                  'b' => $match[3],
 218              )
 219          );
 220      }
 221  
 222      $hsl_regexp = '/^hsl' . $permissive_match3 . '$/';
 223      if ( preg_match( $hsl_regexp, $color_str, $match ) ) {
 224          return wp_tinycolor_hsl_to_rgb(
 225              array(
 226                  'h' => $match[1],
 227                  's' => $match[2],
 228                  'l' => $match[3],
 229              )
 230          );
 231      }
 232  
 233      $hsla_regexp = '/^hsla' . $permissive_match4 . '$/';
 234      if ( preg_match( $hsla_regexp, $color_str, $match ) ) {
 235          return wp_tinycolor_hsl_to_rgb(
 236              array(
 237                  'h' => $match[1],
 238                  's' => $match[2],
 239                  'l' => $match[3],
 240              )
 241          );
 242      }
 243  
 244      $hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
 245      if ( preg_match( $hex8_regexp, $color_str, $match ) ) {
 246          return wp_tinycolor_rgb_to_rgb(
 247              array(
 248                  'r' => base_convert( $match[1], 16, 10 ),
 249                  'g' => base_convert( $match[2], 16, 10 ),
 250                  'b' => base_convert( $match[3], 16, 10 ),
 251              )
 252          );
 253      }
 254  
 255      $hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
 256      if ( preg_match( $hex6_regexp, $color_str, $match ) ) {
 257          return wp_tinycolor_rgb_to_rgb(
 258              array(
 259                  'r' => base_convert( $match[1], 16, 10 ),
 260                  'g' => base_convert( $match[2], 16, 10 ),
 261                  'b' => base_convert( $match[3], 16, 10 ),
 262              )
 263          );
 264      }
 265  
 266      $hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
 267      if ( preg_match( $hex4_regexp, $color_str, $match ) ) {
 268          return wp_tinycolor_rgb_to_rgb(
 269              array(
 270                  'r' => base_convert( $match[1] . $match[1], 16, 10 ),
 271                  'g' => base_convert( $match[2] . $match[2], 16, 10 ),
 272                  'b' => base_convert( $match[3] . $match[3], 16, 10 ),
 273              )
 274          );
 275      }
 276  
 277      $hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
 278      if ( preg_match( $hex3_regexp, $color_str, $match ) ) {
 279          return wp_tinycolor_rgb_to_rgb(
 280              array(
 281                  'r' => base_convert( $match[1] . $match[1], 16, 10 ),
 282                  'g' => base_convert( $match[2] . $match[2], 16, 10 ),
 283                  'b' => base_convert( $match[3] . $match[3], 16, 10 ),
 284              )
 285          );
 286      }
 287  }
 288  
 289  
 290  /**
 291   * Registers the style and colors block attributes for block types that support it.
 292   *
 293   * @since 5.8.0
 294   * @access private
 295   *
 296   * @param WP_Block_Type $block_type Block Type.
 297   */
 298  function wp_register_duotone_support( $block_type ) {
 299      $has_duotone_support = false;
 300      if ( property_exists( $block_type, 'supports' ) ) {
 301          $has_duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
 302      }
 303  
 304      if ( $has_duotone_support ) {
 305          if ( ! $block_type->attributes ) {
 306              $block_type->attributes = array();
 307          }
 308  
 309          if ( ! array_key_exists( 'style', $block_type->attributes ) ) {
 310              $block_type->attributes['style'] = array(
 311                  'type' => 'object',
 312              );
 313          }
 314      }
 315  }
 316  
 317  /**
 318   * Render out the duotone stylesheet and SVG.
 319   *
 320   * @since 5.8.0
 321   * @access private
 322   *
 323   * @param string $block_content Rendered block content.
 324   * @param array  $block         Block object.
 325   *
 326   * @return string Filtered block content.
 327   */
 328  function wp_render_duotone_support( $block_content, $block ) {
 329      $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
 330  
 331      $duotone_support = false;
 332      if ( $block_type && property_exists( $block_type, 'supports' ) ) {
 333          $duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
 334      }
 335  
 336      $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );
 337  
 338      if (
 339          ! $duotone_support ||
 340          ! $has_duotone_attribute
 341      ) {
 342          return $block_content;
 343      }
 344  
 345      $duotone_colors = $block['attrs']['style']['color']['duotone'];
 346  
 347      $duotone_values = array(
 348          'r' => array(),
 349          'g' => array(),
 350          'b' => array(),
 351      );
 352      foreach ( $duotone_colors as $color_str ) {
 353          $color = wp_tinycolor_string_to_rgb( $color_str );
 354  
 355          $duotone_values['r'][] = $color['r'] / 255;
 356          $duotone_values['g'][] = $color['g'] / 255;
 357          $duotone_values['b'][] = $color['b'] / 255;
 358      }
 359  
 360      $duotone_id = 'wp-duotone-filter-' . uniqid();
 361  
 362      $selectors        = explode( ',', $duotone_support );
 363      $selectors_scoped = array_map(
 364          static function ( $selector ) use ( $duotone_id ) {
 365              return '.' . $duotone_id . ' ' . trim( $selector );
 366          },
 367          $selectors
 368      );
 369      $selectors_group  = implode( ', ', $selectors_scoped );
 370  
 371      ob_start();
 372  
 373      ?>
 374  
 375      <style>
 376          <?php echo $selectors_group; ?> {
 377              filter: url( <?php echo esc_url( '#' . $duotone_id ); ?> );
 378          }
 379      </style>
 380  
 381      <svg
 382          xmlns:xlink="http://www.w3.org/1999/xlink"
 383          viewBox="0 0 0 0"
 384          width="0"
 385          height="0"
 386          focusable="false"
 387          role="none"
 388          style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
 389      >
 390          <defs>
 391              <filter id="<?php echo esc_attr( $duotone_id ); ?>">
 392                  <feColorMatrix
 393                      type="matrix"
 394                      <?php // phpcs:disable Generic.WhiteSpace.DisallowSpaceIndent ?>
 395                      values=".299 .587 .114 0 0
 396                              .299 .587 .114 0 0
 397                              .299 .587 .114 0 0
 398                              0 0 0 1 0"
 399                      <?php // phpcs:enable Generic.WhiteSpace.DisallowSpaceIndent ?>
 400                  />
 401                  <feComponentTransfer color-interpolation-filters="sRGB" >
 402                      <feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
 403                      <feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" />
 404                      <feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" />
 405                  </feComponentTransfer>
 406              </filter>
 407          </defs>
 408      </svg>
 409  
 410      <?php
 411  
 412      $duotone = ob_get_clean();
 413  
 414      // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
 415      $content = preg_replace(
 416          '/' . preg_quote( 'class="', '/' ) . '/',
 417          'class="' . $duotone_id . ' ',
 418          $block_content,
 419          1
 420      );
 421  
 422      return $content . $duotone;
 423  }
 424  
 425  // Register the block support.
 426  WP_Block_Supports::get_instance()->register(
 427      'duotone',
 428      array(
 429          'register_attribute' => 'wp_register_duotone_support',
 430      )
 431  );
 432  add_filter( 'render_block', 'wp_render_duotone_support', 10, 2 );


Generated: Mon Sep 20 01:00:04 2021 Cross-referenced by PHPXref 0.7.1