diff --git a/lib/experimental/class-old-wp-webfonts.php b/lib/experimental/class-old-wp-webfonts.php new file mode 100644 index 00000000000000..4c66383765db9f --- /dev/null +++ b/lib/experimental/class-old-wp-webfonts.php @@ -0,0 +1,397 @@ +register_provider( 'local', 'WP_Webfonts_Provider_Local' ); + + // Register callback to generate and enqueue styles. + if ( did_action( 'wp_enqueue_scripts' ) ) { + $this->stylesheet_handle = 'webfonts-footer'; + $hook = 'wp_print_footer_scripts'; + } else { + $this->stylesheet_handle = 'webfonts'; + $hook = 'wp_enqueue_scripts'; + } + add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); + + // Enqueue webfonts in the block editor. + add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); + } + + /** + * Get the list of all fonts. + * + * @return array[] + */ + public function get_all_webfonts() { + return array_merge( $this->get_registered_webfonts(), $this->get_enqueued_webfonts() ); + } + + /** + * Get the list of providers. + * + * @since 6.0.0 + * + * @return WP_Webfonts_Provider[] All registered providers, each keyed by their unique ID. + */ + public function get_providers() { + return $this->providers; + } + + /** + * Register a webfont. + * + * @since 6.0.0 + * + * @param array $webfont Webfont to be registered. + * @return string|false The font family slug if successfully registered, else false. + */ + public function register_webfont( array $webfont ) { + $webfont = $this->validate_webfont( $webfont ); + + // If not valid, bail out. + if ( ! $webfont ) { + return false; + } + + $slug = $this->get_font_slug( $webfont ); + + // Initialize a new font-family collection. + if ( ! isset( $this->registered_webfonts[ $slug ] ) ) { + $this->registered_webfonts[ $slug ] = array(); + } + + $this->registered_webfonts[ $slug ][] = $webfont; + return $slug; + } + + /** + * Enqueue a font-family that has been already registered. + * + * @param string $font_family_name The font family name to be enqueued. + * @return bool True if successfully enqueued, else false. + */ + public function enqueue_webfont( $font_family_name ) { + $slug = $this->get_font_slug( $font_family_name ); + + if ( isset( $this->enqueued_webfonts[ $slug ] ) ) { + return true; + } + + if ( ! isset( $this->registered_webfonts[ $slug ] ) ) { + /* translators: %s unique slug to identify the font family of the webfont */ + _doing_it_wrong( __METHOD__, sprintf( __( 'The "%s" font family is not registered.', 'gutenberg' ), $slug ), '6.0.0' ); + + return false; + } + + $this->enqueued_webfonts[ $slug ] = $this->registered_webfonts[ $slug ]; + unset( $this->registered_webfonts[ $slug ] ); + return true; + } + + /** + * Get the font slug. + * + * @since 6.0.0 + * + * @param array|string $to_convert The value to convert into a slug. Expected as the web font's array + * or a font-family as a string. + * @return string|false The font slug on success, or false if the font-family cannot be determined. + */ + public static function get_font_slug( $to_convert ) { + if ( is_array( $to_convert ) ) { + if ( isset( $to_convert['font-family'] ) ) { + $to_convert = $to_convert['font-family']; + } elseif ( isset( $to_convert['fontFamily'] ) ) { + $to_convert = $to_convert['fontFamily']; + } else { + _doing_it_wrong( __METHOD__, __( 'Could not determine the font family name.', 'gutenberg' ), '6.0.0' ); + return false; + } + } + + return sanitize_title( $to_convert ); + } + + /** + * Validate a webfont. + * + * @since 6.0.0 + * + * @param array $webfont The webfont arguments. + * + * @return array|false The validated webfont arguments, or false if the webfont is invalid. + */ + public function validate_webfont( $webfont ) { + $webfont = wp_parse_args( + $webfont, + array( + 'provider' => 'local', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ) + ); + + // Check the font-family. + if ( empty( $webfont['font-family'] ) || ! is_string( $webfont['font-family'] ) ) { + trigger_error( __( 'Webfont font family must be a non-empty string.', 'gutenberg' ) ); + return false; + } + + // Local fonts need a "src". + if ( 'local' === $webfont['provider'] ) { + // Make sure that local fonts have 'src' defined. + if ( empty( $webfont['src'] ) || ( ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) ) ) { + trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.', 'gutenberg' ) ); + return false; + } + } + + // Validate the 'src' property. + if ( ! empty( $webfont['src'] ) ) { + foreach ( (array) $webfont['src'] as $src ) { + if ( empty( $src ) || ! is_string( $src ) ) { + trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) ); + return false; + } + } + } + + // Check the font-weight. + if ( ! is_string( $webfont['font-weight'] ) && ! is_int( $webfont['font-weight'] ) ) { + trigger_error( __( 'Webfont font weight must be a properly formatted string or integer.', 'gutenberg' ) ); + return false; + } + + // Check the font-display. + if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { + $webfont['font-display'] = 'fallback'; + } + + $valid_props = array( + 'ascend-override', + 'descend-override', + 'font-display', + 'font-family', + 'font-stretch', + 'font-style', + 'font-weight', + 'font-variant', + 'font-feature-settings', + 'font-variation-settings', + 'line-gap-override', + 'size-adjust', + 'src', + 'unicode-range', + + // Exceptions. + 'provider', + ); + + foreach ( $webfont as $prop => $value ) { + if ( ! in_array( $prop, $valid_props, true ) ) { + unset( $webfont[ $prop ] ); + } + } + + return $webfont; + } + + /** + * Register a provider. + * + * @since 6.0.0 + * + * @param string $provider The provider name. + * @param string $class The provider class name. + * @return bool True if successfully registered, else false. + */ + public function register_provider( $provider, $class ) { + if ( empty( $provider ) || empty( $class ) ) { + return false; + } + $this->providers[ $provider ] = $class; + return true; + } + + /** + * Generate and enqueue webfonts styles. + * + * @since 6.0.0 + */ + public function generate_and_enqueue_styles() { + // Generate the styles. + $webfonts = $this->get_webfonts_by_provider( $this->get_enqueued_webfonts() ); + $styles = $this->generate_styles( $webfonts ); + + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; + } + + // Enqueue the stylesheet. + wp_register_style( $this->stylesheet_handle, '' ); + wp_enqueue_style( $this->stylesheet_handle ); + + // Add the styles to the stylesheet. + wp_add_inline_style( $this->stylesheet_handle, $styles ); + } + + /** + * Generate and enqueue editor styles. + * + * @since 6.0.0 + */ + public function generate_and_enqueue_editor_styles() { + // Generate the styles. + $webfonts = $this->get_webfonts_by_provider( $this->get_all_webfonts() ); + $styles = $this->generate_styles( $webfonts ); + + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; + } + + wp_enqueue_style( 'wp-block-library' ); + wp_add_inline_style( 'wp-block-library', $styles ); + } + + /** + * Generate styles for webfonts. + * + * @since 6.0.0 + * + * @param array[] $webfonts_by_provider Webfonts organized by provider. + * @return string $styles Generated styles. + */ + private function generate_styles( array $webfonts_by_provider ) { + $styles = ''; + $providers = $this->get_providers(); + + /* + * Loop through each of the providers to get the CSS for their respective webfonts + * to incrementally generate the collective styles for all of them. + */ + foreach ( $providers as $provider_id => $provider_class ) { + + // Bail out if the provider class does not exist. + if ( ! class_exists( $provider_class ) ) { + /* translators: %s is the provider name. */ + trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider_id ) ); + continue; + } + + $provider_webfonts = isset( $webfonts_by_provider[ $provider_id ] ) + ? $webfonts_by_provider[ $provider_id ] + : array(); + + // If there are no registered webfonts for this provider, skip it. + if ( empty( $provider_webfonts ) ) { + continue; + } + + /* + * Process the webfonts by first passing them to the provider via `set_webfonts()` + * and then getting the CSS from the provider. + */ + $provider = new $provider_class(); + $provider->set_webfonts( $provider_webfonts ); + $styles .= $provider->get_css(); + } + + return $styles; + } + + + /** + * Reorganizes webfonts grouped by font-family into grouped by provider. + * + * @param array[] $font_families Font families and each of their webfonts. + * @return array[] Webfonts organized by providers. + */ + private function get_webfonts_by_provider( array $font_families ) { + $providers = $this->get_providers(); + $webfonts_by_provider = array(); + + foreach ( $font_families as $webfonts ) { + foreach ( $webfonts as $webfont ) { + $provider = $webfont['provider']; + + // Skip if the provider is not registered. + if ( ! isset( $providers[ $provider ] ) ) { + /* translators: %s is the provider name. */ + trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider ) ); + continue; + } + + // Initialize a new provider collection. + if ( ! isset( $webfonts_by_provider[ $provider ] ) ) { + $webfonts_by_provider[ $provider ] = array(); + } + $webfonts_by_provider[ $provider ][] = $webfont; + } + } + + return $webfonts_by_provider; + } +} diff --git a/lib/experimental/class-wp-webfonts.php b/lib/experimental/class-wp-webfonts.php index e422f51658befb..4341d341f3294d 100644 --- a/lib/experimental/class-wp-webfonts.php +++ b/lib/experimental/class-wp-webfonts.php @@ -1,38 +1,6 @@ register_provider( 'local', 'WP_Webfonts_Provider_Local' ); - - // Register callback to generate and enqueue styles. - if ( did_action( 'wp_enqueue_scripts' ) ) { - $this->stylesheet_handle = 'webfonts-footer'; - $hook = 'wp_print_footer_scripts'; - } else { - $this->stylesheet_handle = 'webfonts'; - $hook = 'wp_enqueue_scripts'; - } - add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); - - // Enqueue webfonts in the block editor. - add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); + public function __construct() { + /** + * Fires when the WP_Webfonts instance is initialized. + * + * @since X.X.X + * + * @param WP_Webfonts $wp_webfonts WP_Webfonts instance (passed by reference). + */ + do_action_ref_array( 'wp_default_webfonts', array( &$this ) ); } /** - * Get the list of registered fonts. + * Get the list of all registered web fonts and variations. * * @since 6.0.0 * - * @return array[] + * @return strings[] */ - public function get_registered_webfonts() { - return $this->registered_webfonts; + public function get_registered() { + return array_keys( $this->registered ); } /** - * Get the list of enqueued fonts. + * Get the list of enqueued web fonts and variations. * * @return array[] */ - public function get_enqueued_webfonts() { - return $this->enqueued_webfonts; + public function get_enqueued() { + return $this->queue; } /** - * Get the list of all fonts. + * Gets a list of all variations registered for a font family. * - * @return array[] + * @param $font_family + * @return array */ - public function get_all_webfonts() { - return array_merge( $this->get_registered_webfonts(), $this->get_enqueued_webfonts() ); + public function get_variations( $font_family ) { + $font_family_handle = sanitize_title( $font_family ); + + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return array(); + } + + return $this->registered[ $font_family_handle ]->deps; } /** - * Get the list of providers. - * - * @since 6.0.0 + * Removes a font family and all registered variations. * - * @return WP_Webfonts_Provider[] All registered providers, each keyed by their unique ID. + * @param mixed|string|string[] $font_family */ - public function get_providers() { - return $this->providers; + function remove_family( $font_family ) { + $font_family_handle = sanitize_title( $font_family ); + + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return; + } + + $variations = $this->registered[ $font_family_handle ]->deps; + + foreach ( $variations as $variation ) { + $this->remove( $variation ); + } + + $this->remove( $font_family_handle ); } /** - * Register a webfont. + * Removes a variation. * - * @since 6.0.0 - * - * @param array $webfont Webfont to be registered. - * @return string|false The font family slug if successfully registered, else false. + * @param $font_family + * @param $variation_handle */ - public function register_webfont( array $webfont ) { - $webfont = $this->validate_webfont( $webfont ); - - // If not valid, bail out. - if ( ! $webfont ) { - return false; - } + function remove_variation( $font_family, $variation_handle ) { + $font_family_handle = sanitize_title( $font_family ); - $slug = $this->get_font_slug( $webfont ); + $this->remove( $variation_handle ); - // Initialize a new font-family collection. - if ( ! isset( $this->registered_webfonts[ $slug ] ) ) { - $this->registered_webfonts[ $slug ] = array(); + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return; } - $this->registered_webfonts[ $slug ][] = $webfont; - return $slug; + // Remove the variation as a dependency. + $this->registered[ $font_family_handle ]->deps = array_values( array_diff( + $this->registered[ $font_family_handle ]->deps, + array( $variation_handle ) + ) ); } /** - * Enqueue a font-family that has been already registered. + * Registers a variation for a font family. * - * @param string $font_family_name The font family name to be enqueued. - * @return bool True if successfully enqueued, else false. + * @param string $font_family + * @param string $variation_handle + * @param array $variation */ - public function enqueue_webfont( $font_family_name ) { - $slug = $this->get_font_slug( $font_family_name ); + public function add_variation( $font_family, $variation_handle, $variation ) { + $font_family_handle = sanitize_title( $font_family ); - if ( isset( $this->enqueued_webfonts[ $slug ] ) ) { - return true; + // Register the font family when it does not yet exist. + if ( ! isset( $this->registered[ $font_family ] ) ) { + $this->add( $font_family_handle, false ); } - if ( ! isset( $this->registered_webfonts[ $slug ] ) ) { - /* translators: %s unique slug to identify the font family of the webfont */ - _doing_it_wrong( __METHOD__, sprintf( __( 'The "%s" font family is not registered.', 'gutenberg' ), $slug ), '6.0.0' ); + $variation = $this->validate_variation( $font_family, $variation ); + // Variation validation failed. + if ( ! $variation ) { return false; } - $this->enqueued_webfonts[ $slug ] = $this->registered_webfonts[ $slug ]; - unset( $this->registered_webfonts[ $slug ] ); - return true; + $variation_handle = $font_family_handle . '-' . $variation_handle; + + if ( $variation['src'] ) { + $result = $this->add( $variation_handle, $variation['src'], array(), false, array( 'font-properties' => $variation ) ); + } else { + $result = $this->add( $variation_handle, false, array(), false, array( 'font-properties' => $variation ) ); + } + + if ( $result ) { + $this->providers[ $variation['provider'] ]['fonts'][] = $variation_handle; + } + + return $result; } /** - * Get the font slug. - * - * @since 6.0.0 + * Adds a variation as a dependency for the main font alias. * - * @param array|string $to_convert The value to convert into a slug. Expected as the web font's array - * or a font-family as a string. - * @return string|false The font slug on success, or false if the font-family cannot be determined. + * @param $font_family_handle + * @param $variation_handle */ - public static function get_font_slug( $to_convert ) { - if ( is_array( $to_convert ) ) { - if ( isset( $to_convert['font-family'] ) ) { - $to_convert = $to_convert['font-family']; - } elseif ( isset( $to_convert['fontFamily'] ) ) { - $to_convert = $to_convert['fontFamily']; - } else { - _doing_it_wrong( __METHOD__, __( 'Could not determine the font family name.', 'gutenberg' ), '6.0.0' ); - return false; - } - } - - return sanitize_title( $to_convert ); + public function add_dependency( $font_family_handle, $variation_handle ) { + $this->registered[ $font_family_handle ]->deps[] = $variation_handle; } /** - * Validate a webfont. + * Validates and sanitizes a variation. * - * @since 6.0.0 - * - * @param array $webfont The webfont arguments. - * - * @return array|false The validated webfont arguments, or false if the webfont is invalid. + * @param $font_family + * @param $variation + * @return array|false|object */ - public function validate_webfont( $webfont ) { - $webfont = wp_parse_args( - $webfont, - array( - 'provider' => 'local', - 'font-family' => '', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-display' => 'fallback', - ) + function validate_variation( $font_family, $variation ) { + $defaults = array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', ); - // Check the font-family. - if ( empty( $webfont['font-family'] ) || ! is_string( $webfont['font-family'] ) ) { - trigger_error( __( 'Webfont font family must be a non-empty string.', 'gutenberg' ) ); - return false; - } + $defaults = apply_filters( 'wp_web_font_variation_defaults', $defaults ); + + $defaults['font-family'] = $font_family; + $variation = wp_parse_args( $variation, $defaults ); // Local fonts need a "src". - if ( 'local' === $webfont['provider'] ) { + if ( 'local' === $variation['provider'] ) { // Make sure that local fonts have 'src' defined. - if ( empty( $webfont['src'] ) || ( ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) ) ) { + if ( empty( $variation['src'] ) || ( ! is_string( $variation['src'] ) && ! is_array( $variation['src'] ) ) ) { trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.', 'gutenberg' ) ); return false; } + } elseif ( ! class_exists( $variation['provider'] ) ) { + trigger_error( __( 'The provider class specified does not exist.', 'gutenberg' ) ); + return false; } // Validate the 'src' property. - if ( ! empty( $webfont['src'] ) ) { - foreach ( (array) $webfont['src'] as $src ) { + if ( ! empty( $variation['src'] ) ) { + foreach ( (array) $variation['src'] as $src ) { if ( empty( $src ) || ! is_string( $src ) ) { trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) ); return false; @@ -238,14 +194,14 @@ public function validate_webfont( $webfont ) { } // Check the font-weight. - if ( ! is_string( $webfont['font-weight'] ) && ! is_int( $webfont['font-weight'] ) ) { + if ( ! is_string( $variation['font-weight'] ) && ! is_int( $variation['font-weight'] ) ) { trigger_error( __( 'Webfont font weight must be a properly formatted string or integer.', 'gutenberg' ) ); return false; } // Check the font-display. - if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { - $webfont['font-display'] = 'fallback'; + if ( ! in_array( $variation['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { + $variation['font-display'] = 'fallback'; } $valid_props = array( @@ -268,72 +224,13 @@ public function validate_webfont( $webfont ) { 'provider', ); - foreach ( $webfont as $prop => $value ) { + foreach ( $variation as $prop => $value ) { if ( ! in_array( $prop, $valid_props, true ) ) { - unset( $webfont[ $prop ] ); + unset( $variation[ $prop ] ); } } - return $webfont; - } - - /** - * Register a provider. - * - * @since 6.0.0 - * - * @param string $provider The provider name. - * @param string $class The provider class name. - * @return bool True if successfully registered, else false. - */ - public function register_provider( $provider, $class ) { - if ( empty( $provider ) || empty( $class ) ) { - return false; - } - $this->providers[ $provider ] = $class; - return true; - } - - /** - * Generate and enqueue webfonts styles. - * - * @since 6.0.0 - */ - public function generate_and_enqueue_styles() { - // Generate the styles. - $webfonts = $this->get_webfonts_by_provider( $this->get_enqueued_webfonts() ); - $styles = $this->generate_styles( $webfonts ); - - // Bail out if there are no styles to enqueue. - if ( '' === $styles ) { - return; - } - - // Enqueue the stylesheet. - wp_register_style( $this->stylesheet_handle, '' ); - wp_enqueue_style( $this->stylesheet_handle ); - - // Add the styles to the stylesheet. - wp_add_inline_style( $this->stylesheet_handle, $styles ); - } - - /** - * Generate and enqueue editor styles. - * - * @since 6.0.0 - */ - public function generate_and_enqueue_editor_styles() { - // Generate the styles. - $webfonts = $this->get_webfonts_by_provider( $this->get_all_webfonts() ); - $styles = $this->generate_styles( $webfonts ); - - // Bail out if there are no styles to enqueue. - if ( '' === $styles ) { - return; - } - - wp_enqueue_style( 'wp-block-library' ); - wp_add_inline_style( 'wp-block-library', $styles ); + return $variation; } /** @@ -344,74 +241,101 @@ public function generate_and_enqueue_editor_styles() { * @param array[] $webfonts_by_provider Webfonts organized by provider. * @return string $styles Generated styles. */ - private function generate_styles( array $webfonts_by_provider ) { + public function do_item( $handle, $group = false ) { + if ( ! parent::do_item( $handle ) ) { + return false; + } + $styles = ''; $providers = $this->get_providers(); + $obj = $this->registered[ $handle ]; + /* * Loop through each of the providers to get the CSS for their respective webfonts * to incrementally generate the collective styles for all of them. */ - foreach ( $providers as $provider_id => $provider_class ) { - + foreach ( $providers as $provider_id => $provider ) { // Bail out if the provider class does not exist. - if ( ! class_exists( $provider_class ) ) { + if ( ! class_exists( $provider['class'] ) ) { /* translators: %s is the provider name. */ trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider_id ) ); continue; } - $provider_webfonts = isset( $webfonts_by_provider[ $provider_id ] ) - ? $webfonts_by_provider[ $provider_id ] - : array(); + $fonts = $this->get_enqueued_fonts_for_provider( $provider_id ); // If there are no registered webfonts for this provider, skip it. - if ( empty( $provider_webfonts ) ) { + if ( empty( $fonts ) ) { continue; } + $provider_fonts = array(); + + foreach ( $fonts as $font_handle ) { + $provider_fonts[ $font_handle ] = $this->get_data( $font_handle, 'font-properties' ); + } + /* * Process the webfonts by first passing them to the provider via `set_webfonts()` * and then getting the CSS from the provider. */ - $provider = new $provider_class(); - $provider->set_webfonts( $provider_webfonts ); + $provider = new $provider['class'](); + $provider->set_webfonts( $provider_fonts ); $styles .= $provider->get_css(); } return $styles; } + /** + * Register a provider. + * + * @since 6.0.0 + * + * @param string $provider The provider name. + * @param string $class The provider class name. + * @return bool True if successfully registered, else false. + */ + public function register_provider( $provider, $class ) { + if ( empty( $provider ) || empty( $class ) || ! class_exists( $class ) ) { + return false; + } + + $this->providers[ $provider ] = array( + 'class' => $class, + 'fonts' => array(), + ); + return true; + } /** - * Reorganizes webfonts grouped by font-family into grouped by provider. + * Get the list of providers. + * + * @since 6.0.0 + * + * @return WP_Webfonts_Provider[] All registered providers, each keyed by their unique ID. + */ + public function get_providers() { + return $this->providers; + } + + /** + * Retrieves a list of enqueued web font variations for a provider. * - * @param array[] $font_families Font families and each of their webfonts. * @return array[] Webfonts organized by providers. */ - private function get_webfonts_by_provider( array $font_families ) { - $providers = $this->get_providers(); - $webfonts_by_provider = array(); - - foreach ( $font_families as $webfonts ) { - foreach ( $webfonts as $webfont ) { - $provider = $webfont['provider']; - - // Skip if the provider is not registered. - if ( ! isset( $providers[ $provider ] ) ) { - /* translators: %s is the provider name. */ - trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider ) ); - continue; - } + private function get_enqueued_fonts_for_provider( $provider ) { + $providers = $this->get_providers(); - // Initialize a new provider collection. - if ( ! isset( $webfonts_by_provider[ $provider ] ) ) { - $webfonts_by_provider[ $provider ] = array(); - } - $webfonts_by_provider[ $provider ][] = $webfont; - } + if ( empty( $providers[ $provider ] ) ) { + return array(); } - return $webfonts_by_provider; + return array_intersect( + $providers[ $provider ]['fonts'], + $this->get_enqueued() + ); } + } diff --git a/lib/experimental/register-webfonts-from-theme-json.php b/lib/experimental/register-webfonts-from-theme-json.php index 576d74ab0c56b0..358e18a3881e4c 100644 --- a/lib/experimental/register-webfonts-from-theme-json.php +++ b/lib/experimental/register-webfonts-from-theme-json.php @@ -5,173 +5,180 @@ * @package gutenberg */ -/** - * Register webfonts defined in theme.json. - */ -function gutenberg_register_webfonts_from_theme_json() { - // Get settings. - $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_settings(); - - // If in the editor, add webfonts defined in variations. - if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { - $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); - - foreach ( $variations as $variation ) { - - // Sanity check: Skip if fontFamilies are not defined in the variation. - if ( - empty( $variation['settings'] ) || - empty( $variation['settings']['typography'] ) || - empty( $variation['settings']['typography']['fontFamilies'] ) - ) { - continue; - } +if ( ! function_exists( 'gutenberg_register_webfonts_from_theme_json' ) ) { + /** + * Register webfonts defined in theme.json. + */ + function gutenberg_register_webfonts_from_theme_json() { + // Get settings. + $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_settings(); - // Merge the variation settings with the global settings. - $settings['typography'] = empty( $settings['typography'] ) ? array() : $settings['typography']; - $settings['typography']['fontFamilies'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']; - $settings['typography']['fontFamilies']['theme'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']['theme']; - $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] ); + // If in the editor, add webfonts defined in variations. + if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); - // Make sure there are no duplicates. - $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] ); - } - } + foreach ( $variations as $variation ) { - // Bail out early if there are no settings for webfonts. - if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { - return; - } - - $webfonts = array(); + // Sanity check: Skip if fontFamilies are not defined in the variation. + if ( empty( $variation['settings'] ) || empty( $variation['settings']['typography'] ) || empty( $variation['settings']['typography']['fontFamilies'] ) ) { + continue; + } - // Look for fontFamilies. - foreach ( $settings['typography']['fontFamilies'] as $font_families ) { - foreach ( $font_families as $font_family ) { + // Merge the variation settings with the global settings. + $settings['typography'] = empty( $settings['typography'] ) ? array() : $settings['typography']; + $settings['typography']['fontFamilies'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']; + $settings['typography']['fontFamilies']['theme'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']['theme']; + $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] ); - // Skip if fontFace is not defined. - if ( empty( $font_family['fontFace'] ) ) { - continue; + // Make sure there are no duplicates. + $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] ); } + } + + // Bail out early if there are no settings for webfonts. + if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { + return; + } + + $webfonts = array(); - $font_family['fontFace'] = (array) $font_family['fontFace']; + // Look for fontFamilies. + foreach ( $settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $font_family ) { - foreach ( $font_family['fontFace'] as $font_face ) { - // Skip if the webfont was registered through the Webfonts API. - if ( isset( $font_face['origin'] ) && 'gutenberg_wp_webfonts_api' === $font_face['origin'] ) { + // Skip if fontFace is not defined. + if ( empty( $font_family['fontFace'] ) ) { continue; } - // Check if webfonts have a "src" param, and if they do account for the use of "file:./". - if ( ! empty( $font_face['src'] ) ) { - $font_face['src'] = (array) $font_face['src']; + $font_family['fontFace'] = (array) $font_family['fontFace']; + + foreach ( $font_family['fontFace'] as $font_face ) { + // Skip if the webfont was registered through the Webfonts API. + if ( isset( $font_face['origin'] ) && 'gutenberg_wp_webfonts_api' === $font_face['origin'] ) { + continue; + } - foreach ( $font_face['src'] as $src_key => $url ) { - // Tweak the URL to be relative to the theme root. - if ( ! str_starts_with( $url, 'file:./' ) ) { - continue; + // Check if webfonts have a "src" param, and if they do account for the use of "file:./". + if ( ! empty( $font_face['src'] ) ) { + $font_face['src'] = (array) $font_face['src']; + + foreach ( $font_face['src'] as $src_key => $url ) { + // Tweak the URL to be relative to the theme root. + if ( ! str_starts_with( $url, 'file:./' ) ) { + continue; + } + $font_face['src'][ $src_key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); } - $font_face['src'][ $src_key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); } - } - // Convert keys to kebab-case. - foreach ( $font_face as $property => $value ) { - $kebab_case = _wp_to_kebab_case( $property ); - $font_face[ $kebab_case ] = $value; - if ( $kebab_case !== $property ) { - unset( $font_face[ $property ] ); + // Convert keys to kebab-case. + foreach ( $font_face as $property => $value ) { + $kebab_case = _wp_to_kebab_case( $property ); + $font_face[ $kebab_case ] = $value; + if ( $kebab_case !== $property ) { + unset( $font_face[ $property ] ); + } } - } - $webfonts[] = $font_face; + $webfonts[] = $font_face; + } } } - } - foreach ( $webfonts as $webfont ) { - wp_webfonts()->register_webfont( $webfont ); - } - foreach ( $webfonts as $webfont ) { - wp_webfonts()->enqueue_webfont( $webfont['font-family'] ); - } -} -/** - * Add missing fonts data to the global styles. - * - * @param array $data The global styles. - * @return array The global styles with missing fonts data. - */ -function gutenberg_add_registered_webfonts_to_theme_json( $data ) { - $font_families_registered = wp_webfonts()->get_all_webfonts(); - $font_families_from_theme = array(); - if ( ! empty( $data['settings'] ) && ! empty( $data['settings']['typography'] ) && ! empty( $data['settings']['typography']['fontFamilies'] ) ) { - $font_families_from_theme = $data['settings']['typography']['fontFamilies']; + $to_enqueue = array(); + + foreach ( $webfonts as $webfont ) { + $font_family_handle = sanitize_title( $webfont['font-family'] ); + + wp_register_web_font_family( $font_family_handle ); + + $variation_handle = sanitize_title( implode( ' ', array( $webfont['font-weight'], $webfont['font-style'] ) ) ); + wp_register_web_font_variation( $font_family_handle, $variation_handle, $webfont ); + $to_enqueue[] = $font_family_handle; + } + + foreach ( $to_enqueue as $font_family ) { + wp_webfonts()->enqueue( $font_family ); + } } +} +if ( ! function_exists( 'gutenberg_add_registered_webfonts_to_theme_json' ) ) { /** - * Helper to get an array of the font-families. + * Add missing fonts data to the global styles. * - * @param array $families_data The font-families data. - * @return array The font-families array. + * @param array $data The global styles. + * @return array The global styles with missing fonts data. */ - $get_families = static function( $families_data ) { - $families = array(); - foreach ( $families_data as $family ) { - $families[] = WP_Webfonts::get_font_slug( $family ); + function gutenberg_add_registered_webfonts_to_theme_json( $data ) { + $font_families_registered = wp_webfonts()->get_all_webfonts(); + $font_families_from_theme = array(); + if ( ! empty( $data['settings'] ) && ! empty( $data['settings']['typography'] ) && ! empty( $data['settings']['typography']['fontFamilies'] ) ) { + $font_families_from_theme = $data['settings']['typography']['fontFamilies']; } - // Micro-optimization: Use array_flip( array_flip( $array ) ) - // instead of array_unique( $array ) because it's faster. - // The result is the same. - return array_flip( array_flip( $families ) ); - }; + /** + * Helper to get an array of the font-families. + * + * @param array $families_data The font-families data. + * @return array The font-families array. + */ + $get_families = static function ( $families_data ) { + $families = array(); + foreach ( $families_data as $family ) { + $families[] = WP_Webfonts::get_font_slug( $family ); + } - // Diff the arrays to find the missing fonts. - $to_add = array_diff( - array_keys( $font_families_registered ), - $get_families( $font_families_from_theme ) - ); + // Micro-optimization: Use array_flip( array_flip( $array ) ) + // instead of array_unique( $array ) because it's faster. + // The result is the same. + return array_flip( array_flip( $families ) ); + }; - // Bail out early if there are no missing fonts. - if ( empty( $to_add ) ) { - return $data; - } + // Diff the arrays to find the missing fonts. + $to_add = array_diff( array_keys( $font_families_registered ), $get_families( $font_families_from_theme ) ); - // Make sure the path to settings.typography.fontFamilies.theme exists - // before adding missing fonts. - if ( empty( $data['settings'] ) ) { - $data['settings'] = array(); - } - if ( empty( $data['settings']['typography'] ) ) { - $data['settings']['typography'] = array(); - } - if ( empty( $data['settings']['typography']['fontFamilies'] ) ) { - $data['settings']['typography']['fontFamilies'] = array(); - } + // 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( $data['settings'] ) ) { + $data['settings'] = array(); + } + if ( empty( $data['settings']['typography'] ) ) { + $data['settings']['typography'] = array(); + } + if ( empty( $data['settings']['typography']['fontFamilies'] ) ) { + $data['settings']['typography']['fontFamilies'] = array(); + } - foreach ( $to_add as $slug ) { - $font_faces_for_family = $font_families_registered[ $slug ]; - $family_name = $font_faces_for_family[0]['font-family']; - $font_faces = array(); + foreach ( $to_add as $slug ) { + $font_faces_for_family = $font_families_registered[ $slug ]; + $family_name = $font_faces_for_family[0]['font-family']; + $font_faces = array(); - foreach ( $font_faces_for_family as $font_face ) { - $camel_cased = array( 'origin' => 'gutenberg_wp_webfonts_api' ); - foreach ( $font_face as $key => $value ) { - $camel_cased[ lcfirst( str_replace( '-', '', ucwords( $key, '-' ) ) ) ] = $value; + foreach ( $font_faces_for_family as $font_face ) { + $camel_cased = array( 'origin' => 'gutenberg_wp_webfonts_api' ); + foreach ( $font_face as $key => $value ) { + $camel_cased[ lcfirst( str_replace( '-', '', ucwords( $key, '-' ) ) ) ] = $value; + } + $font_faces[] = $camel_cased; } - $font_faces[] = $camel_cased; + + $data['settings']['typography']['fontFamilies'][] = array( + 'fontFamily' => str_contains( $family_name, ' ' ) ? "'{$family_name}'" : $family_name, + 'name' => $family_name, + 'slug' => $slug, + 'fontFace' => $font_faces, + ); } - $data['settings']['typography']['fontFamilies'][] = array( - 'fontFamily' => str_contains( $family_name, ' ' ) ? "'{$family_name}'" : $family_name, - 'name' => $family_name, - 'slug' => $slug, - 'fontFace' => $font_faces, - ); + return $data; } - - return $data; } add_action( 'init', 'gutenberg_register_webfonts_from_theme_json' ); diff --git a/lib/experimental/webfonts.php b/lib/experimental/webfonts.php index 201101d1d7093e..0bbf0b8f405091 100644 --- a/lib/experimental/webfonts.php +++ b/lib/experimental/webfonts.php @@ -9,152 +9,116 @@ if ( ! function_exists( 'wp_webfonts' ) ) { /** - * Instantiates the webfonts controller, if not already set, and returns it. + * Initialize $wp_webfonts if it has not been set. * - * @since 6.0.0 + * @since X.X.X + * + * @global WP_Webfonts $wp_webfonts * - * @return WP_Webfonts Instance of the controller. + * @return WP_Webfonts WP_Webfonts instance. */ function wp_webfonts() { global $wp_webfonts; - if ( ! $wp_webfonts instanceof WP_Webfonts ) { + if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { $wp_webfonts = new WP_Webfonts(); - $wp_webfonts->init(); } return $wp_webfonts; } } -if ( ! function_exists( 'wp_register_webfonts' ) ) { +if ( ! function_exists( 'wp_register_web_font_family' ) ) { /** - * Registers a collection of webfonts. - * - * Example of how to register Source Serif Pro font with font-weight range of 200-900 - * and font-style of normal and italic: - * - * If the font files are contained within the theme: - * - * wp_register_webfonts( - * array( - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'normal', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), - * ), - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'italic', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), - * ), - * ) - * ); - * - * - * Webfonts should be registered in the `after_setup_theme` hook. + * Registers a font family as an alias. * - * @since 6.0.0 + * @param $font_family + * @return array|bool + */ + function wp_register_web_font_family( $font_family ) { + $wp_webfonts = wp_webfonts(); + + return $wp_webfonts->add( sanitize_title( $font_family ), false ); + } +} + +if ( ! function_exists( 'wp_register_web_font_variation' ) ) { + /** + * Registers a font variation. + * + * @param $font_family + * @return mixed + */ + function wp_register_web_font_variation( $font_family, $variation_handle, $variation ) { + return wp_webfonts()->add_variation( $font_family, $variation_handle, $variation ); + } +} + +if ( ! function_exists( 'wp_register_webfonts' ) ) { + /** + * Registers a list of web fonts and variations. * - * @param array[] $webfonts Webfonts to be registered. - * This contains an array of webfonts to be registered. - * Each webfont is an array. - * @return string[] The font family slug of the registered webfonts. + * @param $webfonts + * @return array */ - function wp_register_webfonts( array $webfonts ) { - $registered_webfont_slugs = array(); + function wp_register_webfonts( $webfonts, $enqueue = false ) { + $registered = array(); + + foreach ( $webfonts as $font_family => $variations ) { + wp_register_web_font_family( $font_family ); - foreach ( $webfonts as $webfont ) { - $slug = wp_register_webfont( $webfont ); + foreach ( $variations as $variation_handle => $variation ) { + $registered[] = wp_register_web_font_variation( $font_family, $variation_handle, $variation ); - if ( is_string( $slug ) ) { - $registered_webfont_slugs[ $slug ] = true; + if ( $enqueue ) { + wp_enqueue_web_font( $variation_handle ); + } } } - return array_keys( $registered_webfont_slugs ); + return $registered; } } -if ( ! function_exists( 'wp_register_webfont' ) ) { +if ( ! function_exists( 'wp_enqueue_web_font' ) ) { /** - * Registers a single webfont. - * - * Example of how to register Source Serif Pro font with font-weight range of 200-900: - * - * If the font file is contained within the theme: - * - * - * wp_register_webfont( - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'normal', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), - * ) - * ); - * - * - * @since 6.0.0 - * - * @param array $webfont Webfont to be registered. - * @return string|false The font family slug if successfully registered, else false. + * Enqueues a web font family and all variations. */ - function wp_register_webfont( array $webfont ) { - return wp_webfonts()->register_webfont( $webfont ); + function wp_enqueue_web_font( $handle ) { + $wp_webfonts = wp_webfonts(); + $wp_webfonts->enqueue( $handle ); } } -if ( ! function_exists( 'wp_enqueue_webfonts' ) ) { +if ( ! function_exists( 'wp_enqueue_web_font_variations' ) ) { /** - * Enqueues a collection of font families. - * - * Example of how to enqueue Source Serif Pro and Roboto font families, both registered beforehand. - * - * - * wp_enqueue_webfonts( - * 'Roboto', - * 'Sans Serif Pro' - * ); - * - * - * Font families should be enqueued from the `init` hook or later. - * - * @since 6.0.0 - * - * @param string[] $webfonts Font families to be enqueued. + * Enqueues a specific set of web font variations. */ - function wp_enqueue_webfonts( array $webfonts ) { - foreach ( $webfonts as $webfont ) { - wp_enqueue_webfont( $webfont ); + function wp_enqueue_web_font_variations( $variations ) { + $wp_webfonts = wp_webfonts(); + + // Looking to enqueue all variations of a font family. + foreach ( $variations as $variation ) { + $wp_webfonts->enqueue( $variation ); } } } -if ( ! function_exists( 'wp_enqueue_webfont' ) ) { +if ( ! function_exists( 'wp_deregister_web_font_family' ) ) { /** - * Enqueue a single font family that has been registered beforehand. - * - * Example of how to enqueue Source Serif Pro font: - * - * - * wp_enqueue_webfont( 'Source Serif Pro' ); - * - * - * Font families should be enqueued from the `init` hook or later. - * - * @since 6.0.0 - * - * @param string $font_family_name The font family name to be enqueued. - * @return bool True if successfully enqueued, else false. + * Unregisters an entire font family and all variations. + */ + function wp_deregister_web_font_family( $font_family ) { + wp_webfonts()->remove_family( $font_family ); + } +} + +if ( ! function_exists( 'wp_deregister_web_font_variation' ) ) { + /** + * @param $variation_handle */ - function wp_enqueue_webfont( $font_family_name ) { - return wp_webfonts()->enqueue_webfont( $font_family_name ); + function wp_deregister_web_font_variation( $font_family, $variation_handle ) { + wp_webfonts()->remove_variation( $font_family, $variation_handle ); } } @@ -211,6 +175,42 @@ function wp_get_webfont_providers() { } } +if ( ! function_exists( 'wp_print_webfonts' ) ) { + function wp_print_webfonts( $handles = false ) { + global $wp_webfonts; + + /** + * Fires before webfonts in the $handles queue are printed. + * + * @since X.X.X + */ + do_action( 'wp_print_webfonts' ); + + if ( '' === $handles ) { // For 'wp_head'. + $handles = false; + } + + _wp_scripts_maybe_doing_it_wrong( __FUNCTION__ ); + + if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { + if ( ! $handles ) { + return array(); // No need to instantiate if nothing is there. + } + } + + return wp_webfonts()->do_items( $handles ); + } +} + + + + + + + + + + /** * Add webfonts mime types. */