Skip to content

Commit

Permalink
Webfonts API (#37140)
Browse files Browse the repository at this point in the history
* rebase - combining 46 commits to 1 and resolving lots of conflicts

* Revert changes to resolver class

* Override the parts we need

* Rename file for a better description

* Add webfonts to the parent theme

* indentation fix

* Update Global Styles endpoint to use Gutenberg callback and theme json resolver

* rebase - combining 46 commits to 1 and resolving lots of conflicts

* Add missing textdomain

* add missing inline docs

* inline doc (copy from 5.9 class)

* Remove extra blank line

* Remove non-applicable docs

* This already exists in the parent

* add full item

* add missing global styles

* remove multiples

* update test

* remove extra comma

* Missed this in previous commit

* Remove old docs (no longer applicable)

* doc

* revert 5.9 changes

* get_merged_data no longer needs to be overriden

* use static instead of self

* simplify

* add an explanation for when porting to wp-core

* Revert adding fonts to the webfonts stylesheet

* Update test

* explain why we skip the provider

* Move webfonts-API files to the compat/wordpress-6.0 folder

* Remove out-of-date comment

* Trigger a notice when an unregistered provider is used.

* use error_log instead of trigger_error

* typo

Co-authored-by: André <[email protected]>
Co-authored-by: Grant Kinney <[email protected]>
  • Loading branch information
3 people authored Feb 28, 2022
1 parent f392267 commit b6a787b
Show file tree
Hide file tree
Showing 10 changed files with 1,298 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*
* @access private
*/
class WP_Theme_JSON_Resolver_Gutenberg {
class WP_Theme_JSON_Resolver_5_9 {

/**
* Container for data coming from core.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
/**
* WP_Theme_JSON_Resolver_Gutenberg class
*
* @package gutenberg
*/

/**
* Class that abstracts the processing of the different data sources
* for site-level config and offers an API to work with them.
*
* This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
* This is a low-level API that may need to do breaking changes. Please,
* use get_global_settings, get_global_styles, and get_global_stylesheet instead.
*
* @access private
*/
class WP_Theme_JSON_Resolver_Gutenberg extends WP_Theme_JSON_Resolver_5_9 {

/**
* Returns the theme's data.
*
* Data from theme.json will be backfilled from existing
* theme supports, if any. Note that if the same data
* is present in theme.json and in theme supports,
* the theme.json takes precedence.
*
* @param array $deprecated Deprecated argument.
* @return WP_Theme_JSON_Gutenberg Entity that holds theme data.
*/
public static function get_theme_data( $deprecated = array() ) {
if ( ! empty( $deprecated ) ) {
_deprecated_argument( __METHOD__, '5.9' );
}
if ( null === static::$theme ) {
$theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
$theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
$theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $theme_json_data );
static::$theme = new WP_Theme_JSON_Gutenberg( $theme_json_data );

if ( wp_get_theme()->parent() ) {
// Get parent theme.json.
$parent_theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json', true ) );
$parent_theme_json_data = static::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) );
$parent_theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $parent_theme_json_data );
$parent_theme = new WP_Theme_JSON_Gutenberg( $parent_theme_json_data );

// Merge the child theme.json into the parent theme.json.
// The child theme takes precedence over the parent.
$parent_theme->merge( static::$theme );
static::$theme = $parent_theme;
}
}

/*
* We want the presets and settings declared in theme.json
* to override the ones declared via theme supports.
* So we take theme supports, transform it to theme.json shape
* and merge the static::$theme upon that.
*/
$theme_support_data = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_default_block_editor_settings() );
if ( ! static::theme_has_support() ) {
if ( ! isset( $theme_support_data['settings']['color'] ) ) {
$theme_support_data['settings']['color'] = array();
}

$default_palette = false;
if ( current_theme_supports( 'default-color-palette' ) ) {
$default_palette = true;
}
if ( ! isset( $theme_support_data['settings']['color']['palette'] ) ) {
// If the theme does not have any palette, we still want to show the core one.
$default_palette = true;
}
$theme_support_data['settings']['color']['defaultPalette'] = $default_palette;

$default_gradients = false;
if ( current_theme_supports( 'default-gradient-presets' ) ) {
$default_gradients = true;
}
if ( ! isset( $theme_support_data['settings']['color']['gradients'] ) ) {
// If the theme does not have any gradients, we still want to show the core ones.
$default_gradients = true;
}
$theme_support_data['settings']['color']['defaultGradients'] = $default_gradients;
}
$with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data );
$with_theme_supports->merge( static::$theme );

return $with_theme_supports;
}
}
259 changes: 259 additions & 0 deletions lib/compat/wordpress-6.0/class-wp-webfonts-provider-local.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
<?php
/**
* Webfonts API: Provider for locally-hosted fonts.
*
* @package WordPress
* @subpackage WebFonts
* @since 6.0.0
*/

/**
* A core bundled provider for generating `@font-face` styles
* from locally-hosted font files.
*
* This provider builds an optimized `src` (for browser support)
* and then generates the `@font-face` styles.
*
* All know-how (business logic) for how to interact with and
* generate styles from locally-hosted font files is contained
* in this provider.
*
* @since 6.0.0
*/
class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider {

/**
* The provider's unique ID.
*
* @since 6.0.0
*
* @var string
*/
protected $id = 'local';

/**
* Gets the `@font-face` CSS styles for locally-hosted font files.
*
* This method does the following processing tasks:
* 1. Orchestrates an optimized `src` (with format) for browser support.
* 2. Generates the `@font-face` for all its webfonts.
*
* For example, when given these webfonts:
* <code>
* array(
* 'source-serif-pro.normal.200 900' => array(
* 'provider' => 'local',
* 'font_family' => 'Source Serif Pro',
* 'font_weight' => '200 900',
* 'font_style' => 'normal',
* 'src' => 'https://example.com/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ),
* ),
* 'source-serif-pro.italic.400 900' => array(
* 'provider' => 'local',
* 'font_family' => 'Source Serif Pro',
* 'font_weight' => '200 900',
* 'font_style' => 'italic',
* 'src' => 'https://example.com/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ),
* ),
* )
* </code>
*
* the following `@font-face` styles are generated and returned:
* <code>
*
* @font-face{
* font-family:"Source Serif Pro";
* font-style:normal;
* font-weight:200 900;
* font-stretch:normal;
* src:local("Source Serif Pro"), url('/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');
* }
* @font-face{
* font-family:"Source Serif Pro";
* font-style:italic;
* font-weight:200 900;
* font-stretch:normal;
* src:local("Source Serif Pro"), url('/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');
* }
* </code>
*
* @since 6.0.0
*
* @return string The `@font-face` CSS.
*/
public function get_css() {
$css = '';

foreach ( $this->webfonts as $webfont ) {
// Order the webfont's `src` items to optimize for browser support.
$webfont = $this->order_src( $webfont );

// Build the @font-face CSS for this webfont.
$css .= '@font-face{' . $this->build_font_face_css( $webfont ) . '}';
}

return $css;
}

/**
* Order `src` items to optimize for browser support.
*
* @since 6.0.0
*
* @param array $webfont Webfont to process.
* @return array
*/
private function order_src( array $webfont ) {
if ( ! is_array( $webfont['src'] ) ) {
$webfont['src'] = (array) $webfont['src'];
}

$src = array();
$src_ordered = array();

foreach ( $webfont['src'] as $url ) {
// Add data URIs first.
if ( 0 === strpos( trim( $url ), 'data:' ) ) {
$src_ordered[] = array(
'url' => $url,
'format' => 'data',
);
continue;
}
$format = pathinfo( $url, PATHINFO_EXTENSION );
$src[ $format ] = $url;
}

// Add woff2.
if ( ! empty( $src['woff2'] ) ) {
$src_ordered[] = array(
'url' => $src['woff2'],
'format' => 'woff2',
);
}

// Add woff.
if ( ! empty( $src['woff'] ) ) {
$src_ordered[] = array(
'url' => $src['woff'],
'format' => 'woff',
);
}

// Add ttf.
if ( ! empty( $src['ttf'] ) ) {
$src_ordered[] = array(
'url' => $src['ttf'],
'format' => 'truetype',
);
}

// Add eot.
if ( ! empty( $src['eot'] ) ) {
$src_ordered[] = array(
'url' => $src['eot'],
'format' => 'embedded-opentype',
);
}

// Add otf.
if ( ! empty( $src['otf'] ) ) {
$src_ordered[] = array(
'url' => $src['otf'],
'format' => 'opentype',
);
}
$webfont['src'] = $src_ordered;

return $webfont;
}

/**
* Builds the font-family's CSS.
*
* @since 6.0.0
*
* @param array $webfont Webfont to process.
* @return string This font-family's CSS.
*/
private function build_font_face_css( array $webfont ) {
$css = '';

// Wrap font-family in quotes if it contains spaces.
if (
false !== strpos( $webfont['font-family'], ' ' ) &&
false === strpos( $webfont['font-family'], '"' ) &&
false === strpos( $webfont['font-family'], "'" )
) {
$webfont['font-family'] = '"' . $webfont['font-family'] . '"';
}

foreach ( $webfont as $key => $value ) {

// Skip "provider", since it's for internal API use,
// and not a valid CSS property.
if ( 'provider' === $key ) {
continue;
}

// Compile the "src" parameter.
if ( 'src' === $key ) {
$value = $this->compile_src( $webfont['font-family'], $value );
}

// If font-variation-settings is an array, convert it to a string.
if ( 'font-variation-settings' === $key && is_array( $value ) ) {
$value = $this->compile_variations( $value );
}

if ( ! empty( $value ) ) {
$css .= "$key:$value;";
}
}

return $css;
}

/**
* Compiles the `src` into valid CSS.
*
* @since 6.0.0
*
* @param string $font_family Font family.
* @param array $value Value to process.
* @return string The CSS.
*/
private function compile_src( $font_family, array $value ) {
$src = "local($font_family)";

foreach ( $value as $item ) {

if ( 0 === strpos( $item['url'], get_site_url() ) ) {
$item['url'] = wp_make_link_relative( $item['url'] );
}

$src .= ( 'data' === $item['format'] )
? ", url({$item['url']})"
: ", url('{$item['url']}') format('{$item['format']}')";
}
return $src;
}

/**
* Compiles the font variation settings.
*
* @since 6.0.0
*
* @param array $font_variation_settings Array of font variation settings.
* @return string The CSS.
*/
private function compile_variations( array $font_variation_settings ) {
$variations = '';

foreach ( $font_variation_settings as $key => $value ) {
$variations .= "$key $value";
}

return $variations;
}
}
Loading

0 comments on commit b6a787b

Please sign in to comment.