Skip to content

Commit

Permalink
Editor: Format and sanitize font family names according the CSS spec.
Browse files Browse the repository at this point in the history
This fixes two bugs in the font library. 
 - Fonts using special characters were not being rendered properly in the frontend.
 - Allows the ability to use generic() in font family names.

Props mmaattiiaass, nithins53, kafleg, vivekawsm, swissspidy, audrasjb.
Fixes #60537.

git-svn-id: https://develop.svn.wordpress.org/trunk@57657 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
youknowriad committed Feb 20, 2024
1 parent b05838f commit d7672c4
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 31 deletions.
6 changes: 4 additions & 2 deletions src/wp-includes/fonts/class-wp-font-collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,10 @@ private static function get_sanitization_schema() {
array(
'font_family_settings' => array(
'name' => 'sanitize_text_field',
'slug' => 'sanitize_title',
'fontFamily' => 'sanitize_text_field',
'slug' => static function ( $value ) {
return _wp_to_kebab_case( sanitize_title( $value ) );
},
'fontFamily' => 'WP_Font_Utils::sanitize_font_family',
'preview' => 'sanitize_url',
'fontFace' => array(
array(
Expand Down
58 changes: 38 additions & 20 deletions src/wp-includes/fonts/class-wp-font-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,36 @@
* @access private
*/
class WP_Font_Utils {
/**
* Adds surrounding quotes to font family names that contain special characters.
*
* It follows the recommendations from the CSS Fonts Module Level 4.
* @link https://www.w3.org/TR/css-fonts-4/#font-family-prop
*
* @since 6.5.0
*
* @param string $item A font family name.
* @return string The font family name with surrounding quotes, if necessary.
*/
private static function maybe_add_quotes( $item ) {
// Matches strings that are not exclusively alphabetic characters or hyphens, and do not exactly follow the pattern generic(alphabetic characters or hyphens).
$regex = '/^(?!generic\([a-zA-Z\-]+\)$)(?!^[a-zA-Z\-]+$).+/';
$item = trim( $item );
if ( preg_match( $regex, $item ) ) {
$item = trim( $item, "\"'" );
return '"' . $item . '"';
}
return $item;
}

/**
* Sanitizes and formats font family names.
*
* - Applies `sanitize_text_field`
* - Adds surrounding quotes to names that contain spaces and are not already quoted
* - Applies `sanitize_text_field`.
* - Adds surrounding quotes to names containing any characters that are not alphabetic or dashes.
*
* It follows the recommendations from the CSS Fonts Module Level 4.
* @link https://www.w3.org/TR/css-fonts-4/#font-family-prop
*
* @since 6.5.0
* @access private
Expand All @@ -37,26 +62,19 @@ public static function sanitize_font_family( $font_family ) {
return '';
}

$font_family = sanitize_text_field( $font_family );
$font_families = explode( ',', $font_family );
$wrapped_font_families = array_map(
function ( $family ) {
$trimmed = trim( $family );
if ( ! empty( $trimmed ) && str_contains( $trimmed, ' ' ) && ! str_contains( $trimmed, "'" ) && ! str_contains( $trimmed, '"' ) ) {
return '"' . $trimmed . '"';
$output = sanitize_text_field( $font_family );
$formatted_items = array();
if ( str_contains( $output, ',' ) ) {
$items = explode( ',', $output );
foreach ( $items as $item ) {
$formatted_item = self::maybe_add_quotes( $item );
if ( ! empty( $formatted_item ) ) {
$formatted_items[] = $formatted_item;
}
return $trimmed;
},
$font_families
);

if ( count( $wrapped_font_families ) === 1 ) {
$font_family = $wrapped_font_families[0];
} else {
$font_family = implode( ', ', $wrapped_font_families );
}
return implode( ', ', $formatted_items );
}

return $font_family;
return self::maybe_add_quotes( $output );
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public function data_create_font_collection() {
'font_families' => array(
array(
'font_family_settings' => array(
'fontFamily' => 'Open Sans, sans-serif',
'fontFamily' => '"Open Sans", sans-serif',
'slug' => 'open-sans',
'name' => 'Open Sans',
'fontFace' => array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,13 @@ public function test_should_sanitize_font_family( $font_family, $expected ) {
public function data_should_sanitize_font_family() {
return array(
'data_families_with_spaces_and_numbers' => array(
'font_family' => 'Rock 3D , Open Sans,serif',
'expected' => '"Rock 3D", "Open Sans", serif',
'font_family' => 'Arial, Rock 3D , Open Sans,serif',
'expected' => 'Arial, "Rock 3D", "Open Sans", serif',
),
'data_single_font_family' => array(
'font_family' => 'Rock 3D',
'expected' => '"Rock 3D"',
),
'data_no_spaces' => array(
'font_family' => 'Rock3D',
'expected' => 'Rock3D',
),
'data_many_spaces_and_existing_quotes' => array(
'font_family' => 'Rock 3D serif, serif,sans-serif, "Open Sans"',
'expected' => '"Rock 3D serif", serif, sans-serif, "Open Sans"',
Expand All @@ -58,6 +54,10 @@ public function data_should_sanitize_font_family() {
'font_family' => " Rock 3D</style><script>alert('XSS');</script>\n ",
'expected' => '"Rock 3D"',
),
'data_font_family_with_generic_names' => array(
'font_family' => 'generic(kai), generic(font[name]), generic(fangsong), Rock 3D',
'expected' => 'generic(kai), "generic(font[name])", generic(fangsong), "Rock 3D"',
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ public function test_update_item() {

$settings = array(
'name' => 'Open Sans',
'fontFamily' => '"Open Sans, "Noto Sans", sans-serif',
'fontFamily' => 'Open Sans, "Noto Sans", sans-serif',
'preview' => 'https://s.w.org/images/fonts/16.9/previews/open-sans/open-sans-400-normal.svg',
);

Expand All @@ -700,7 +700,7 @@ public function test_update_item() {
$expected_settings = array(
'name' => $settings['name'],
'slug' => 'open-sans-2',
'fontFamily' => $settings['fontFamily'],
'fontFamily' => '"Open Sans", "Noto Sans", sans-serif',
'preview' => $settings['preview'],
);
$this->assertSame( $expected_settings, $data['font_family_settings'], 'The response font_family_settings should match expected settings.' );
Expand Down

0 comments on commit d7672c4

Please sign in to comment.