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