Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typography: replace non-dot decimal separator for float to string conversions #57634

Closed
59 changes: 34 additions & 25 deletions lib/block-supports/typography.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ function gutenberg_render_typography_support( $block_content, $block ) {
}

/**
* Internal method that checks a string for a unit and value and returns an array consisting of `'value'` and `'unit'`, e.g., [ '42', 'rem' ].
* Internal method that checks a string for a unit and value and returns an array consisting of 'value'`, `'unit'` and `'combined'`, e.g., [ 42, 'rem', '42rem' ].
* A raw font size of `value + unit` is expected. If the value is a number, it will convert to `value + 'px'`.
*
* @access private
Expand All @@ -260,7 +260,7 @@ function gutenberg_render_typography_support( $block_content, $block ) {
* @type int $root_size_value Value of root font size for rem|em <-> px conversion. Default `16`.
* @type array<string> $acceptable_units An array of font size units. Default `[ 'rem', 'px', 'em' ]`;
* }
* @return array An array consisting of `'value'` and `'unit'` properties.
* @return array An array consisting of `'value'`, `'unit'` and `'combined'` properties.
*/
function gutenberg_get_typography_value_and_unit( $raw_value, $options = array() ) {
if ( ! is_string( $raw_value ) && ! is_int( $raw_value ) && ! is_float( $raw_value ) ) {
Expand All @@ -276,31 +276,31 @@ function gutenberg_get_typography_value_and_unit( $raw_value, $options = array()
return null;
}

// Converts numeric values to pixel values by default.
if ( is_numeric( $raw_value ) ) {
$raw_value = $raw_value . 'px';
}

$defaults = array(
'coerce_to' => '',
'root_size_value' => 16,
'acceptable_units' => array( 'rem', 'px', 'em' ),
);

$options = wp_parse_args( $options, $defaults );
$options = wp_parse_args( $options, $defaults );

$acceptable_units_group = implode( '|', $options['acceptable_units'] );
$pattern = '/^(\d*\.?\d+)(' . $acceptable_units_group . '){1,1}$/';
// Matches integers and floats (including negative values) with optional decimal (dot or comma) and a unit value.
$pattern = '/^(-?\d*[\.,]?\d+)(' . $acceptable_units_group . '){0,1}$/';

preg_match( $pattern, $raw_value, $matches );

// We need a number value and a px or rem unit.
if ( ! isset( $matches[1] ) || ! isset( $matches[2] ) ) {
// We need a number value.
if ( ! isset( $matches[1] ) ) {
return null;
}

$value = $matches[1];
$unit = $matches[2];
/*
* For floats coerced to strings, locales can use either a comma (,) or dot (.) as decimal separators.
* Use `str_replace` to replace any comma separators with a dot for CSS rules to be valid.
*/
$value = str_replace( ',', '.', $matches[1] );
// Converts units to pixel values by default.
$unit = isset( $matches[2] ) ? $matches[2] : 'px';

// Default browser font size. Later we could inject some JS to compute this `getComputedStyle( document.querySelector( "html" ) ).fontSize`.
if ( 'px' === $options['coerce_to'] && ( 'em' === $unit || 'rem' === $unit ) ) {
Expand All @@ -322,9 +322,12 @@ function gutenberg_get_typography_value_and_unit( $raw_value, $options = array()
$unit = $options['coerce_to'];
}

$value = round( $value, 3 );

return array(
'value' => round( $value, 3 ),
'unit' => $unit,
'value' => $value,
'unit' => $unit,
'combined' => str_replace( ',', '.', $value . $unit ),
Copy link
Member Author

@ramonjd ramonjd Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is an addition of convenience. Any subsequent string coercion runs the risk of triggering the locale behaviour, so do it here once and return the combined value for use in our output.

);
}

Expand Down Expand Up @@ -401,13 +404,17 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) {
return null;
}

// Build CSS rule.
// Borrowed from https://websemantics.uk/tools/responsive-font-calculator/.
$view_port_width_offset = round( $minimum_viewport_width['value'] / 100, 3 ) . $font_size_unit;
/*
* Build CSS rule.
* Borrowed from https://websemantics.uk/tools/responsive-font-calculator/.
* For floats coerced to strings, locales can use either a comma (,) or dot (.) as decimal separators.
* Use `str_replace` to replace any comma separators with a dot for CSS rules to be valid.
*/
$view_port_width_offset = str_replace( ',', '.', round( $minimum_viewport_width['value'] / 100, 3 ) ) . $font_size_unit;
$linear_factor = 100 * ( ( $maximum_font_size['value'] - $minimum_font_size['value'] ) / ( $maximum_viewport_width['value'] - $minimum_viewport_width['value'] ) );
$linear_factor_scaled = round( $linear_factor * $scale_factor, 3 );
$linear_factor_scaled = empty( $linear_factor_scaled ) ? 1 : $linear_factor_scaled;
$fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor_scaled)";
$linear_factor_scaled = $linear_factor * $scale_factor;
$linear_factor_scaled = empty( $linear_factor_scaled ) || 1 === $linear_factor_scaled ? 1 : str_replace( ',', '.', round( $linear_factor_scaled, 3 ) );
$fluid_target_font_size = $minimum_font_size_rem['combined'] . " + ((1vw - $view_port_width_offset) * $linear_factor_scaled)";

return "clamp($minimum_font_size_raw, $fluid_target_font_size, $maximum_font_size_raw)";
}
Expand Down Expand Up @@ -523,7 +530,7 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty

// If no fluid max font size is available use the incoming value.
if ( ! $maximum_font_size_raw ) {
$maximum_font_size_raw = $preferred_size['value'] . $preferred_size['unit'];
$maximum_font_size_raw = $preferred_size['combined'];
}

/*
Expand All @@ -544,9 +551,11 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty

// Only use calculated min font size if it's > $minimum_font_size_limit value.
if ( ! empty( $minimum_font_size_limit ) && $calculated_minimum_font_size <= $minimum_font_size_limit['value'] ) {
$minimum_font_size_raw = $minimum_font_size_limit['value'] . $minimum_font_size_limit['unit'];
$minimum_font_size_raw = $minimum_font_size_limit['combined'];
} else {
$minimum_font_size_raw = $calculated_minimum_font_size . $preferred_size['unit'];
$minimum_font_size_raw = gutenberg_get_typography_value_and_unit(
$calculated_minimum_font_size . $preferred_size['unit']
)['combined'];
}
}

Expand Down
75 changes: 59 additions & 16 deletions phpunit/block-supports/typography-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -787,9 +787,10 @@ public function data_generate_replace_inline_font_styles_with_fluid_values_fixtu
*
* @param mixed $raw_value Raw size value to test.
* @param mixed $expected An expected return value.
* @param array $options Options to pass to function.
*/
public function test_valid_size_wp_get_typography_value_and_unit( $raw_value, $expected ) {
$this->assertEquals( $expected, gutenberg_get_typography_value_and_unit( $raw_value ) );
public function test_valid_size_wp_get_typography_value_and_unit( $raw_value, $expected, $options = array() ) {
$this->assertEquals( $expected, gutenberg_get_typography_value_and_unit( $raw_value, $options ) );
}

/**
Expand All @@ -814,50 +815,92 @@ public function data_valid_size_wp_get_typography_value_and_unit() {
'size: `"10"`' => array(
'raw_value' => '10',
'expected' => array(
'value' => 10,
'unit' => 'px',
'value' => 10,
'unit' => 'px',
'combined' => '10px',
),
),
'size: `11`' => array(
'raw_value' => 11,
'expected' => array(
'value' => 11,
'unit' => 'px',
'value' => 11,
'unit' => 'px',
'combined' => '11px',
),
),
'size: `11.234`' => array(
'raw_value' => '11.234',
'expected' => array(
'value' => 11.234,
'unit' => 'px',
'value' => 11.234,
'unit' => 'px',
'combined' => '11.234px',
),
),
'size: `"12rem"`' => array(
'raw_value' => '12rem',
'expected' => array(
'value' => 12,
'unit' => 'rem',
'value' => 12,
'unit' => 'rem',
'combined' => '12rem',
),
),
'size: `"12px"`' => array(
'raw_value' => '12px',
'expected' => array(
'value' => 12,
'unit' => 'px',
'value' => 12,
'unit' => 'px',
'combined' => '12px',
),
),
'size: `"12em"`' => array(
'raw_value' => '12em',
'expected' => array(
'value' => 12,
'unit' => 'em',
'value' => 12,
'unit' => 'em',
'combined' => '12em',
),
),
'size: `"12.74em"`' => array(
'raw_value' => '12.74em',
'expected' => array(
'value' => 12.74,
'unit' => 'em',
'value' => 12.74,
'unit' => 'em',
'combined' => '12.74em',
),
),
'size: `"7.353vh"`' => array(
'raw_value' => '7.357777vh',
'expected' => array(
'value' => 7.358,
'unit' => 'vh',
'combined' => '7.358vh',
),
'options' => array(
'acceptable_units' => array( 'vh' ),
),
),
'size: `"12,1238rem"`' => array(
'raw_value' => '12,1238rem',
'expected' => array(
'value' => 12.124,
'unit' => 'rem',
'combined' => '12.124rem',
),
),
'size: `"1,7879"`' => array(
'raw_value' => '1,7879',
'expected' => array(
'value' => 1.788,
'unit' => 'px',
'combined' => '1.788px',
),
),
'size: `-10.2rem`' => array(
'raw_value' => '-10.2rem',
'expected' => array(
'value' => -10.2,
'unit' => 'rem',
'combined' => '-10.2rem',
),
),
);
Expand Down
Loading