PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
gutenberg / lib / block-supports / typography.php

typography.php in Gutenberg 24.1.0, at lib/block-supports/typography.php

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