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

[Fonts API] Refactor Theme JSON global functions into WP_Fonts_Resolver #50811

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8f7db42
Rename register-fonts-from-theme-json.php to class-wp-fonts-theme-jso…
anton-vlasenko May 5, 2023
9aef1bb
Guard the class.
anton-vlasenko May 8, 2023
2ae9a5e
Move gutenberg_register_fonts_from_theme_json() and gutenberg_add_reg…
anton-vlasenko May 15, 2023
2b6ca3c
Rename WP_Fonts_Theme_Json_Handler::gutenberg_register_fonts_from_the…
anton-vlasenko May 15, 2023
d3e5875
Rename WP_Fonts_Theme_Json_Handler::gutenberg_add_registered_fonts_to…
anton-vlasenko May 15, 2023
e86d6f9
Move part of the code to WP_Fonts_Theme_Json_Handler::parse_font_fami…
anton-vlasenko May 15, 2023
a7121fe
Fix PHPDoc block.
anton-vlasenko May 17, 2023
e61fda9
Move anonymous function into a static method.
anton-vlasenko May 17, 2023
0069976
Fix PHPDoc block.
anton-vlasenko May 18, 2023
87c7f37
Move methods within the file.
anton-vlasenko May 19, 2023
12c0684
Rename to ::get_font_families.
anton-vlasenko May 19, 2023
1b457a2
Rename ::get_families to ::get_font_families.
anton-vlasenko May 19, 2023
367cc15
Fix CS.
anton-vlasenko May 19, 2023
c8b384a
Fix @covers
anton-vlasenko May 19, 2023
a6e8a03
Update PHPDoc blocks.
anton-vlasenko May 21, 2023
6b2a4a9
Rename files.
anton-vlasenko May 22, 2023
326da9c
Rename files.
anton-vlasenko May 22, 2023
6411d24
Update PHPDoc blocks.
anton-vlasenko May 22, 2023
901182f
Relocate code to WP_Fonts_Resolver
hellofromtonya May 25, 2023
0a4a7f1
Rename add_registered_fonts to add_missing_fonts.
hellofromtonya May 25, 2023
0ed7cc2
Define registered origin on WP_Fonts
hellofromtonya May 25, 2023
1214f8d
Additional refactorings
hellofromtonya May 25, 2023
c146215
Girlscouting: since, webfont, spacing.
hellofromtonya May 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ public static function get_theme_data( $deprecated = array(), $options = array()
}

// BEGIN OF EXPERIMENTAL CODE. Not to backport to core.
static::$theme = gutenberg_add_registered_fonts_to_theme_json( static::$theme );
static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme );
// END OF EXPERIMENTAL CODE.

}
Expand Down
289 changes: 289 additions & 0 deletions lib/experimental/fonts-api/class-wp-fonts-resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,293 @@ private static function get_value_from_style( $style, $preset_type = 'font-famil
$length = strpos( $style, $ending_pattern ) - $offset;
return substr( $style, $offset, $length );
}

/**
* Register fonts defined in theme.json.
*
* @since X.X.X
*/
public static function register_fonts_from_theme_json() {

$settings = static::get_settings();
// Bail out early if there are no settings for fonts.
if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) {
return;
}

list( $fonts, $handles ) = static::parse_font_families( $settings );

wp_register_fonts( $fonts );
wp_enqueue_fonts( $handles );
}

/**
* Add missing fonts to the global styles.
*
* @since X.X.X
*
* @param WP_Theme_JSON_Gutenberg|WP_Theme_JSON $data The global styles.
* @return WP_Theme_JSON_Gutenberg|WP_Theme_JSON The global styles with missing fonts.
*/
public static function add_missing_fonts_to_theme_json( $data ) {
$font_families_registered = wp_fonts()->get_registered_font_families();

$raw_data = $data->get_raw_data();

$font_families_from_theme = ! empty( $raw_data['settings']['typography']['fontFamilies']['theme'] )
? $raw_data['settings']['typography']['fontFamilies']['theme']
: array();

// Find missing fonts that are not in the theme's theme.json.
$to_add = array();
if ( ! empty( $font_families_registered ) ) {
$to_add = array_diff( $font_families_registered, static::get_font_families( $font_families_from_theme ) );
}

// Bail out early if there are no missing fonts.
if ( empty( $to_add ) ) {
return $data;
}

/*
* Make sure the path to settings.typography.fontFamilies.theme exists
* before adding missing fonts.
*/
if ( empty( $raw_data['settings'] ) ) {
$raw_data['settings'] = array();
}
$raw_data['settings'] = static::set_tyopgraphy_settings_array_structure( $raw_data['settings'] );

foreach ( $to_add as $font_family_handle ) {
$raw_data['settings']['typography']['fontFamilies']['theme'][] = wp_fonts()->to_theme_json( $font_family_handle );
}

return new WP_Theme_JSON_Gutenberg( $raw_data );
}

/**
* Returns theme's settings and adds fonts defined in variations.
*
* @since X.X.X
*
* @return array An array containing theme's settings.
*/
private static function get_settings() {
// Get settings.
$settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_settings();

if ( ! is_admin() && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
return $settings;
}

// If in the editor, add fonts defined in variations.
$variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations();
$set_theme_structure = true;

foreach ( $variations as $variation ) {

// Skip if settings.typography.fontFamilies are not defined in the variation.
if ( empty( $variation['settings']['typography']['fontFamilies'] ) ) {
continue;
}

// One time, set any missing parts of the array structure.
if ( $set_theme_structure ) {
$set_theme_structure = false;
$settings = static::set_tyopgraphy_settings_array_structure( $settings );
}

// Merge the variation settings with the global settings.
$settings['typography']['fontFamilies']['theme'] = array_merge(
$settings['typography']['fontFamilies']['theme'],
$variation['settings']['typography']['fontFamilies']['theme']
);

// Make sure there are no duplicates.
$settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] );
}

return $settings;
}

/**
* Converts a list of font families into font handles and returns them as an array.
*
* @since X.X.X
*
* @param array $families_data An array of font families data.
* @return array An array containing font handles.
*/
private static function get_font_families( $families_data ) {
$families = array();
foreach ( $families_data as $family ) {
$font_family = WP_Fonts_Utils::get_font_family_from_variation( $family );
$handle = WP_Fonts_Utils::convert_font_family_into_handle( $font_family );
if ( ! empty( $handle ) ) {
$families[ $handle ] = true;
}
}

return ! empty( $families ) ? array_keys( $families ) : array();
}

/**
* Parse font families from theme.json.
*
* @since X.X.X
*
* @param array $settings Font settings to parse.
* @return array Returns an array that contains font data and corresponding handles.
*/
private static function parse_font_families( array $settings ) {
$handles = array();
$fonts = array();

// Look for fontFamilies.
foreach ( $settings['typography']['fontFamilies'] as $font_families ) {
foreach ( $font_families as $font_family ) {

// Skip if fontFace is not defined.
if ( empty( $font_family['fontFace'] ) ) {
continue;
}

$font_family['fontFace'] = (array) $font_family['fontFace'];
$font_family_slug = isset( $font_family['slug'] ) ? $font_family['slug'] : '';

foreach ( $font_family['fontFace'] as $font_face ) {
// Skip if the font was registered through the Fonts API.
if ( isset( $font_face['origin'] ) && WP_Fonts::REGISTERED_ORIGIN === $font_face['origin'] ) {
continue;
}

// For each font "src", convert the "file:./" placeholder into a theme font file URI.
if ( ! empty( $font_face['src'] ) ) {
$font_face['src'] = static::to_theme_file_uri( (array) $font_face['src'] );
}

// Convert font-face properties into kebab-case.
$font_face = static::to_kebab_case( $font_face );

// Convert font-face properties into kebab-case.
$font_face = static::to_kebab_case( $font_face );

$font_family_handle = static::get_font_family_handle( $font_family_slug, $font_face );

// Skip if no font-family handle was found.
if ( null === $font_family_handle ) {
continue;
}

$handles[] = $font_family_handle;
if ( ! array_key_exists( $font_family_handle, $fonts ) ) {
$fonts[ $font_family_handle ] = array();
}

$fonts[ $font_family_handle ][] = $font_face;
}
}
}

return array( $fonts, $handles );
}

/**
* Sets the typography.fontFamilies.theme structure in the given array, if not already set.
* if not already set.
*
* @since X.X.X
*
* @param array $data The target array to process.
* @return array Data array with typography.fontFamilies.theme structure set.
*/
private static function set_tyopgraphy_settings_array_structure( array $data ) {
if ( empty( $data['typography'] ) ) {
$data['typography'] = array();
}
if ( empty( $data['typography']['fontFamilies'] ) ) {
$data['typography']['fontFamilies'] = array();
}
if ( empty( $data['typography']['fontFamilies']['theme'] ) ) {
$data['typography']['fontFamilies']['theme'] = array();
}

return $data;
}

/**
* Converts each 'file:./' placeholder into a URI to the font file in the theme.
*
* The 'file:./' is specified in the theme's `theme.json` as a placeholder to be
* replaced with the URI to the font file's location in the theme. When a "src"
* beings with this placeholder, it is replaced, converting the src into a URI.
*
* @since X.X.X
*
* @param array $src An array of font file sources to process.
* @return array An array of font file src URI(s).
*/
private static function to_theme_file_uri( array $src ) {
$placeholder = 'file:./';

foreach ( $src as $src_key => $src_url ) {
// Skip if the src doesn't start with the placeholder, as there's nothing to replace.
if ( ! str_starts_with( $src_url, $placeholder ) ) {
continue;
}

$src_file = str_replace( $placeholder, '', $src_url );
$src[ $src_key ] = get_theme_file_uri( $src_file );
}

return $src;
}

/**
* Converts all first dimension keys into kebab-case.
*
* @since X.X.X
*
* @param array $data The array to process.
*/
private static function to_kebab_case( array $data ) {
foreach ( $data as $key => $value ) {
$kebab_case = _wp_to_kebab_case( $key );
$data[ $kebab_case ] = $value;
if ( $kebab_case !== $key ) {
unset( $data[ $key ] );
}
}

return $data;
}

/**
* Gets the font-family handle if defined in the "slug" or font-face variation.
*
* @since X.X.X
*
* @param string $font_family_slug Font-family "slug".
* @param array $font_face Font-face (variation) to search.
* @return string|null Font-family handle on success, else null if not found.
*/
private static function get_font_family_handle( $font_family_slug, array $font_face ) {
$font_family_handle = WP_Fonts_Utils::get_font_family_from_variation( $font_face );

// Use the defined slug if no handle found.
if ( empty( $font_family_handle ) ) {
$font_family_handle = $font_family_slug;
}

if ( ! empty( $font_family_handle ) ) {
$font_family_handle = WP_Fonts_Utils::convert_font_family_into_handle( $font_family_handle );
}

if ( empty( $font_family_handle ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Font family not defined in the variation or "slug".', 'gutenberg' ), '6.1.0' );
return null;
}

return $font_family_handle;
}
}
11 changes: 10 additions & 1 deletion lib/experimental/fonts-api/class-wp-fonts.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
*/
class WP_Fonts extends WP_Dependencies {

/**
* Registered "origin", indicating the font is registered in the API.
*
* @since X.X.X
*
* @var string
*/
const REGISTERED_ORIGIN = 'gutenberg_wp_fonts_api';

/**
* An array of registered providers.
*
Expand Down Expand Up @@ -731,7 +740,7 @@ public function to_theme_json( $font_family_handle ) {
}

$variation_obj = $this->registered[ $variation_handle ];
$variation_properties = array( 'origin' => 'gutenberg_wp_fonts_api' );
$variation_properties = array( 'origin' => static::REGISTERED_ORIGIN );
foreach ( $variation_obj->extra['font-properties'] as $property_name => $property_value ) {
$property_in_camelcase = lcfirst( str_replace( '-', '', ucwords( $property_name, '-' ) ) );
$variation_properties[ $property_in_camelcase ] = $property_value;
Expand Down
9 changes: 9 additions & 0 deletions lib/experimental/fonts-api/fonts-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,12 @@ function( $mime_types ) {
return $mime_types;
}
);

/*
* To make sure blocks are registered before any Theme_JSON operations take place, a priority of 21 is used.
*
* Why 21?
* Blocks are registered via the "init" hook with a priority value of `20`, which is dynamically added
* during the build. See: tools/webpack/blocks.js.
*/
add_action( 'init', 'WP_Fonts_Resolver::register_fonts_from_theme_json', 21 );
Loading