[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/fonts/ -> class-wp-font-utils.php (source)

   1  <?php
   2  /**
   3   * Font Utils class.
   4   *
   5   * Provides utility functions for working with font families.
   6   *
   7   * @package    WordPress
   8   * @subpackage Fonts
   9   * @since      6.5.0
  10   */
  11  
  12  /**
  13   * A class of utilities for working with the Font Library.
  14   *
  15   * These utilities may change or be removed in the future and are intended for internal use only.
  16   *
  17   * @since 6.5.0
  18   * @access private
  19   */
  20  class WP_Font_Utils {
  21      /**
  22       * Adds surrounding quotes to font family names that contain special characters.
  23       *
  24       * It follows the recommendations from the CSS Fonts Module Level 4.
  25       * @link https://www.w3.org/TR/css-fonts-4/#font-family-prop
  26       *
  27       * @since 6.5.0
  28       *
  29       * @param string $item A font family name.
  30       * @return string The font family name with surrounding quotes, if necessary.
  31       */
  32  	private static function maybe_add_quotes( $item ) {
  33          // Matches strings that are not exclusively alphabetic characters or hyphens, and do not exactly follow the pattern generic(alphabetic characters or hyphens).
  34          $regex = '/^(?!generic\([a-zA-Z\-]+\)$)(?!^[a-zA-Z\-]+$).+/';
  35          $item  = trim( $item );
  36          if ( preg_match( $regex, $item ) ) {
  37              $item = trim( $item, "\"'" );
  38              return '"' . $item . '"';
  39          }
  40          return $item;
  41      }
  42  
  43      /**
  44       * Sanitizes and formats font family names.
  45       *
  46       * - Applies `sanitize_text_field`.
  47       * - Adds surrounding quotes to names containing any characters that are not alphabetic or dashes.
  48       *
  49       * It follows the recommendations from the CSS Fonts Module Level 4.
  50       * @link https://www.w3.org/TR/css-fonts-4/#font-family-prop
  51       *
  52       * @since 6.5.0
  53       * @access private
  54       *
  55       * @see sanitize_text_field()
  56       *
  57       * @param string $font_family Font family name(s), comma-separated.
  58       * @return string Sanitized and formatted font family name(s).
  59       */
  60  	public static function sanitize_font_family( $font_family ) {
  61          if ( ! $font_family ) {
  62              return '';
  63          }
  64  
  65          $output          = sanitize_text_field( $font_family );
  66          $formatted_items = array();
  67          if ( str_contains( $output, ',' ) ) {
  68              $items = explode( ',', $output );
  69              foreach ( $items as $item ) {
  70                  $formatted_item = self::maybe_add_quotes( $item );
  71                  if ( ! empty( $formatted_item ) ) {
  72                      $formatted_items[] = $formatted_item;
  73                  }
  74              }
  75              return implode( ', ', $formatted_items );
  76          }
  77          return self::maybe_add_quotes( $output );
  78      }
  79  
  80      /**
  81       * Generates a slug from font face properties, e.g. `open sans;normal;400;100%;U+0-10FFFF`
  82       *
  83       * Used for comparison with other font faces in the same family, to prevent duplicates
  84       * that would both match according the CSS font matching spec. Uses only simple case-insensitive
  85       * matching for fontFamily and unicodeRange, so does not handle overlapping font-family lists or
  86       * unicode ranges.
  87       *
  88       * @since 6.5.0
  89       * @access private
  90       *
  91       * @link https://drafts.csswg.org/css-fonts/#font-style-matching
  92       *
  93       * @param array $settings {
  94       *     Font face settings.
  95       *
  96       *     @type string $fontFamily   Font family name.
  97       *     @type string $fontStyle    Optional font style, defaults to 'normal'.
  98       *     @type string $fontWeight   Optional font weight, defaults to 400.
  99       *     @type string $fontStretch  Optional font stretch, defaults to '100%'.
 100       *     @type string $unicodeRange Optional unicode range, defaults to 'U+0-10FFFF'.
 101       * }
 102       * @return string Font face slug.
 103       */
 104  	public static function get_font_face_slug( $settings ) {
 105          $defaults = array(
 106              'fontFamily'   => '',
 107              'fontStyle'    => 'normal',
 108              'fontWeight'   => '400',
 109              'fontStretch'  => '100%',
 110              'unicodeRange' => 'U+0-10FFFF',
 111          );
 112          $settings = wp_parse_args( $settings, $defaults );
 113          if ( function_exists( 'mb_strtolower' ) ) {
 114              $font_family = mb_strtolower( $settings['fontFamily'] );
 115          } else {
 116              $font_family = strtolower( $settings['fontFamily'] );
 117          }
 118          $font_style    = strtolower( $settings['fontStyle'] );
 119          $font_weight   = strtolower( $settings['fontWeight'] );
 120          $font_stretch  = strtolower( $settings['fontStretch'] );
 121          $unicode_range = strtoupper( $settings['unicodeRange'] );
 122  
 123          // Convert weight keywords to numeric strings.
 124          $font_weight = str_replace( array( 'normal', 'bold' ), array( '400', '700' ), $font_weight );
 125  
 126          // Convert stretch keywords to numeric strings.
 127          $font_stretch_map = array(
 128              'ultra-condensed' => '50%',
 129              'extra-condensed' => '62.5%',
 130              'condensed'       => '75%',
 131              'semi-condensed'  => '87.5%',
 132              'normal'          => '100%',
 133              'semi-expanded'   => '112.5%',
 134              'expanded'        => '125%',
 135              'extra-expanded'  => '150%',
 136              'ultra-expanded'  => '200%',
 137          );
 138          $font_stretch     = str_replace( array_keys( $font_stretch_map ), array_values( $font_stretch_map ), $font_stretch );
 139  
 140          $slug_elements = array( $font_family, $font_style, $font_weight, $font_stretch, $unicode_range );
 141  
 142          $slug_elements = array_map(
 143              function ( $elem ) {
 144                  // Remove quotes to normalize font-family names, and ';' to use as a separator.
 145                  $elem = trim( str_replace( array( '"', "'", ';' ), '', $elem ) );
 146  
 147                  // Normalize comma separated lists by removing whitespace in between items,
 148                  // but keep whitespace within items (e.g. "Open Sans" and "OpenSans" are different fonts).
 149                  // CSS spec for whitespace includes: U+000A LINE FEED, U+0009 CHARACTER TABULATION, or U+0020 SPACE,
 150                  // which by default are all matched by \s in PHP.
 151                  return preg_replace( '/,\s+/', ',', $elem );
 152              },
 153              $slug_elements
 154          );
 155  
 156          return sanitize_text_field( implode( ';', $slug_elements ) );
 157      }
 158  
 159      /**
 160       * Sanitizes a tree of data using a schema.
 161       *
 162       * The schema structure should mirror the data tree. Each value provided in the
 163       * schema should be a callable that will be applied to sanitize the corresponding
 164       * value in the data tree. Keys that are in the data tree, but not present in the
 165       * schema, will be removed in the sanitized data. Nested arrays are traversed recursively.
 166       *
 167       * @since 6.5.0
 168       *
 169       * @access private
 170       *
 171       * @param array $tree   The data to sanitize.
 172       * @param array $schema The schema used for sanitization.
 173       * @return array The sanitized data.
 174       */
 175  	public static function sanitize_from_schema( $tree, $schema ) {
 176          if ( ! is_array( $tree ) || ! is_array( $schema ) ) {
 177              return array();
 178          }
 179  
 180          foreach ( $tree as $key => $value ) {
 181              // Remove keys not in the schema or with null/empty values.
 182              if ( ! array_key_exists( $key, $schema ) ) {
 183                  unset( $tree[ $key ] );
 184                  continue;
 185              }
 186  
 187              $is_value_array  = is_array( $value );
 188              $is_schema_array = is_array( $schema[ $key ] ) && ! is_callable( $schema[ $key ] );
 189  
 190              if ( $is_value_array && $is_schema_array ) {
 191                  if ( wp_is_numeric_array( $value ) ) {
 192                      // If indexed, process each item in the array.
 193                      foreach ( $value as $item_key => $item_value ) {
 194                          $tree[ $key ][ $item_key ] = isset( $schema[ $key ][0] ) && is_array( $schema[ $key ][0] )
 195                              ? self::sanitize_from_schema( $item_value, $schema[ $key ][0] )
 196                              : self::apply_sanitizer( $item_value, $schema[ $key ][0] );
 197                      }
 198                  } else {
 199                      // If it is an associative or indexed array, process as a single object.
 200                      $tree[ $key ] = self::sanitize_from_schema( $value, $schema[ $key ] );
 201                  }
 202              } elseif ( ! $is_value_array && $is_schema_array ) {
 203                  // If the value is not an array but the schema is, remove the key.
 204                  unset( $tree[ $key ] );
 205              } elseif ( ! $is_schema_array ) {
 206                  // If the schema is not an array, apply the sanitizer to the value.
 207                  $tree[ $key ] = self::apply_sanitizer( $value, $schema[ $key ] );
 208              }
 209  
 210              // Remove keys with null/empty values.
 211              if ( empty( $tree[ $key ] ) ) {
 212                  unset( $tree[ $key ] );
 213              }
 214          }
 215  
 216          return $tree;
 217      }
 218  
 219      /**
 220       * Applies a sanitizer function to a value.
 221       *
 222       * @since 6.5.0
 223       *
 224       * @param mixed    $value     The value to sanitize.
 225       * @param callable $sanitizer The sanitizer function to apply.
 226       * @return mixed The sanitized value.
 227       */
 228  	private static function apply_sanitizer( $value, $sanitizer ) {
 229          if ( null === $sanitizer ) {
 230              return $value;
 231  
 232          }
 233          return call_user_func( $sanitizer, $value );
 234      }
 235  
 236      /**
 237       * Returns the expected mime-type values for font files, depending on PHP version.
 238       *
 239       * This is needed because font mime types vary by PHP version, so checking the PHP version
 240       * is necessary until a list of valid mime-types for each file extension can be provided to
 241       * the 'upload_mimes' filter.
 242       *
 243       * @since 6.5.0
 244       *
 245       * @access private
 246       *
 247       * @return string[] A collection of mime types keyed by file extension.
 248       */
 249  	public static function get_allowed_font_mime_types() {
 250          $php_7_ttf_mime_type = PHP_VERSION_ID >= 70300 ? 'application/font-sfnt' : 'application/x-font-ttf';
 251  
 252          return array(
 253              'otf'   => 'application/vnd.ms-opentype',
 254              'ttf'   => PHP_VERSION_ID >= 70400 ? 'font/sfnt' : $php_7_ttf_mime_type,
 255              'woff'  => PHP_VERSION_ID >= 80112 ? 'font/woff' : 'application/font-woff',
 256              'woff2' => PHP_VERSION_ID >= 80112 ? 'font/woff2' : 'application/font-woff2',
 257          );
 258      }
 259  }


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref