wp/wp-includes/block-supports/typography.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     8 
     8 
     9 /**
     9 /**
    10  * Registers the style and typography block attributes for block types that support it.
    10  * Registers the style and typography block attributes for block types that support it.
    11  *
    11  *
    12  * @since 5.6.0
    12  * @since 5.6.0
       
    13  * @since 6.3.0 Added support for text-columns.
    13  * @access private
    14  * @access private
    14  *
    15  *
    15  * @param WP_Block_Type $block_type Block Type.
    16  * @param WP_Block_Type $block_type Block Type.
    16  */
    17  */
    17 function wp_register_typography_support( $block_type ) {
    18 function wp_register_typography_support( $block_type ) {
    18 	if ( ! property_exists( $block_type, 'supports' ) ) {
    19 	if ( ! ( $block_type instanceof WP_Block_Type ) ) {
    19 		return;
    20 		return;
    20 	}
    21 	}
    21 
    22 
    22 	$typography_supports = _wp_array_get( $block_type->supports, array( 'typography' ), false );
    23 	$typography_supports = isset( $block_type->supports['typography'] ) ? $block_type->supports['typography'] : false;
    23 	if ( ! $typography_supports ) {
    24 	if ( ! $typography_supports ) {
    24 		return;
    25 		return;
    25 	}
    26 	}
    26 
    27 
    27 	$has_font_family_support     = _wp_array_get( $typography_supports, array( '__experimentalFontFamily' ), false );
    28 	$has_font_family_support     = isset( $typography_supports['__experimentalFontFamily'] ) ? $typography_supports['__experimentalFontFamily'] : false;
    28 	$has_font_size_support       = _wp_array_get( $typography_supports, array( 'fontSize' ), false );
    29 	$has_font_size_support       = isset( $typography_supports['fontSize'] ) ? $typography_supports['fontSize'] : false;
    29 	$has_font_style_support      = _wp_array_get( $typography_supports, array( '__experimentalFontStyle' ), false );
    30 	$has_font_style_support      = isset( $typography_supports['__experimentalFontStyle'] ) ? $typography_supports['__experimentalFontStyle'] : false;
    30 	$has_font_weight_support     = _wp_array_get( $typography_supports, array( '__experimentalFontWeight' ), false );
    31 	$has_font_weight_support     = isset( $typography_supports['__experimentalFontWeight'] ) ? $typography_supports['__experimentalFontWeight'] : false;
    31 	$has_letter_spacing_support  = _wp_array_get( $typography_supports, array( '__experimentalLetterSpacing' ), false );
    32 	$has_letter_spacing_support  = isset( $typography_supports['__experimentalLetterSpacing'] ) ? $typography_supports['__experimentalLetterSpacing'] : false;
    32 	$has_line_height_support     = _wp_array_get( $typography_supports, array( 'lineHeight' ), false );
    33 	$has_line_height_support     = isset( $typography_supports['lineHeight'] ) ? $typography_supports['lineHeight'] : false;
    33 	$has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false );
    34 	$has_text_align_support      = isset( $typography_supports['textAlign'] ) ? $typography_supports['textAlign'] : false;
    34 	$has_text_transform_support  = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false );
    35 	$has_text_columns_support    = isset( $typography_supports['textColumns'] ) ? $typography_supports['textColumns'] : false;
       
    36 	$has_text_decoration_support = isset( $typography_supports['__experimentalTextDecoration'] ) ? $typography_supports['__experimentalTextDecoration'] : false;
       
    37 	$has_text_transform_support  = isset( $typography_supports['__experimentalTextTransform'] ) ? $typography_supports['__experimentalTextTransform'] : false;
       
    38 	$has_writing_mode_support    = isset( $typography_supports['__experimentalWritingMode'] ) ? $typography_supports['__experimentalWritingMode'] : false;
    35 
    39 
    36 	$has_typography_support = $has_font_family_support
    40 	$has_typography_support = $has_font_family_support
    37 		|| $has_font_size_support
    41 		|| $has_font_size_support
    38 		|| $has_font_style_support
    42 		|| $has_font_style_support
    39 		|| $has_font_weight_support
    43 		|| $has_font_weight_support
    40 		|| $has_letter_spacing_support
    44 		|| $has_letter_spacing_support
    41 		|| $has_line_height_support
    45 		|| $has_line_height_support
       
    46 		|| $has_text_align_support
       
    47 		|| $has_text_columns_support
    42 		|| $has_text_decoration_support
    48 		|| $has_text_decoration_support
    43 		|| $has_text_transform_support;
    49 		|| $has_text_transform_support
       
    50 		|| $has_writing_mode_support;
    44 
    51 
    45 	if ( ! $block_type->attributes ) {
    52 	if ( ! $block_type->attributes ) {
    46 		$block_type->attributes = array();
    53 		$block_type->attributes = array();
    47 	}
    54 	}
    48 
    55 
    55 	if ( $has_font_size_support && ! array_key_exists( 'fontSize', $block_type->attributes ) ) {
    62 	if ( $has_font_size_support && ! array_key_exists( 'fontSize', $block_type->attributes ) ) {
    56 		$block_type->attributes['fontSize'] = array(
    63 		$block_type->attributes['fontSize'] = array(
    57 			'type' => 'string',
    64 			'type' => 'string',
    58 		);
    65 		);
    59 	}
    66 	}
       
    67 
       
    68 	if ( $has_font_family_support && ! array_key_exists( 'fontFamily', $block_type->attributes ) ) {
       
    69 		$block_type->attributes['fontFamily'] = array(
       
    70 			'type' => 'string',
       
    71 		);
       
    72 	}
    60 }
    73 }
    61 
    74 
    62 /**
    75 /**
    63  * Adds CSS classes and inline styles for typography features such as font sizes
    76  * Adds CSS classes and inline styles for typography features such as font sizes
    64  * to the incoming attributes array. This will be applied to the block markup in
    77  * to the incoming attributes array. This will be applied to the block markup in
    65  * the front-end.
    78  * the front-end.
    66  *
    79  *
    67  * @since 5.6.0
    80  * @since 5.6.0
       
    81  * @since 6.1.0 Used the style engine to generate CSS and classnames.
       
    82  * @since 6.3.0 Added support for text-columns.
    68  * @access private
    83  * @access private
    69  *
    84  *
    70  * @param WP_Block_Type $block_type       Block type.
    85  * @param WP_Block_Type $block_type       Block type.
    71  * @param array         $block_attributes Block attributes.
    86  * @param array         $block_attributes Block attributes.
    72  * @return array Typography CSS classes and inline styles.
    87  * @return array Typography CSS classes and inline styles.
    73  */
    88  */
    74 function wp_apply_typography_support( $block_type, $block_attributes ) {
    89 function wp_apply_typography_support( $block_type, $block_attributes ) {
    75 	if ( ! property_exists( $block_type, 'supports' ) ) {
    90 	if ( ! ( $block_type instanceof WP_Block_Type ) ) {
    76 		return array();
    91 		return array();
    77 	}
    92 	}
    78 
    93 
    79 	$typography_supports = _wp_array_get( $block_type->supports, array( 'typography' ), false );
    94 	$typography_supports = isset( $block_type->supports['typography'] )
       
    95 		? $block_type->supports['typography']
       
    96 		: false;
    80 	if ( ! $typography_supports ) {
    97 	if ( ! $typography_supports ) {
    81 		return array();
    98 		return array();
    82 	}
    99 	}
    83 
   100 
    84 	if ( wp_should_skip_block_supports_serialization( $block_type, 'typography' ) ) {
   101 	if ( wp_should_skip_block_supports_serialization( $block_type, 'typography' ) ) {
    85 		return array();
   102 		return array();
    86 	}
   103 	}
    87 
   104 
       
   105 	$has_font_family_support     = isset( $typography_supports['__experimentalFontFamily'] ) ? $typography_supports['__experimentalFontFamily'] : false;
       
   106 	$has_font_size_support       = isset( $typography_supports['fontSize'] ) ? $typography_supports['fontSize'] : false;
       
   107 	$has_font_style_support      = isset( $typography_supports['__experimentalFontStyle'] ) ? $typography_supports['__experimentalFontStyle'] : false;
       
   108 	$has_font_weight_support     = isset( $typography_supports['__experimentalFontWeight'] ) ? $typography_supports['__experimentalFontWeight'] : false;
       
   109 	$has_letter_spacing_support  = isset( $typography_supports['__experimentalLetterSpacing'] ) ? $typography_supports['__experimentalLetterSpacing'] : false;
       
   110 	$has_line_height_support     = isset( $typography_supports['lineHeight'] ) ? $typography_supports['lineHeight'] : false;
       
   111 	$has_text_align_support      = isset( $typography_supports['textAlign'] ) ? $typography_supports['textAlign'] : false;
       
   112 	$has_text_columns_support    = isset( $typography_supports['textColumns'] ) ? $typography_supports['textColumns'] : false;
       
   113 	$has_text_decoration_support = isset( $typography_supports['__experimentalTextDecoration'] ) ? $typography_supports['__experimentalTextDecoration'] : false;
       
   114 	$has_text_transform_support  = isset( $typography_supports['__experimentalTextTransform'] ) ? $typography_supports['__experimentalTextTransform'] : false;
       
   115 	$has_writing_mode_support    = isset( $typography_supports['__experimentalWritingMode'] ) ? $typography_supports['__experimentalWritingMode'] : false;
       
   116 
       
   117 	// Whether to skip individual block support features.
       
   118 	$should_skip_font_size       = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' );
       
   119 	$should_skip_font_family     = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' );
       
   120 	$should_skip_font_style      = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' );
       
   121 	$should_skip_font_weight     = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' );
       
   122 	$should_skip_line_height     = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' );
       
   123 	$should_skip_text_align      = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textAlign' );
       
   124 	$should_skip_text_columns    = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textColumns' );
       
   125 	$should_skip_text_decoration = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' );
       
   126 	$should_skip_text_transform  = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' );
       
   127 	$should_skip_letter_spacing  = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' );
       
   128 	$should_skip_writing_mode    = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'writingMode' );
       
   129 
       
   130 	$typography_block_styles = array();
       
   131 	if ( $has_font_size_support && ! $should_skip_font_size ) {
       
   132 		$preset_font_size                    = array_key_exists( 'fontSize', $block_attributes )
       
   133 			? "var:preset|font-size|{$block_attributes['fontSize']}"
       
   134 			: null;
       
   135 		$custom_font_size                    = isset( $block_attributes['style']['typography']['fontSize'] )
       
   136 			? $block_attributes['style']['typography']['fontSize']
       
   137 			: null;
       
   138 		$typography_block_styles['fontSize'] = $preset_font_size ? $preset_font_size : wp_get_typography_font_size_value(
       
   139 			array(
       
   140 				'size' => $custom_font_size,
       
   141 			)
       
   142 		);
       
   143 	}
       
   144 
       
   145 	if ( $has_font_family_support && ! $should_skip_font_family ) {
       
   146 		$preset_font_family                    = array_key_exists( 'fontFamily', $block_attributes )
       
   147 			? "var:preset|font-family|{$block_attributes['fontFamily']}"
       
   148 			: null;
       
   149 		$custom_font_family                    = isset( $block_attributes['style']['typography']['fontFamily'] )
       
   150 			? wp_typography_get_preset_inline_style_value( $block_attributes['style']['typography']['fontFamily'], 'font-family' )
       
   151 			: null;
       
   152 		$typography_block_styles['fontFamily'] = $preset_font_family ? $preset_font_family : $custom_font_family;
       
   153 	}
       
   154 
       
   155 	if (
       
   156 		$has_font_style_support &&
       
   157 		! $should_skip_font_style &&
       
   158 		isset( $block_attributes['style']['typography']['fontStyle'] )
       
   159 	) {
       
   160 		$typography_block_styles['fontStyle'] = wp_typography_get_preset_inline_style_value(
       
   161 			$block_attributes['style']['typography']['fontStyle'],
       
   162 			'font-style'
       
   163 		);
       
   164 	}
       
   165 
       
   166 	if (
       
   167 		$has_font_weight_support &&
       
   168 		! $should_skip_font_weight &&
       
   169 		isset( $block_attributes['style']['typography']['fontWeight'] )
       
   170 	) {
       
   171 		$typography_block_styles['fontWeight'] = wp_typography_get_preset_inline_style_value(
       
   172 			$block_attributes['style']['typography']['fontWeight'],
       
   173 			'font-weight'
       
   174 		);
       
   175 	}
       
   176 
       
   177 	if ( $has_line_height_support && ! $should_skip_line_height ) {
       
   178 		$typography_block_styles['lineHeight'] = isset( $block_attributes['style']['typography']['lineHeight'] )
       
   179 			? $block_attributes['style']['typography']['lineHeight']
       
   180 			: null;
       
   181 	}
       
   182 
       
   183 	if ( $has_text_align_support && ! $should_skip_text_align ) {
       
   184 		$typography_block_styles['textAlign'] = isset( $block_attributes['style']['typography']['textAlign'] )
       
   185 			? $block_attributes['style']['typography']['textAlign']
       
   186 			: null;
       
   187 	}
       
   188 
       
   189 	if ( $has_text_columns_support && ! $should_skip_text_columns && isset( $block_attributes['style']['typography']['textColumns'] ) ) {
       
   190 		$typography_block_styles['textColumns'] = isset( $block_attributes['style']['typography']['textColumns'] )
       
   191 			? $block_attributes['style']['typography']['textColumns']
       
   192 			: null;
       
   193 	}
       
   194 
       
   195 	if (
       
   196 		$has_text_decoration_support &&
       
   197 		! $should_skip_text_decoration &&
       
   198 		isset( $block_attributes['style']['typography']['textDecoration'] )
       
   199 	) {
       
   200 		$typography_block_styles['textDecoration'] = wp_typography_get_preset_inline_style_value(
       
   201 			$block_attributes['style']['typography']['textDecoration'],
       
   202 			'text-decoration'
       
   203 		);
       
   204 	}
       
   205 
       
   206 	if (
       
   207 		$has_text_transform_support &&
       
   208 		! $should_skip_text_transform &&
       
   209 		isset( $block_attributes['style']['typography']['textTransform'] )
       
   210 	) {
       
   211 		$typography_block_styles['textTransform'] = wp_typography_get_preset_inline_style_value(
       
   212 			$block_attributes['style']['typography']['textTransform'],
       
   213 			'text-transform'
       
   214 		);
       
   215 	}
       
   216 
       
   217 	if (
       
   218 		$has_letter_spacing_support &&
       
   219 		! $should_skip_letter_spacing &&
       
   220 		isset( $block_attributes['style']['typography']['letterSpacing'] )
       
   221 	) {
       
   222 		$typography_block_styles['letterSpacing'] = wp_typography_get_preset_inline_style_value(
       
   223 			$block_attributes['style']['typography']['letterSpacing'],
       
   224 			'letter-spacing'
       
   225 		);
       
   226 	}
       
   227 
       
   228 	if ( $has_writing_mode_support &&
       
   229 		! $should_skip_writing_mode &&
       
   230 		isset( $block_attributes['style']['typography']['writingMode'] )
       
   231 	) {
       
   232 		$typography_block_styles['writingMode'] = isset( $block_attributes['style']['typography']['writingMode'] )
       
   233 			? $block_attributes['style']['typography']['writingMode']
       
   234 			: null;
       
   235 	}
       
   236 
    88 	$attributes = array();
   237 	$attributes = array();
    89 	$classes    = array();
   238 	$classnames = array();
    90 	$styles     = array();
   239 	$styles     = wp_style_engine_get_styles(
    91 
   240 		array( 'typography' => $typography_block_styles ),
    92 	$has_font_family_support     = _wp_array_get( $typography_supports, array( '__experimentalFontFamily' ), false );
   241 		array( 'convert_vars_to_classnames' => true )
    93 	$has_font_size_support       = _wp_array_get( $typography_supports, array( 'fontSize' ), false );
   242 	);
    94 	$has_font_style_support      = _wp_array_get( $typography_supports, array( '__experimentalFontStyle' ), false );
   243 
    95 	$has_font_weight_support     = _wp_array_get( $typography_supports, array( '__experimentalFontWeight' ), false );
   244 	if ( ! empty( $styles['classnames'] ) ) {
    96 	$has_letter_spacing_support  = _wp_array_get( $typography_supports, array( '__experimentalLetterSpacing' ), false );
   245 		$classnames[] = $styles['classnames'];
    97 	$has_line_height_support     = _wp_array_get( $typography_supports, array( 'lineHeight' ), false );
   246 	}
    98 	$has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false );
   247 
    99 	$has_text_transform_support  = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false );
   248 	if ( $has_text_align_support && ! $should_skip_text_align && isset( $block_attributes['style']['typography']['textAlign'] ) ) {
   100 
   249 		$classnames[] = 'has-text-align-' . $block_attributes['style']['typography']['textAlign'];
   101 	if ( $has_font_size_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' ) ) {
   250 	}
   102 		$has_named_font_size  = array_key_exists( 'fontSize', $block_attributes );
   251 
   103 		$has_custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] );
   252 	if ( ! empty( $classnames ) ) {
   104 
   253 		$attributes['class'] = implode( ' ', $classnames );
   105 		if ( $has_named_font_size ) {
   254 	}
   106 			$classes[] = sprintf( 'has-%s-font-size', _wp_to_kebab_case( $block_attributes['fontSize'] ) );
   255 
   107 		} elseif ( $has_custom_font_size ) {
   256 	if ( ! empty( $styles['css'] ) ) {
   108 			$styles[] = sprintf( 'font-size: %s;', $block_attributes['style']['typography']['fontSize'] );
   257 		$attributes['style'] = $styles['css'];
   109 		}
       
   110 	}
       
   111 
       
   112 	if ( $has_font_family_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' ) ) {
       
   113 		$has_named_font_family  = array_key_exists( 'fontFamily', $block_attributes );
       
   114 		$has_custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] );
       
   115 
       
   116 		if ( $has_named_font_family ) {
       
   117 			$classes[] = sprintf( 'has-%s-font-family', _wp_to_kebab_case( $block_attributes['fontFamily'] ) );
       
   118 		} elseif ( $has_custom_font_family ) {
       
   119 			// Before using classes, the value was serialized as a CSS Custom Property.
       
   120 			// We don't need this code path when it lands in core.
       
   121 			$font_family_custom = $block_attributes['style']['typography']['fontFamily'];
       
   122 			if ( strpos( $font_family_custom, 'var:preset|font-family' ) !== false ) {
       
   123 				$index_to_splice    = strrpos( $font_family_custom, '|' ) + 1;
       
   124 				$font_family_slug   = _wp_to_kebab_case( substr( $font_family_custom, $index_to_splice ) );
       
   125 				$font_family_custom = sprintf( 'var(--wp--preset--font-family--%s)', $font_family_slug );
       
   126 			}
       
   127 			$styles[] = sprintf( 'font-family: %s;', $font_family_custom );
       
   128 		}
       
   129 	}
       
   130 
       
   131 	if ( $has_font_style_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' ) ) {
       
   132 		$font_style = wp_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' );
       
   133 		if ( $font_style ) {
       
   134 			$styles[] = $font_style;
       
   135 		}
       
   136 	}
       
   137 
       
   138 	if ( $has_font_weight_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' ) ) {
       
   139 		$font_weight = wp_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' );
       
   140 		if ( $font_weight ) {
       
   141 			$styles[] = $font_weight;
       
   142 		}
       
   143 	}
       
   144 
       
   145 	if ( $has_line_height_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' ) ) {
       
   146 		$has_line_height = isset( $block_attributes['style']['typography']['lineHeight'] );
       
   147 		if ( $has_line_height ) {
       
   148 			$styles[] = sprintf( 'line-height: %s;', $block_attributes['style']['typography']['lineHeight'] );
       
   149 		}
       
   150 	}
       
   151 
       
   152 	if ( $has_text_decoration_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' ) ) {
       
   153 		$text_decoration_style = wp_typography_get_css_variable_inline_style( $block_attributes, 'textDecoration', 'text-decoration' );
       
   154 		if ( $text_decoration_style ) {
       
   155 			$styles[] = $text_decoration_style;
       
   156 		}
       
   157 	}
       
   158 
       
   159 	if ( $has_text_transform_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' ) ) {
       
   160 		$text_transform_style = wp_typography_get_css_variable_inline_style( $block_attributes, 'textTransform', 'text-transform' );
       
   161 		if ( $text_transform_style ) {
       
   162 			$styles[] = $text_transform_style;
       
   163 		}
       
   164 	}
       
   165 
       
   166 	if ( $has_letter_spacing_support && ! wp_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' ) ) {
       
   167 		$letter_spacing_style = wp_typography_get_css_variable_inline_style( $block_attributes, 'letterSpacing', 'letter-spacing' );
       
   168 		if ( $letter_spacing_style ) {
       
   169 			$styles[] = $letter_spacing_style;
       
   170 		}
       
   171 	}
       
   172 
       
   173 	if ( ! empty( $classes ) ) {
       
   174 		$attributes['class'] = implode( ' ', $classes );
       
   175 	}
       
   176 	if ( ! empty( $styles ) ) {
       
   177 		$attributes['style'] = implode( ' ', $styles );
       
   178 	}
   258 	}
   179 
   259 
   180 	return $attributes;
   260 	return $attributes;
   181 }
   261 }
   182 
   262 
   183 /**
   263 /**
   184  * Generates an inline style for a typography feature e.g. text decoration,
   264  * Generates an inline style value for a typography feature e.g. text decoration,
   185  * text transform, and font style.
   265  * text transform, and font style.
   186  *
   266  *
   187  * @since 5.8.0
   267  * Note: This function is for backwards compatibility.
       
   268  * * It is necessary to parse older blocks whose typography styles contain presets.
       
   269  * * It mostly replaces the deprecated `wp_typography_get_css_variable_inline_style()`,
       
   270  *   but skips compiling a CSS declaration as the style engine takes over this role.
       
   271  * @link https://github.com/wordpress/gutenberg/pull/27555
       
   272  *
       
   273  * @since 6.1.0
       
   274  *
       
   275  * @param string $style_value  A raw style value for a single typography feature from a block's style attribute.
       
   276  * @param string $css_property Slug for the CSS property the inline style sets.
       
   277  * @return string A CSS inline style value.
       
   278  */
       
   279 function wp_typography_get_preset_inline_style_value( $style_value, $css_property ) {
       
   280 	// If the style value is not a preset CSS variable go no further.
       
   281 	if ( empty( $style_value ) || ! str_contains( $style_value, "var:preset|{$css_property}|" ) ) {
       
   282 		return $style_value;
       
   283 	}
       
   284 
       
   285 	/*
       
   286 	 * For backwards compatibility.
       
   287 	 * Presets were removed in WordPress/gutenberg#27555.
       
   288 	 * A preset CSS variable is the style.
       
   289 	 * Gets the style value from the string and return CSS style.
       
   290 	 */
       
   291 	$index_to_splice = strrpos( $style_value, '|' ) + 1;
       
   292 	$slug            = _wp_to_kebab_case( substr( $style_value, $index_to_splice ) );
       
   293 
       
   294 	// Return the actual CSS inline style value,
       
   295 	// e.g. `var(--wp--preset--text-decoration--underline);`.
       
   296 	return sprintf( 'var(--wp--preset--%s--%s);', $css_property, $slug );
       
   297 }
       
   298 
       
   299 /**
       
   300  * Renders typography styles/content to the block wrapper.
       
   301  *
       
   302  * @since 6.1.0
       
   303  *
       
   304  * @param string $block_content Rendered block content.
       
   305  * @param array  $block         Block object.
       
   306  * @return string Filtered block content.
       
   307  */
       
   308 function wp_render_typography_support( $block_content, $block ) {
       
   309 	if ( ! isset( $block['attrs']['style']['typography']['fontSize'] ) ) {
       
   310 		return $block_content;
       
   311 	}
       
   312 
       
   313 	$custom_font_size = $block['attrs']['style']['typography']['fontSize'];
       
   314 	$fluid_font_size  = wp_get_typography_font_size_value( array( 'size' => $custom_font_size ) );
       
   315 
       
   316 	/*
       
   317 	 * Checks that $fluid_font_size does not match $custom_font_size,
       
   318 	 * which means it's been mutated by the fluid font size functions.
       
   319 	 */
       
   320 	if ( ! empty( $fluid_font_size ) && $fluid_font_size !== $custom_font_size ) {
       
   321 		// Replaces the first instance of `font-size:$custom_font_size` with `font-size:$fluid_font_size`.
       
   322 		return preg_replace( '/font-size\s*:\s*' . preg_quote( $custom_font_size, '/' ) . '\s*;?/', 'font-size:' . esc_attr( $fluid_font_size ) . ';', $block_content, 1 );
       
   323 	}
       
   324 
       
   325 	return $block_content;
       
   326 }
       
   327 
       
   328 /**
       
   329  * Checks a string for a unit and value and returns an array
       
   330  * consisting of `'value'` and `'unit'`, e.g. array( '42', 'rem' ).
       
   331  *
       
   332  * @since 6.1.0
       
   333  *
       
   334  * @param string|int|float $raw_value Raw size value from theme.json.
       
   335  * @param array            $options   {
       
   336  *     Optional. An associative array of options. Default is empty array.
       
   337  *
       
   338  *     @type string   $coerce_to        Coerce the value to rem or px. Default `'rem'`.
       
   339  *     @type int      $root_size_value  Value of root font size for rem|em <-> px conversion. Default `16`.
       
   340  *     @type string[] $acceptable_units An array of font size units. Default `array( 'rem', 'px', 'em' )`;
       
   341  * }
       
   342  * @return array|null An array consisting of `'value'` and `'unit'` properties on success.
       
   343  *                    `null` on failure.
       
   344  */
       
   345 function wp_get_typography_value_and_unit( $raw_value, $options = array() ) {
       
   346 	if ( ! is_string( $raw_value ) && ! is_int( $raw_value ) && ! is_float( $raw_value ) ) {
       
   347 		_doing_it_wrong(
       
   348 			__FUNCTION__,
       
   349 			__( 'Raw size value must be a string, integer, or float.' ),
       
   350 			'6.1.0'
       
   351 		);
       
   352 		return null;
       
   353 	}
       
   354 
       
   355 	if ( empty( $raw_value ) ) {
       
   356 		return null;
       
   357 	}
       
   358 
       
   359 	// Converts numbers to pixel values by default.
       
   360 	if ( is_numeric( $raw_value ) ) {
       
   361 		$raw_value = $raw_value . 'px';
       
   362 	}
       
   363 
       
   364 	$defaults = array(
       
   365 		'coerce_to'        => '',
       
   366 		'root_size_value'  => 16,
       
   367 		'acceptable_units' => array( 'rem', 'px', 'em' ),
       
   368 	);
       
   369 
       
   370 	$options = wp_parse_args( $options, $defaults );
       
   371 
       
   372 	$acceptable_units_group = implode( '|', $options['acceptable_units'] );
       
   373 	$pattern                = '/^(\d*\.?\d+)(' . $acceptable_units_group . '){1,1}$/';
       
   374 
       
   375 	preg_match( $pattern, $raw_value, $matches );
       
   376 
       
   377 	// Bails out if not a number value and a px or rem unit.
       
   378 	if ( ! isset( $matches[1] ) || ! isset( $matches[2] ) ) {
       
   379 		return null;
       
   380 	}
       
   381 
       
   382 	$value = $matches[1];
       
   383 	$unit  = $matches[2];
       
   384 
       
   385 	/*
       
   386 	 * Default browser font size. Later, possibly could inject some JS to
       
   387 	 * compute this `getComputedStyle( document.querySelector( "html" ) ).fontSize`.
       
   388 	 */
       
   389 	if ( 'px' === $options['coerce_to'] && ( 'em' === $unit || 'rem' === $unit ) ) {
       
   390 		$value = $value * $options['root_size_value'];
       
   391 		$unit  = $options['coerce_to'];
       
   392 	}
       
   393 
       
   394 	if ( 'px' === $unit && ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) ) {
       
   395 		$value = $value / $options['root_size_value'];
       
   396 		$unit  = $options['coerce_to'];
       
   397 	}
       
   398 
       
   399 	/*
       
   400 	 * No calculation is required if swapping between em and rem yet,
       
   401 	 * since we assume a root size value. Later we might like to differentiate between
       
   402 	 * :root font size (rem) and parent element font size (em) relativity.
       
   403 	 */
       
   404 	if ( ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) && ( 'em' === $unit || 'rem' === $unit ) ) {
       
   405 		$unit = $options['coerce_to'];
       
   406 	}
       
   407 
       
   408 	return array(
       
   409 		'value' => round( $value, 3 ),
       
   410 		'unit'  => $unit,
       
   411 	);
       
   412 }
       
   413 
       
   414 /**
       
   415  * Internal implementation of CSS clamp() based on available min/max viewport
       
   416  * width and min/max font sizes.
       
   417  *
       
   418  * @since 6.1.0
       
   419  * @since 6.3.0 Checks for unsupported min/max viewport values that cause invalid clamp values.
       
   420  * @since 6.5.0 Returns early when min and max viewport subtraction is zero to avoid division by zero.
   188  * @access private
   421  * @access private
   189  *
   422  *
   190  * @param array  $attributes   Block's attributes.
   423  * @param array $args {
   191  * @param string $feature      Key for the feature within the typography styles.
   424  *     Optional. An associative array of values to calculate a fluid formula
   192  * @param string $css_property Slug for the CSS property the inline style sets.
   425  *     for font size. Default is empty array.
   193  * @return string CSS inline style.
   426  *
   194  */
   427  *     @type string $maximum_viewport_width Maximum size up to which type will have fluidity.
   195 function wp_typography_get_css_variable_inline_style( $attributes, $feature, $css_property ) {
   428  *     @type string $minimum_viewport_width Minimum viewport size from which type will have fluidity.
   196 	// Retrieve current attribute value or skip if not found.
   429  *     @type string $maximum_font_size      Maximum font size for any clamp() calculation.
   197 	$style_value = _wp_array_get( $attributes, array( 'style', 'typography', $feature ), false );
   430  *     @type string $minimum_font_size      Minimum font size for any clamp() calculation.
   198 	if ( ! $style_value ) {
   431  *     @type int    $scale_factor           A scale factor to determine how fast a font scales within boundaries.
   199 		return;
   432  * }
   200 	}
   433  * @return string|null A font-size value using clamp() on success, otherwise null.
   201 
   434  */
   202 	// If we don't have a preset CSS variable, we'll assume it's a regular CSS value.
   435 function wp_get_computed_fluid_typography_value( $args = array() ) {
   203 	if ( strpos( $style_value, "var:preset|{$css_property}|" ) === false ) {
   436 	$maximum_viewport_width_raw = isset( $args['maximum_viewport_width'] ) ? $args['maximum_viewport_width'] : null;
   204 		return sprintf( '%s:%s;', $css_property, $style_value );
   437 	$minimum_viewport_width_raw = isset( $args['minimum_viewport_width'] ) ? $args['minimum_viewport_width'] : null;
   205 	}
   438 	$maximum_font_size_raw      = isset( $args['maximum_font_size'] ) ? $args['maximum_font_size'] : null;
   206 
   439 	$minimum_font_size_raw      = isset( $args['minimum_font_size'] ) ? $args['minimum_font_size'] : null;
   207 	// We have a preset CSS variable as the style.
   440 	$scale_factor               = isset( $args['scale_factor'] ) ? $args['scale_factor'] : null;
   208 	// Get the style value from the string and return CSS style.
   441 
   209 	$index_to_splice = strrpos( $style_value, '|' ) + 1;
   442 	// Normalizes the minimum font size in order to use the value for calculations.
   210 	$slug            = substr( $style_value, $index_to_splice );
   443 	$minimum_font_size = wp_get_typography_value_and_unit( $minimum_font_size_raw );
   211 
   444 
   212 	// Return the actual CSS inline style e.g. `text-decoration:var(--wp--preset--text-decoration--underline);`.
   445 	/*
   213 	return sprintf( '%s:var(--wp--preset--%s--%s);', $css_property, $css_property, $slug );
   446 	 * We get a 'preferred' unit to keep units consistent when calculating,
       
   447 	 * otherwise the result will not be accurate.
       
   448 	 */
       
   449 	$font_size_unit = isset( $minimum_font_size['unit'] ) ? $minimum_font_size['unit'] : 'rem';
       
   450 
       
   451 	// Normalizes the maximum font size in order to use the value for calculations.
       
   452 	$maximum_font_size = wp_get_typography_value_and_unit(
       
   453 		$maximum_font_size_raw,
       
   454 		array(
       
   455 			'coerce_to' => $font_size_unit,
       
   456 		)
       
   457 	);
       
   458 
       
   459 	// Checks for mandatory min and max sizes, and protects against unsupported units.
       
   460 	if ( ! $maximum_font_size || ! $minimum_font_size ) {
       
   461 		return null;
       
   462 	}
       
   463 
       
   464 	// Uses rem for accessible fluid target font scaling.
       
   465 	$minimum_font_size_rem = wp_get_typography_value_and_unit(
       
   466 		$minimum_font_size_raw,
       
   467 		array(
       
   468 			'coerce_to' => 'rem',
       
   469 		)
       
   470 	);
       
   471 
       
   472 	// Viewport widths defined for fluid typography. Normalize units.
       
   473 	$maximum_viewport_width = wp_get_typography_value_and_unit(
       
   474 		$maximum_viewport_width_raw,
       
   475 		array(
       
   476 			'coerce_to' => $font_size_unit,
       
   477 		)
       
   478 	);
       
   479 	$minimum_viewport_width = wp_get_typography_value_and_unit(
       
   480 		$minimum_viewport_width_raw,
       
   481 		array(
       
   482 			'coerce_to' => $font_size_unit,
       
   483 		)
       
   484 	);
       
   485 
       
   486 	// Protects against unsupported units in min and max viewport widths.
       
   487 	if ( ! $minimum_viewport_width || ! $maximum_viewport_width ) {
       
   488 		return null;
       
   489 	}
       
   490 
       
   491 	// Calculates the linear factor denominator. If it's 0, we cannot calculate a fluid value.
       
   492 	$linear_factor_denominator = $maximum_viewport_width['value'] - $minimum_viewport_width['value'];
       
   493 	if ( empty( $linear_factor_denominator ) ) {
       
   494 		return null;
       
   495 	}
       
   496 
       
   497 	/*
       
   498 	 * Build CSS rule.
       
   499 	 * Borrowed from https://websemantics.uk/tools/responsive-font-calculator/.
       
   500 	 */
       
   501 	$view_port_width_offset = round( $minimum_viewport_width['value'] / 100, 3 ) . $font_size_unit;
       
   502 	$linear_factor          = 100 * ( ( $maximum_font_size['value'] - $minimum_font_size['value'] ) / ( $linear_factor_denominator ) );
       
   503 	$linear_factor_scaled   = round( $linear_factor * $scale_factor, 3 );
       
   504 	$linear_factor_scaled   = empty( $linear_factor_scaled ) ? 1 : $linear_factor_scaled;
       
   505 	$fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor_scaled)";
       
   506 
       
   507 	return "clamp($minimum_font_size_raw, $fluid_target_font_size, $maximum_font_size_raw)";
       
   508 }
       
   509 
       
   510 /**
       
   511  * Returns a font-size value based on a given font-size preset.
       
   512  * Takes into account fluid typography parameters and attempts to return a CSS
       
   513  * formula depending on available, valid values.
       
   514  *
       
   515  * @since 6.1.0
       
   516  * @since 6.1.1 Adjusted rules for min and max font sizes.
       
   517  * @since 6.2.0 Added 'settings.typography.fluid.minFontSize' support.
       
   518  * @since 6.3.0 Using layout.wideSize as max viewport width, and logarithmic scale factor to calculate minimum font scale.
       
   519  * @since 6.4.0 Added configurable min and max viewport width values to the typography.fluid theme.json schema.
       
   520  * @since 6.6.0 Deprecated bool argument $should_use_fluid_typography.
       
   521  *
       
   522  * @param array      $preset   {
       
   523  *     Required. fontSizes preset value as seen in theme.json.
       
   524  *
       
   525  *     @type string           $name Name of the font size preset.
       
   526  *     @type string           $slug Kebab-case, unique identifier for the font size preset.
       
   527  *     @type string|int|float $size CSS font-size value, including units if applicable.
       
   528  * }
       
   529  * @param bool|array $settings Optional Theme JSON settings array that overrides any global theme settings.
       
   530  *                             Default is false.
       
   531  * @return string|null Font-size value or null if a size is not passed in $preset.
       
   532  */
       
   533 
       
   534 
       
   535 function wp_get_typography_font_size_value( $preset, $settings = array() ) {
       
   536 	if ( ! isset( $preset['size'] ) ) {
       
   537 		return null;
       
   538 	}
       
   539 
       
   540 	/*
       
   541 	 * Catches empty values and 0/'0'.
       
   542 	 * Fluid calculations cannot be performed on 0.
       
   543 	 */
       
   544 	if ( empty( $preset['size'] ) ) {
       
   545 		return $preset['size'];
       
   546 	}
       
   547 
       
   548 	/*
       
   549 	 * As a boolean (deprecated since 6.6), $settings acts as an override to switch fluid typography "on" (`true`) or "off" (`false`).
       
   550 	 */
       
   551 	if ( is_bool( $settings ) ) {
       
   552 		_deprecated_argument( __FUNCTION__, '6.6.0', __( '`boolean` type for second argument `$settings` is deprecated. Use `array()` instead.' ) );
       
   553 		$settings = array(
       
   554 			'typography' => array(
       
   555 				'fluid' => $settings,
       
   556 			),
       
   557 		);
       
   558 	}
       
   559 
       
   560 	// Fallback to global settings as default.
       
   561 	$global_settings = wp_get_global_settings();
       
   562 	$settings        = wp_parse_args(
       
   563 		$settings,
       
   564 		$global_settings
       
   565 	);
       
   566 
       
   567 	$typography_settings         = isset( $settings['typography'] ) ? $settings['typography'] : array();
       
   568 	$should_use_fluid_typography = ! empty( $typography_settings['fluid'] );
       
   569 
       
   570 	if ( ! $should_use_fluid_typography ) {
       
   571 		return $preset['size'];
       
   572 	}
       
   573 
       
   574 	// $typography_settings['fluid'] can be a bool or an array. Normalize to array.
       
   575 	$fluid_settings  = is_array( $typography_settings['fluid'] ) ? $typography_settings['fluid'] : array();
       
   576 	$layout_settings = isset( $settings['layout'] ) ? $settings['layout'] : array();
       
   577 
       
   578 	// Defaults.
       
   579 	$default_maximum_viewport_width       = '1600px';
       
   580 	$default_minimum_viewport_width       = '320px';
       
   581 	$default_minimum_font_size_factor_max = 0.75;
       
   582 	$default_minimum_font_size_factor_min = 0.25;
       
   583 	$default_scale_factor                 = 1;
       
   584 	$default_minimum_font_size_limit      = '14px';
       
   585 
       
   586 	// Defaults overrides.
       
   587 	$minimum_viewport_width = isset( $fluid_settings['minViewportWidth'] ) ? $fluid_settings['minViewportWidth'] : $default_minimum_viewport_width;
       
   588 	$maximum_viewport_width = isset( $layout_settings['wideSize'] ) && ! empty( wp_get_typography_value_and_unit( $layout_settings['wideSize'] ) ) ? $layout_settings['wideSize'] : $default_maximum_viewport_width;
       
   589 	if ( isset( $fluid_settings['maxViewportWidth'] ) ) {
       
   590 		$maximum_viewport_width = $fluid_settings['maxViewportWidth'];
       
   591 	}
       
   592 	$has_min_font_size       = isset( $fluid_settings['minFontSize'] ) && ! empty( wp_get_typography_value_and_unit( $fluid_settings['minFontSize'] ) );
       
   593 	$minimum_font_size_limit = $has_min_font_size ? $fluid_settings['minFontSize'] : $default_minimum_font_size_limit;
       
   594 
       
   595 	// Font sizes.
       
   596 	$fluid_font_size_settings = isset( $preset['fluid'] ) ? $preset['fluid'] : null;
       
   597 
       
   598 	// A font size has explicitly bypassed fluid calculations.
       
   599 	if ( false === $fluid_font_size_settings ) {
       
   600 		return $preset['size'];
       
   601 	}
       
   602 
       
   603 	// Try to grab explicit min and max fluid font sizes.
       
   604 	$minimum_font_size_raw = isset( $fluid_font_size_settings['min'] ) ? $fluid_font_size_settings['min'] : null;
       
   605 	$maximum_font_size_raw = isset( $fluid_font_size_settings['max'] ) ? $fluid_font_size_settings['max'] : null;
       
   606 
       
   607 	// Font sizes.
       
   608 	$preferred_size = wp_get_typography_value_and_unit( $preset['size'] );
       
   609 
       
   610 	// Protects against unsupported units.
       
   611 	if ( empty( $preferred_size['unit'] ) ) {
       
   612 		return $preset['size'];
       
   613 	}
       
   614 
       
   615 	/*
       
   616 	 * Normalizes the minimum font size limit according to the incoming unit,
       
   617 	 * in order to perform comparative checks.
       
   618 	 */
       
   619 	$minimum_font_size_limit = wp_get_typography_value_and_unit(
       
   620 		$minimum_font_size_limit,
       
   621 		array(
       
   622 			'coerce_to' => $preferred_size['unit'],
       
   623 		)
       
   624 	);
       
   625 
       
   626 	// Don't enforce minimum font size if a font size has explicitly set a min and max value.
       
   627 	if ( ! empty( $minimum_font_size_limit ) && ( ! $minimum_font_size_raw && ! $maximum_font_size_raw ) ) {
       
   628 		/*
       
   629 		 * If a minimum size was not passed to this function
       
   630 		 * and the user-defined font size is lower than $minimum_font_size_limit,
       
   631 		 * do not calculate a fluid value.
       
   632 		 */
       
   633 		if ( $preferred_size['value'] <= $minimum_font_size_limit['value'] ) {
       
   634 			return $preset['size'];
       
   635 		}
       
   636 	}
       
   637 
       
   638 	// If no fluid max font size is available use the incoming value.
       
   639 	if ( ! $maximum_font_size_raw ) {
       
   640 		$maximum_font_size_raw = $preferred_size['value'] . $preferred_size['unit'];
       
   641 	}
       
   642 
       
   643 	/*
       
   644 	 * If no minimumFontSize is provided, create one using
       
   645 	 * the given font size multiplied by the min font size scale factor.
       
   646 	 */
       
   647 	if ( ! $minimum_font_size_raw ) {
       
   648 		$preferred_font_size_in_px = 'px' === $preferred_size['unit'] ? $preferred_size['value'] : $preferred_size['value'] * 16;
       
   649 
       
   650 		/*
       
   651 		 * The scale factor is a multiplier that affects how quickly the curve will move towards the minimum,
       
   652 		 * that is, how quickly the size factor reaches 0 given increasing font size values.
       
   653 		 * For a - b * log2(), lower values of b will make the curve move towards the minimum faster.
       
   654 		 * The scale factor is constrained between min and max values.
       
   655 		 */
       
   656 		$minimum_font_size_factor     = min( max( 1 - 0.075 * log( $preferred_font_size_in_px, 2 ), $default_minimum_font_size_factor_min ), $default_minimum_font_size_factor_max );
       
   657 		$calculated_minimum_font_size = round( $preferred_size['value'] * $minimum_font_size_factor, 3 );
       
   658 
       
   659 		// Only use calculated min font size if it's > $minimum_font_size_limit value.
       
   660 		if ( ! empty( $minimum_font_size_limit ) && $calculated_minimum_font_size <= $minimum_font_size_limit['value'] ) {
       
   661 			$minimum_font_size_raw = $minimum_font_size_limit['value'] . $minimum_font_size_limit['unit'];
       
   662 		} else {
       
   663 			$minimum_font_size_raw = $calculated_minimum_font_size . $preferred_size['unit'];
       
   664 		}
       
   665 	}
       
   666 
       
   667 	$fluid_font_size_value = wp_get_computed_fluid_typography_value(
       
   668 		array(
       
   669 			'minimum_viewport_width' => $minimum_viewport_width,
       
   670 			'maximum_viewport_width' => $maximum_viewport_width,
       
   671 			'minimum_font_size'      => $minimum_font_size_raw,
       
   672 			'maximum_font_size'      => $maximum_font_size_raw,
       
   673 			'scale_factor'           => $default_scale_factor,
       
   674 		)
       
   675 	);
       
   676 
       
   677 	if ( ! empty( $fluid_font_size_value ) ) {
       
   678 		return $fluid_font_size_value;
       
   679 	}
       
   680 
       
   681 	return $preset['size'];
   214 }
   682 }
   215 
   683 
   216 // Register the block support.
   684 // Register the block support.
   217 WP_Block_Supports::get_instance()->register(
   685 WP_Block_Supports::get_instance()->register(
   218 	'typography',
   686 	'typography',