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

Webfonts API #37140

Merged
merged 35 commits into from
Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
325d4d2
rebase - combining 46 commits to 1 and resolving lots of conflicts
aristath Jan 24, 2022
c513718
Revert changes to resolver class
oandregal Feb 8, 2022
c335778
Override the parts we need
oandregal Feb 8, 2022
f9cc7d2
Rename file for a better description
oandregal Feb 8, 2022
32193e4
Add webfonts to the parent theme
aristath Feb 9, 2022
bfe756b
indentation fix
aristath Feb 9, 2022
699ab2f
Update Global Styles endpoint to use Gutenberg callback and theme jso…
creativecoder Feb 9, 2022
c8144fd
rebase - combining 46 commits to 1 and resolving lots of conflicts
aristath Jan 24, 2022
a51d2af
Add missing textdomain
aristath Feb 10, 2022
2e56ef5
add missing inline docs
aristath Feb 10, 2022
42fd092
inline doc (copy from 5.9 class)
aristath Feb 10, 2022
6fe2971
Remove extra blank line
aristath Feb 10, 2022
fc44a08
Remove non-applicable docs
aristath Feb 10, 2022
ddb51ce
This already exists in the parent
aristath Feb 10, 2022
524d8af
add full item
aristath Feb 15, 2022
32770bc
add missing global styles
aristath Feb 15, 2022
151effe
remove multiples
aristath Feb 15, 2022
bbd3d27
update test
aristath Feb 15, 2022
938771d
remove extra comma
aristath Feb 15, 2022
5aa2217
Missed this in previous commit
aristath Feb 15, 2022
0112e50
Remove old docs (no longer applicable)
aristath Feb 17, 2022
51bcf85
doc
aristath Feb 18, 2022
38d9cb3
revert 5.9 changes
aristath Feb 18, 2022
33bcfd6
get_merged_data no longer needs to be overriden
aristath Feb 18, 2022
9d3dd5d
use static instead of self
aristath Feb 18, 2022
447daac
simplify
aristath Feb 18, 2022
8a6dec7
add an explanation for when porting to wp-core
aristath Feb 18, 2022
f6e58dd
Revert adding fonts to the webfonts stylesheet
oandregal Feb 21, 2022
19248d2
Update test
oandregal Feb 21, 2022
af8eb3d
explain why we skip the provider
aristath Feb 28, 2022
5ed7aa1
Move webfonts-API files to the compat/wordpress-6.0 folder
aristath Feb 28, 2022
0376a3e
Remove out-of-date comment
aristath Feb 28, 2022
2f865fc
Trigger a notice when an unregistered provider is used.
aristath Feb 28, 2022
8b63574
use error_log instead of trigger_error
aristath Feb 28, 2022
88f86de
typo
aristath Feb 28, 2022
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
263 changes: 263 additions & 0 deletions lib/class-wp-webfonts-provider-local.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
<?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.
*
* When enqueued styles are rendered, the Controller passes its
* 'local' webfonts {@see WP_Webfonts_Provider::set_setfonts()}
* and then triggers {@see WP_Webfonts_Provider_Local::get_css()}
* the processing to transform them into `@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".
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;
}
}
68 changes: 68 additions & 0 deletions lib/class-wp-webfonts-provider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* Webfonts API: Provider abstract class.
*
* Individual webfonts providers should extend this class and implement.
*
* @package WordPress
* @subpackage WebFonts
* @since 6.0.0
*/

/**
* Abstract class for Webfonts API providers.
*
* The starting point to building a webfont service provider.
*
* What is a Provider?
*
* A provider contains the know-how (business logic) for how to
* process its specific font service (i.e. local or remote)
* and how to generate the `@font-face` styles for its service.
*
* It receives a collection of webfonts from the Controller
* {@see WP_Webfonts_Provider::set_setfonts()}, and when requested
* {@see WP_Webfonts_Provider::get_css()}, it transforms them
* into styles (in a performant way for the provider service
* it manages).
*
* @since 6.0.0
*/
abstract class WP_Webfonts_Provider {

/**
* Webfonts to be processed.
*
* @since 6.0.0
*
* @var array[]
*/
protected $webfonts = array();

/**
* Sets this provider's webfonts property.
*
* The API's Controller passes this provider's webfonts
* for processing here in the provider.
*
* @since 6.0.0
*
* @param array[] $webfonts Registered webfonts.
*/
public function set_webfonts( array $webfonts ) {
$this->webfonts = $webfonts;
}

/**
* Gets the `@font-face` CSS for the provider's webfonts.
*
* This method is where the provider does it processing to build the
* needed `@font-face` CSS for all of its webfonts. Specifics of how
* this processing is done is contained in each provider.
*
* @since 6.0.0
*
* @return string The `@font-face` CSS.
*/
abstract public function get_css();
}
Loading