PluginProbe
Gutenberg / 23.3.0
Gutenberg v23.3.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 12.6.0 7.4.0 All 402 releases
gutenberg / lib / block-supports / typography.php

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

651 lines 27.5 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_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