From f109cb7b29cdc1bf044aac3e28d3ad19d9eea33d Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Wed, 20 Apr 2022 17:21:57 -0500 Subject: [PATCH 01/14] Adds stopgap handler for theme.json webfonts. --- .../class-theme-json-webfonts-handler.php | 537 ++++++++++++++++++ lib/load.php | 8 +- .../data/themedir1/webfonts-theme/style.css | 7 + .../data/themedir1/webfonts-theme/theme.json | 373 ++++++++++++ phpunit/theme-json-webfonts-handler-test.php | 117 ++++ 5 files changed, 1041 insertions(+), 1 deletion(-) create mode 100644 lib/experimental/class-theme-json-webfonts-handler.php create mode 100644 phpunit/data/themedir1/webfonts-theme/style.css create mode 100644 phpunit/data/themedir1/webfonts-theme/theme.json create mode 100644 phpunit/theme-json-webfonts-handler-test.php diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php new file mode 100644 index 00000000000000..8234ead94a7517 --- /dev/null +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -0,0 +1,537 @@ +stylesheet_handle = 'wp-webfonts-footer'; + $hook = 'wp_print_footer_scripts'; + } else { + $this->stylesheet_handle = 'wp-webfonts'; + $hook = 'wp_enqueue_scripts'; + } + add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); + add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); + + add_filter( 'mime_types', array( $this, 'add_mime_types' ) ); + } + + /** + * Register webfonts within the `theme.json`. + * + * @since 6.0.0 + */ + public function register_webfonts() { + $this->webfonts = array(); + $webfonts = $this->get_webfonts_from_theme_json(); + + foreach ( $webfonts as $webfont ) { + if ( ! is_array( $webfont ) ) { + continue; + } + + $webfont = $this->convert_keys_to_kebab_case( $webfont ); + + $webfont = $this->validate_webfont( $webfont ); + + $webfont['src'] = $this->transform_src_into_uri( (array) $webfont['src'] ); + + // Skip if not valid. + if ( empty( $webfont ) ) { + continue; + } + + $this->webfonts[] = $webfont; + } + } + + /** + * Gets the webfonts from `theme.json`. + * + * @since 6.0.0 + * + * @return array Array of defined webfonts. + */ + private function get_webfonts_from_theme_json() { + // Get settings from theme.json. + $theme_settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); + + // Bail out early if there are no settings for webfonts. + if ( empty( $theme_settings['typography'] ) || empty( $theme_settings['typography']['fontFamilies'] ) ) { + return array(); + } + + $webfonts = array(); + + // Look for fontFamilies. + foreach ( $theme_settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $font_family ) { + + // Skip if fontFace is not defined. + if ( empty( $font_family['fontFace'] ) ) { + continue; + } + + // Skip if fontFace is not an array of webfonts. + if ( ! is_array( $font_family['fontFace'] ) ) { + continue; + } + + $webfonts = array_merge( $webfonts, $font_family['fontFace'] ); + } + } + + return $webfonts; + } + + /** + * Transform each `src` into an URI by replacing the `file:./` + * placeholder from theme.json. + * + * The absolute path to the webfont file(s) Cannot be defined in + * theme.json. `file:./` is the placeholder which is replaced by + * the theme's URL path to the theme's root. + * + * @since 6.0.0 + * + * @param array $src Webfont file(s) `src`. + * + * @return array Webfont's `src` in URI. + */ + private function transform_src_into_uri( array $src ) { + foreach ( $src as $key => $url ) { + // Tweak the URL to be relative to the theme root. + if ( ! str_starts_with( $url, 'file:./' ) ) { + continue; + } + + $src[ $key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); + } + + return $src; + } + + /** + * Converts the face-face properties (i.e. keys) into kebab-case. + * + * @since 6.0.0 + * + * @param array $font_face Font face to convert. + * + * @return array Font faces with each property in kebab-case format. + */ + private function convert_keys_to_kebab_case( array $font_face ) { + 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 ] ); + } + } + + return $font_face; + } + + /** + * 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( + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + 'src' => array(), + ) + ); + + // 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; + } + + // Make a `src` property is 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', + ); + + foreach ( $webfont as $prop => $value ) { + if ( ! in_array( $prop, $valid_props, true ) ) { + unset( $webfont[ $prop ] ); + } + } + + return $webfont; + } + + /** + * Generate and enqueue webfonts styles. + * + * @since 6.0.0 + */ + public function generate_and_enqueue_styles() { + // Generate the styles. + $styles = $this->get_css(); + + // 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. + $styles = $this->get_css(); + + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; + } + + wp_add_inline_style( 'wp-block-library', $styles ); + } + + /** + * Gets the `@font-face` CSS styles for locally-hosted font files. + * + * @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 Ordered `src` items. + */ + private function order_src( array $webfont ) { + $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 ( + str_contains( $webfont['font-family'], ' ' ) && + ! str_contains( $webfont['font-family'], '"' ) && + ! str_contains( $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; + } + + /** + * Add webfonts mime types. + * + * @since 6.0.0 + * + * @param array $mime_types Array of mime types. + * @return array Mime types with webfonts formats. + */ + public function add_mime_types( $mime_types ) { + // Webfonts formats. + $mime_types['woff2'] = 'font/woff2'; + $mime_types['woff'] = 'font/woff'; + $mime_types['ttf'] = 'font/ttf'; + $mime_types['eot'] = 'application/vnd.ms-fontobject'; + $mime_types['otf'] = 'application/x-font-opentype'; + + return $mime_types; + } + } + + // Run the handler. + $handler = new Theme_JSON_Webfonts_Handler(); + $handler->init(); + } +} diff --git a/lib/load.php b/lib/load.php index 5d9969c409ffa1..06f993c27300ab 100644 --- a/lib/load.php +++ b/lib/load.php @@ -114,12 +114,18 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.0/block-patterns.php'; require __DIR__ . '/compat/wordpress-6.0/block-template.php'; require __DIR__ . '/compat/wordpress-6.0/edit-form-blocks.php'; -require __DIR__ . '/experimental/register-webfonts-from-theme-json.php'; +require __DIR__ . '/experimental/class-theme-json-webfonts-handler.php'; require __DIR__ . '/experimental/class-wp-theme-json-resolver-gutenberg.php'; + +/* + * DO NOT MERGE THIS CHANGE. + * Don't load the Webfonts API. +require __DIR__ . '/experimental/register-webfonts-from-theme-json.php'; require __DIR__ . '/experimental/class-wp-webfonts.php'; require __DIR__ . '/experimental/class-wp-webfonts-provider.php'; require __DIR__ . '/experimental/class-wp-webfonts-provider-local.php'; require __DIR__ . '/experimental/webfonts.php'; + */ require __DIR__ . '/experimental/blocks.php'; require __DIR__ . '/blocks.php'; diff --git a/phpunit/data/themedir1/webfonts-theme/style.css b/phpunit/data/themedir1/webfonts-theme/style.css new file mode 100644 index 00000000000000..bee25a0bfa18cb --- /dev/null +++ b/phpunit/data/themedir1/webfonts-theme/style.css @@ -0,0 +1,7 @@ +/* +Theme Name: Webfonts theme. +Theme URI: https://wordpress.org/ +Description: For testing purposes only. +Version: 1.0.0 +Text Domain: webfonts-theme +*/ diff --git a/phpunit/data/themedir1/webfonts-theme/theme.json b/phpunit/data/themedir1/webfonts-theme/theme.json new file mode 100644 index 00000000000000..39291efce4204b --- /dev/null +++ b/phpunit/data/themedir1/webfonts-theme/theme.json @@ -0,0 +1,373 @@ +{ + "version": 2, + "customTemplates": [ + { + "name": "blank", + "title": "Blank", + "postTypes": [ + "page", + "post" + ] + }, + { + "name": "page-large-header", + "title": "Page (Large Header)", + "postTypes": [ + "page" + ] + }, + { + "name": "single-no-separators", + "title": "Single Post (No Separators)", + "postTypes": [ + "post" + ] + }, + { + "name": "page-no-separators", + "title": "Page (No Separators)", + "postTypes": [ + "page" + ] + } + ], + "settings": { + "appearanceTools": true, + "color": { + "duotone": [ + { + "colors": [ "#000000", "#ffffff" ], + "slug": "foreground-and-background", + "name": "Foreground and background" + }, + { + "colors": [ "#000000", "#ffe2c7" ], + "slug": "foreground-and-secondary", + "name": "Foreground and secondary" + }, + { + "colors": [ "#000000", "#f6f6f6" ], + "slug": "foreground-and-tertiary", + "name": "Foreground and tertiary" + }, + { + "colors": [ "#1a4548", "#ffffff" ], + "slug": "primary-and-background", + "name": "Primary and background" + }, + { + "colors": [ "#1a4548", "#ffe2c7" ], + "slug": "primary-and-secondary", + "name": "Primary and secondary" + }, + { + "colors": [ "#1a4548", "#f6f6f6" ], + "slug": "primary-and-tertiary", + "name": "Primary and tertiary" + } + ], + "gradients": [ + { + "slug": "vertical-secondary-to-tertiary", + "gradient": "linear-gradient(to bottom,var(--wp--preset--color--secondary) 0%,var(--wp--preset--color--tertiary) 100%)", + "name": "Vertical secondary to tertiary" + }, + { + "slug": "vertical-secondary-to-background", + "gradient": "linear-gradient(to bottom,var(--wp--preset--color--secondary) 0%,var(--wp--preset--color--background) 100%)", + "name": "Vertical secondary to background" + }, + { + "slug": "vertical-tertiary-to-background", + "gradient": "linear-gradient(to bottom,var(--wp--preset--color--tertiary) 0%,var(--wp--preset--color--background) 100%)", + "name": "Vertical tertiary to background" + }, + { + "slug": "diagonal-primary-to-foreground", + "gradient": "linear-gradient(to bottom right,var(--wp--preset--color--primary) 0%,var(--wp--preset--color--foreground) 100%)", + "name": "Diagonal primary to foreground" + }, + { + "slug": "diagonal-secondary-to-background", + "gradient": "linear-gradient(to bottom right,var(--wp--preset--color--secondary) 50%,var(--wp--preset--color--background) 50%)", + "name": "Diagonal secondary to background" + }, + { + "slug": "diagonal-background-to-secondary", + "gradient": "linear-gradient(to bottom right,var(--wp--preset--color--background) 50%,var(--wp--preset--color--secondary) 50%)", + "name": "Diagonal background to secondary" + }, + { + "slug": "diagonal-tertiary-to-background", + "gradient": "linear-gradient(to bottom right,var(--wp--preset--color--tertiary) 50%,var(--wp--preset--color--background) 50%)", + "name": "Diagonal tertiary to background" + }, + { + "slug": "diagonal-background-to-tertiary", + "gradient": "linear-gradient(to bottom right,var(--wp--preset--color--background) 50%,var(--wp--preset--color--tertiary) 50%)", + "name": "Diagonal background to tertiary" + } + ], + "palette": [ + { + "slug": "foreground", + "color": "#000000", + "name": "Foreground" + }, + { + "slug": "background", + "color": "#ffffff", + "name": "Background" + }, + { + "slug": "primary", + "color": "#1a4548", + "name": "Primary" + }, + { + "slug": "secondary", + "color": "#ffe2c7", + "name": "Secondary" + }, + { + "slug": "tertiary", + "color": "#F6F6F6", + "name": "Tertiary" + } + ] + }, + "custom": { + "spacing": { + "small": "max(1.25rem, 5vw)", + "medium": "clamp(2rem, 8vw, calc(4 * var(--wp--style--block-gap)))", + "large": "clamp(4rem, 10vw, 8rem)", + "outer": "var(--wp--custom--spacing--small, 1.25rem)" + }, + "typography": { + "font-size": { + "huge": "clamp(2.25rem, 4vw, 2.75rem)", + "gigantic": "clamp(2.75rem, 6vw, 3.25rem)", + "colossal": "clamp(3.25rem, 8vw, 6.25rem)" + }, + "line-height": { + "tiny": 1.15, + "small": 1.2, + "medium": 1.4, + "normal": 1.6 + } + } + }, + "spacing": { + "units": [ + "%", + "px", + "em", + "rem", + "vh", + "vw" + ] + }, + "typography": { + "dropCap": false, + "fontFamilies": [ + { + "fontFamily": "-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif", + "name": "System Font", + "slug": "system-font" + }, + { + "fontFamily": "\"Source Serif Pro\", serif", + "name": "Source Serif Pro", + "slug": "source-serif-pro", + "fontFace": [ + { + "fontFamily": "Source Serif Pro", + "fontWeight": "200 900", + "fontStyle": "normal", + "fontStretch": "normal", + "src": [ "file:./assets/fonts/SourceSerif4Variable-Roman.ttf.woff2" ] + }, + { + "fontFamily": "Source Serif Pro", + "fontWeight": "200 900", + "fontStyle": "italic", + "fontStretch": "normal", + "src": [ "file:./assets/fonts/SourceSerif4Variable-Italic.ttf.woff2" ] + } + ] + } + ], + "fontSizes": [ + { + "size": "1rem", + "slug": "small" + }, + { + "size": "1.125rem", + "slug": "medium" + }, + { + "size": "1.75rem", + "slug": "large" + }, + { + "size": "clamp(1.75rem, 3vw, 2.25rem)", + "slug": "x-large" + } + ] + }, + "layout": { + "contentSize": "650px", + "wideSize": "1000px" + } + }, + "styles": { + "blocks": { + "core/button": { + "border": { + "radius": "0" + }, + "color": { + "background": "var(--wp--preset--color--primary)", + "text": "var(--wp--preset--color--background)" + }, + "typography": { + "fontSize": "var(--wp--preset--font-size--medium)" + } + }, + "core/post-title": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--source-serif-pro)", + "fontWeight": "300", + "lineHeight": "var(--wp--custom--typography--line-height--tiny)", + "fontSize": "var(--wp--custom--typography--font-size--gigantic)" + } + }, + "core/post-comments": { + "spacing": { + "padding": { + "top": "var(--wp--custom--spacing--small)" + } + } + }, + "core/pullquote": { + "border": { + "width": "1px 0" + } + }, + "core/query-title": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--source-serif-pro)", + "fontWeight": "300", + "lineHeight": "var(--wp--custom--typography--line-height--small)", + "fontSize": "var(--wp--custom--typography--font-size--gigantic)" + } + }, + "core/quote": { + "border": { + "width": "1px" + } + }, + "core/site-title": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--system-font)", + "lineHeight": "var(--wp--custom--typography--line-height--normal)", + "fontSize": "var(--wp--preset--font-size--medium)", + "fontStyle": "italic", + "fontWeight": "normal" + } + } + }, + "color": { + "background": "var(--wp--preset--color--background)", + "text": "var(--wp--preset--color--foreground)" + }, + "elements": { + "h1": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--source-serif-pro)", + "fontWeight": "300", + "lineHeight": "var(--wp--custom--typography--line-height--tiny)", + "fontSize": "var(--wp--custom--typography--font-size--colossal)" + } + }, + "h2": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--source-serif-pro)", + "fontWeight": "300", + "lineHeight": "var(--wp--custom--typography--line-height--small)", + "fontSize": "var(--wp--custom--typography--font-size--gigantic)" + } + }, + "h3": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--source-serif-pro)", + "fontWeight": "300", + "lineHeight": "var(--wp--custom--typography--line-height--tiny)", + "fontSize": "var(--wp--custom--typography--font-size--huge)" + } + }, + "h4": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--source-serif-pro)", + "fontWeight": "300", + "lineHeight": "var(--wp--custom--typography--line-height--tiny)", + "fontSize": "var(--wp--preset--font-size--x-large)" + } + }, + "h5": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--system-font)", + "fontWeight": "700", + "textTransform": "uppercase", + "lineHeight": "var(--wp--custom--typography--line-height--normal)", + "fontSize": "var(--wp--preset--font-size--medium)" + } + }, + "h6": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--system-font)", + "fontWeight": "400", + "textTransform": "uppercase", + "lineHeight": "var(--wp--custom--typography--line-height--normal)", + "fontSize": "var(--wp--preset--font-size--medium)" + } + }, + "link": { + "color": { + "text": "var(--wp--preset--color--foreground)" + } + } + }, + "spacing": { + "blockGap": "1.5rem" + }, + "typography": { + "fontFamily": "var(--wp--preset--font-family--system-font)", + "lineHeight": "var(--wp--custom--typography--line-height--normal)", + "fontSize": "var(--wp--preset--font-size--medium)" + } + }, + "templateParts": [ + { + "name": "header", + "title": "Header", + "area": "header" + }, + { + "name": "header-large-dark", + "title": "Header (Dark, large)", + "area": "header" + }, + { + "name": "header-small-dark", + "title": "Header (Dark, small)", + "area": "header" + }, + { + "name": "footer", + "title": "Footer", + "area": "footer" + } + ] +} diff --git a/phpunit/theme-json-webfonts-handler-test.php b/phpunit/theme-json-webfonts-handler-test.php new file mode 100644 index 00000000000000..1bfdb24861474f --- /dev/null +++ b/phpunit/theme-json-webfonts-handler-test.php @@ -0,0 +1,117 @@ +old_wp_styles = $wp_styles; + $wp_styles = null; + + global $wp_webfonts; + $this->old_wp_webfonts = $wp_webfonts; + $wp_webfonts = null; + + $this->theme_root = realpath( __DIR__ . '/data/themedir1' ); + $this->old_theme_dir = $GLOBALS['wp_theme_directories']; + + // /themes is necessary as theme.php functions assume /themes is the root if there is only one root. + $GLOBALS['wp_theme_directories'] = array( WP_CONTENT_DIR . '/themes', $this->theme_root ); + + $theme_root_callback = function () { + return $this->theme_root; + }; + + add_filter( 'theme_root', $theme_root_callback ); + add_filter( 'stylesheet_root', $theme_root_callback ); + add_filter( 'template_root', $theme_root_callback ); + + // Clear caches. + wp_clean_themes_cache(); + unset( $GLOBALS['wp_themes'] ); + } + + public function tear_down() { + global $wp_webfonts; + global $wp_styles; + + $wp_webfonts = $this->old_wp_webfonts; + $wp_styles = $this->old_wp_styles; + + // Restore the original theme directory setup. + $GLOBALS['wp_theme_directories'] = $this->old_theme_dir; + wp_clean_themes_cache(); + unset( $GLOBALS['wp_themes'] ); + + parent::tear_down(); + } + + public function test_font_face_styles_are_generated() { + // Set up the theme. + switch_theme( 'webfonts-theme' ); + do_action( 'after_setup_theme' ); + WP_Theme_JSON_Resolver::clean_cached_data(); + do_action( 'wp_loaded' ); + do_action( 'wp_enqueue_scripts' ); + + $expected = << +@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('/wp-content/gutenberg/phpunit/data/themedir1/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');font-stretch:normal;}@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('/wp-content/gutenberg/phpunit/data/themedir1/assets/fonts/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');font-stretch:normal;} + +EOF; + + $this->assertStringContainsString( + $expected, + get_echo( 'wp_print_styles' ) + ); + } + + public function test_mime_types_added() { + $actual = wp_get_mime_types(); + + $this->assertArrayHasKey( 'woff2', $actual, 'woff2 mime type is expected to be added' ); + $this->assertArrayHasKey( 'woff', $actual, 'woff mime type is expected to be added' ); + $this->assertArrayHasKey( 'ttf', $actual, 'ttf mime type is expected to be added' ); + $this->assertArrayHasKey( 'otf', $actual, 'otf mime type is expected to be added' ); + } +} From eca6343e9937d99a4906824632a08b8624d4c14b Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Thu, 21 Apr 2022 03:33:40 -0500 Subject: [PATCH 02/14] Updates DocBlock from code review. Co-authored-by: Colin Stewart <79332690+costdev@users.noreply.github.com> --- .../class-theme-json-webfonts-handler.php | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index 8234ead94a7517..0dd36366e21182 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -11,7 +11,7 @@ add_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); /** * Runs the theme.json webfonts handler to expose defined webfonts - * to the editor's typography pickers and generate `@font-face` style + * to the editor's typography pickers and generate '@font-face' style * declarations. * * This is not a public API, but rather an internal handler. @@ -36,7 +36,7 @@ function _wp_theme_json_webfonts_handler() { } /** - * Handler for `theme.json` webfonts. + * Handler for theme.json webfonts. * * This class is intentionally placed within the function to create * a private, non-public, non-accessible container. This code will be @@ -65,7 +65,7 @@ class Theme_JSON_Webfonts_Handler { private $stylesheet_handle = ''; /** - * Initialize the handler. + * Initializes the handler. * * @since 6.0.0 */ @@ -87,7 +87,7 @@ public function init() { } /** - * Register webfonts within the `theme.json`. + * Registers webfonts declared in theme.json. * * @since 6.0.0 */ @@ -116,7 +116,7 @@ public function register_webfonts() { } /** - * Gets the webfonts from `theme.json`. + * Gets the webfonts from theme.json. * * @since 6.0.0 * @@ -155,10 +155,10 @@ private function get_webfonts_from_theme_json() { } /** - * Transform each `src` into an URI by replacing the `file:./` + * Transforms each 'src' into an URI by replacing 'file:./' * placeholder from theme.json. * - * The absolute path to the webfont file(s) Cannot be defined in + * The absolute path to the webfont file(s) cannot be defined in * theme.json. `file:./` is the placeholder which is replaced by * the theme's URL path to the theme's root. * @@ -182,7 +182,7 @@ private function transform_src_into_uri( array $src ) { } /** - * Converts the face-face properties (i.e. keys) into kebab-case. + * Converts the font-face properties (i.e. keys) into kebab-case. * * @since 6.0.0 * @@ -203,7 +203,7 @@ private function convert_keys_to_kebab_case( array $font_face ) { } /** - * Validate a webfont. + * Validates a webfont. * * @since 6.0.0 * @@ -230,14 +230,14 @@ public function validate_webfont( $webfont ) { return false; } - // Make a `src` property is defined. + // Check that the `src` property is defined and a valid type. 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. + // Validate the `src` property. if ( ! empty( $webfont['src'] ) ) { foreach ( (array) $webfont['src'] as $src ) { if ( empty( $src ) || ! is_string( $src ) ) { @@ -287,7 +287,7 @@ public function validate_webfont( $webfont ) { } /** - * Generate and enqueue webfonts styles. + * Generates and enqueues webfonts styles. * * @since 6.0.0 */ @@ -309,7 +309,7 @@ public function generate_and_enqueue_styles() { } /** - * Generate and enqueue editor styles. + * Generates and enqueues editor styles. * * @since 6.0.0 */ @@ -326,7 +326,7 @@ public function generate_and_enqueue_editor_styles() { } /** - * Gets the `@font-face` CSS styles for locally-hosted font files. + * Gets the '@font-face' CSS styles for locally-hosted font files. * * @since 6.0.0 * @@ -347,7 +347,7 @@ public function get_css() { } /** - * Order `src` items to optimize for browser support. + * Orders 'src' items to optimize for browser support. * * @since 6.0.0 * @@ -465,7 +465,7 @@ private function build_font_face_css( array $webfont ) { } /** - * Compiles the `src` into valid CSS. + * Compiles the 'src' into valid CSS. * * @since 6.0.0 * @@ -511,7 +511,7 @@ private function compile_variations( array $font_variation_settings ) { } /** - * Add webfonts mime types. + * Adds webfonts mime types. * * @since 6.0.0 * From 45c2c8854ceb27a6a7c3c32f46ed58a973790007 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Thu, 21 Apr 2022 03:43:33 -0500 Subject: [PATCH 03/14] Use str_starts_with() instead of strpos(). Co-authored-by: Colin Stewart <79332690+costdev@users.noreply.github.com> --- lib/experimental/class-theme-json-webfonts-handler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index 0dd36366e21182..6f55ea1cafc1e1 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -361,7 +361,7 @@ private function order_src( array $webfont ) { foreach ( $webfont['src'] as $url ) { // Add data URIs first. - if ( 0 === strpos( trim( $url ), 'data:' ) ) { + if ( str_starts_with( trim( $url ), 'data:' ) ) { $src_ordered[] = array( 'url' => $url, 'format' => 'data', @@ -479,7 +479,7 @@ private function compile_src( $font_family, array $value ) { foreach ( $value as $item ) { - if ( 0 === strpos( $item['url'], get_site_url() ) ) { + if ( str_starts_with( $item['url'], get_site_url() ) ) { $item['url'] = wp_make_link_relative( $item['url'] ); } From 99a14e933967d40e00b49d2c5a0c53ee1cab2664 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 03:57:50 -0500 Subject: [PATCH 04/14] Fix guarding conditions. --- lib/experimental/class-theme-json-webfonts-handler.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index 6f55ea1cafc1e1..05e209523a295c 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -238,13 +238,11 @@ public function validate_webfont( $webfont ) { } // 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' ) ); + foreach ( (array) $webfont['src'] as $src ) { + if ( ! is_string( $src ) || '' === trim( $src ) ) { + trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) ); - return false; - } + return false; } } From 4f6f098598f99a14a400920847f7dc2666c70043 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 04:00:52 -0500 Subject: [PATCH 05/14] Sanitize src URLs. --- .../class-theme-json-webfonts-handler.php | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index 05e209523a295c..726ebe99da85e4 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -2,9 +2,9 @@ /** * Handler for theme.json webfonts. * - * @package WordPress - * @subpackage WebFonts * @since 6.0.0 + * @subpackage WebFonts + * @package WordPress */ if ( ! function_exists( '_wp_theme_json_webfonts_handler' ) ) { @@ -23,7 +23,7 @@ * for webfonts to be defined in a theme's `theme.json` file for style * variations. * - * @link https://github.com/WordPress/gutenberg/issues/40472 + * @link https://github.com/WordPress/gutenberg/issues/40472 * * @since 6.0.0 */ @@ -165,7 +165,6 @@ private function get_webfonts_from_theme_json() { * @since 6.0.0 * * @param array $src Webfont file(s) `src`. - * * @return array Webfont's `src` in URI. */ private function transform_src_into_uri( array $src ) { @@ -187,7 +186,6 @@ private function transform_src_into_uri( array $src ) { * @since 6.0.0 * * @param array $font_face Font face to convert. - * * @return array Font faces with each property in kebab-case format. */ private function convert_keys_to_kebab_case( array $font_face ) { @@ -208,7 +206,6 @@ private function convert_keys_to_kebab_case( array $font_face ) { * @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 ) { @@ -350,7 +347,6 @@ public function get_css() { * @since 6.0.0 * * @param array $webfont Webfont to process. - * * @return array Ordered `src` items. */ private function order_src( array $webfont ) { @@ -373,7 +369,7 @@ private function order_src( array $webfont ) { // Add woff2. if ( ! empty( $src['woff2'] ) ) { $src_ordered[] = array( - 'url' => $src['woff2'], + 'url' => sanitize_url( $src['woff2'] ), 'format' => 'woff2', ); } @@ -381,7 +377,7 @@ private function order_src( array $webfont ) { // Add woff. if ( ! empty( $src['woff'] ) ) { $src_ordered[] = array( - 'url' => $src['woff'], + 'url' => sanitize_url( $src['woff'] ), 'format' => 'woff', ); } @@ -389,7 +385,7 @@ private function order_src( array $webfont ) { // Add ttf. if ( ! empty( $src['ttf'] ) ) { $src_ordered[] = array( - 'url' => $src['ttf'], + 'url' => sanitize_url( $src['ttf'] ), 'format' => 'truetype', ); } @@ -397,7 +393,7 @@ private function order_src( array $webfont ) { // Add eot. if ( ! empty( $src['eot'] ) ) { $src_ordered[] = array( - 'url' => $src['eot'], + 'url' => sanitize_url( $src['eot'] ), 'format' => 'embedded-opentype', ); } @@ -405,7 +401,7 @@ private function order_src( array $webfont ) { // Add otf. if ( ! empty( $src['otf'] ) ) { $src_ordered[] = array( - 'url' => $src['otf'], + 'url' => sanitize_url( $src['otf'] ), 'format' => 'opentype', ); } @@ -420,7 +416,6 @@ private function order_src( array $webfont ) { * @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 ) { @@ -469,7 +464,6 @@ private function build_font_face_css( array $webfont ) { * * @param string $font_family Font family. * @param array $value Value to process. - * * @return string The CSS. */ private function compile_src( $font_family, array $value ) { @@ -514,6 +508,7 @@ private function compile_variations( array $font_variation_settings ) { * @since 6.0.0 * * @param array $mime_types Array of mime types. + * * @return array Mime types with webfonts formats. */ public function add_mime_types( $mime_types ) { From e32700d58e0b97765f1f09a767177389c36d831b Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 04:03:36 -0500 Subject: [PATCH 06/14] Remove wp_print_footer_scripts hook. --- .../class-theme-json-webfonts-handler.php | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index 726ebe99da85e4..c7e926a2ac4aad 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -55,15 +55,6 @@ class Theme_JSON_Webfonts_Handler { */ private $webfonts = array(); - /** - * Stylesheet handle. - * - * @since 6.0.0 - * - * @var string - */ - private $stylesheet_handle = ''; - /** * Initializes the handler. * @@ -72,15 +63,7 @@ class Theme_JSON_Webfonts_Handler { public function init() { add_action( 'wp_loaded', array( $this, 'register_webfonts' ) ); - // Register callback to generate and enqueue styles. - if ( did_action( 'wp_enqueue_scripts' ) ) { - $this->stylesheet_handle = 'wp-webfonts-footer'; - $hook = 'wp_print_footer_scripts'; - } else { - $this->stylesheet_handle = 'wp-webfonts'; - $hook = 'wp_enqueue_scripts'; - } - add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'generate_and_enqueue_styles' ) ); add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); add_filter( 'mime_types', array( $this, 'add_mime_types' ) ); @@ -296,11 +279,11 @@ public function generate_and_enqueue_styles() { } // Enqueue the stylesheet. - wp_register_style( $this->stylesheet_handle, '' ); - wp_enqueue_style( $this->stylesheet_handle ); + wp_register_style( 'wp-webfonts', '' ); + wp_enqueue_style( 'wp-webfonts' ); // Add the styles to the stylesheet. - wp_add_inline_style( $this->stylesheet_handle, $styles ); + wp_add_inline_style( 'wp-webfonts', $styles ); } /** From 66926e28c600e9eabb3701ec85e9af27bde0b129 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 04:37:37 -0500 Subject: [PATCH 07/14] Adds home_url() as a relative link check. --- lib/experimental/class-theme-json-webfonts-handler.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index c7e926a2ac4aad..b9c9f883cb7fb1 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -454,7 +454,10 @@ private function compile_src( $font_family, array $value ) { foreach ( $value as $item ) { - if ( str_starts_with( $item['url'], get_site_url() ) ) { + if ( + str_starts_with( $item['url'], site_url() ) || + str_starts_with( $item['url'], home_url() ) + ) { $item['url'] = wp_make_link_relative( $item['url'] ); } From 6347e92d95f6d1871e4a34c699d33549117c5a77 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 05:53:29 -0500 Subject: [PATCH 08/14] Use closures assigned to variables to limit external access. Changes to use $fn_var = function() {} design pattern, which eliminates external access. The previous design used a `class` which when using reflection is not fully unaccessible. --- .../class-theme-json-webfonts-handler.php | 773 +++++++++--------- 1 file changed, 374 insertions(+), 399 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index b9c9f883cb7fb1..01abbc57a2e90a 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -28,489 +28,464 @@ * @since 6.0.0 */ function _wp_theme_json_webfonts_handler() { - static $handler = null; - - // This check prevents the class from being loaded into memory more than once. - if ( null !== $handler ) { - return; - } + // Webfonts to be processed. + $registered_webfonts = array(); /** - * Handler for theme.json webfonts. - * - * This class is intentionally placed within the function to create - * a private, non-public, non-accessible container. This code will be - * removed when the Webfonts API is merged into Core. + * Gets the webfonts from theme.json. * * @since 6.0.0 + * + * @return array Array of defined webfonts. */ - class Theme_JSON_Webfonts_Handler { - - /** - * Webfonts to be processed. - * - * @since 6.0.0 - * - * @var array[] - */ - private $webfonts = array(); - - /** - * Initializes the handler. - * - * @since 6.0.0 - */ - public function init() { - add_action( 'wp_loaded', array( $this, 'register_webfonts' ) ); - - add_action( 'wp_enqueue_scripts', array( $this, 'generate_and_enqueue_styles' ) ); - add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); - - add_filter( 'mime_types', array( $this, 'add_mime_types' ) ); - } + $fn_get_webfonts_from_theme_json = static function() { + // Get settings from theme.json. + $theme_settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); - /** - * Registers webfonts declared in theme.json. - * - * @since 6.0.0 - */ - public function register_webfonts() { - $this->webfonts = array(); - $webfonts = $this->get_webfonts_from_theme_json(); - - foreach ( $webfonts as $webfont ) { - if ( ! is_array( $webfont ) ) { - continue; - } + // Bail out early if there are no settings for webfonts. + if ( empty( $theme_settings['typography'] ) || empty( $theme_settings['typography']['fontFamilies'] ) ) { + return array(); + } - $webfont = $this->convert_keys_to_kebab_case( $webfont ); + $webfonts = array(); - $webfont = $this->validate_webfont( $webfont ); + // Look for fontFamilies. + foreach ( $theme_settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $font_family ) { - $webfont['src'] = $this->transform_src_into_uri( (array) $webfont['src'] ); + // Skip if fontFace is not defined. + if ( empty( $font_family['fontFace'] ) ) { + continue; + } - // Skip if not valid. - if ( empty( $webfont ) ) { + // Skip if fontFace is not an array of webfonts. + if ( ! is_array( $font_family['fontFace'] ) ) { continue; } - $this->webfonts[] = $webfont; + $webfonts = array_merge( $webfonts, $font_family['fontFace'] ); } } - /** - * Gets the webfonts from theme.json. - * - * @since 6.0.0 - * - * @return array Array of defined webfonts. - */ - private function get_webfonts_from_theme_json() { - // Get settings from theme.json. - $theme_settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); - - // Bail out early if there are no settings for webfonts. - if ( empty( $theme_settings['typography'] ) || empty( $theme_settings['typography']['fontFamilies'] ) ) { - return array(); + return $webfonts; + }; + + /** + * Transforms each 'src' into an URI by replacing 'file:./' + * placeholder from theme.json. + * + * The absolute path to the webfont file(s) cannot be defined in + * theme.json. `file:./` is the placeholder which is replaced by + * the theme's URL path to the theme's root. + * + * @since 6.0.0 + * + * @param array $src Webfont file(s) `src`. + * @return array Webfont's `src` in URI. + */ + $fn_transform_src_into_uri = static function( array $src ) { + foreach ( $src as $key => $url ) { + // Tweak the URL to be relative to the theme root. + if ( ! str_starts_with( $url, 'file:./' ) ) { + continue; } - $webfonts = array(); + $src[ $key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); + } + + return $src; + }; - // Look for fontFamilies. - foreach ( $theme_settings['typography']['fontFamilies'] as $font_families ) { - foreach ( $font_families as $font_family ) { + /** + * Converts the font-face properties (i.e. keys) into kebab-case. + * + * @since 6.0.0 + * + * @param array $font_face Font face to convert. + * @return array Font faces with each property in kebab-case format. + */ + $fn_convert_keys_to_kebab_case = static function( array $font_face ) { + 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 ] ); + } + } - // Skip if fontFace is not defined. - if ( empty( $font_family['fontFace'] ) ) { - continue; - } + return $font_face; + }; - // Skip if fontFace is not an array of webfonts. - if ( ! is_array( $font_family['fontFace'] ) ) { - continue; - } + /** + * Validates 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. + */ + $fn_validate_webfont = static function( $webfont ) { + $webfont = wp_parse_args( + $webfont, + array( + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + 'src' => array(), + ) + ); + + // 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; + } - $webfonts = array_merge( $webfonts, $font_family['fontFace'] ); - } - } + // Check that the `src` property is defined and a valid type. + 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 $webfonts; + return false; } - /** - * Transforms each 'src' into an URI by replacing 'file:./' - * placeholder from theme.json. - * - * The absolute path to the webfont file(s) cannot be defined in - * theme.json. `file:./` is the placeholder which is replaced by - * the theme's URL path to the theme's root. - * - * @since 6.0.0 - * - * @param array $src Webfont file(s) `src`. - * @return array Webfont's `src` in URI. - */ - private function transform_src_into_uri( array $src ) { - foreach ( $src as $key => $url ) { - // Tweak the URL to be relative to the theme root. - if ( ! str_starts_with( $url, 'file:./' ) ) { - continue; - } + // Validate the `src` property. + foreach ( (array) $webfont['src'] as $src ) { + if ( ! is_string( $src ) || '' === trim( $src ) ) { + trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) ); - $src[ $key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); + 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 $src; + return false; } - /** - * Converts the font-face properties (i.e. keys) into kebab-case. - * - * @since 6.0.0 - * - * @param array $font_face Font face to convert. - * @return array Font faces with each property in kebab-case format. - */ - private function convert_keys_to_kebab_case( array $font_face ) { - 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 ] ); - } - } + // Check the font-display. + if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { + $webfont['font-display'] = 'fallback'; + } - return $font_face; + $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', + ); + + foreach ( $webfont as $prop => $value ) { + if ( ! in_array( $prop, $valid_props, true ) ) { + unset( $webfont[ $prop ] ); + } } - /** - * Validates 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( - 'font-family' => '', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-display' => 'fallback', - 'src' => array(), - ) - ); + return $webfont; + }; - // 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' ) ); + /** + * Registers webfonts declared in theme.json. + * + * @since 6.0.0 + * + * @uses $registered_webfonts To access and update the registered webfonts registry (passed by reference). + * @uses $fn_get_webfonts_from_theme_json To run the function that gets the webfonts from theme.json. + * @uses $fn_convert_keys_to_kebab_case To run the function that converts keys into kebab-case. + * @uses $fn_validate_webfont To run the function that validates each font-face (webfont) from theme.json. + */ + $fn_register_webfonts = static function() use ( &$registered_webfonts, $fn_get_webfonts_from_theme_json, $fn_convert_keys_to_kebab_case, $fn_validate_webfont, $fn_transform_src_into_uri ) { + $registered_webfonts = array(); - return false; + foreach ( $fn_get_webfonts_from_theme_json() as $webfont ) { + if ( ! is_array( $webfont ) ) { + continue; } - // Check that the `src` property is defined and a valid type. - 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' ) ); + $webfont = $fn_convert_keys_to_kebab_case( $webfont ); - return false; - } + $webfont = $fn_validate_webfont( $webfont ); - // Validate the `src` property. - foreach ( (array) $webfont['src'] as $src ) { - if ( ! is_string( $src ) || '' === trim( $src ) ) { - trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) ); + $webfont['src'] = $fn_transform_src_into_uri( (array) $webfont['src'] ); - return false; - } + // Skip if not valid. + if ( empty( $webfont ) ) { + continue; } - // 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' ) ); + $registered_webfonts[] = $webfont; + } + }; - return false; - } + /** + * Orders 'src' items to optimize for browser support. + * + * @since 6.0.0 + * + * @param array $webfont Webfont to process. + * @return array Ordered `src` items. + */ + $fn_order_src = static function( array $webfont ) { + $src = array(); + $src_ordered = array(); - // Check the font-display. - if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { - $webfont['font-display'] = 'fallback'; + foreach ( $webfont['src'] as $url ) { + // Add data URIs first. + if ( str_starts_with( trim( $url ), 'data:' ) ) { + $src_ordered[] = array( + 'url' => $url, + 'format' => 'data', + ); + continue; } + $format = pathinfo( $url, PATHINFO_EXTENSION ); + $src[ $format ] = $url; + } - $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', + // Add woff2. + if ( ! empty( $src['woff2'] ) ) { + $src_ordered[] = array( + 'url' => sanitize_url( $src['woff2'] ), + 'format' => 'woff2', ); + } - foreach ( $webfont as $prop => $value ) { - if ( ! in_array( $prop, $valid_props, true ) ) { - unset( $webfont[ $prop ] ); - } - } - - return $webfont; + // Add woff. + if ( ! empty( $src['woff'] ) ) { + $src_ordered[] = array( + 'url' => sanitize_url( $src['woff'] ), + 'format' => 'woff', + ); } - /** - * Generates and enqueues webfonts styles. - * - * @since 6.0.0 - */ - public function generate_and_enqueue_styles() { - // Generate the styles. - $styles = $this->get_css(); - - // Bail out if there are no styles to enqueue. - if ( '' === $styles ) { - return; - } + // Add ttf. + if ( ! empty( $src['ttf'] ) ) { + $src_ordered[] = array( + 'url' => sanitize_url( $src['ttf'] ), + 'format' => 'truetype', + ); + } - // Enqueue the stylesheet. - wp_register_style( 'wp-webfonts', '' ); - wp_enqueue_style( 'wp-webfonts' ); + // Add eot. + if ( ! empty( $src['eot'] ) ) { + $src_ordered[] = array( + 'url' => sanitize_url( $src['eot'] ), + 'format' => 'embedded-opentype', + ); + } - // Add the styles to the stylesheet. - wp_add_inline_style( 'wp-webfonts', $styles ); + // Add otf. + if ( ! empty( $src['otf'] ) ) { + $src_ordered[] = array( + 'url' => sanitize_url( $src['otf'] ), + 'format' => 'opentype', + ); } + $webfont['src'] = $src_ordered; - /** - * Generates and enqueues editor styles. - * - * @since 6.0.0 - */ - public function generate_and_enqueue_editor_styles() { - // Generate the styles. - $styles = $this->get_css(); - - // Bail out if there are no styles to enqueue. - if ( '' === $styles ) { - return; + return $webfont; + }; + + /** + * 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. + */ + $fn_compile_src = static function( $font_family, array $value ) { + $src = "local($font_family)"; + + foreach ( $value as $item ) { + + if ( + str_starts_with( $item['url'], site_url() ) || + str_starts_with( $item['url'], home_url() ) + ) { + $item['url'] = wp_make_link_relative( $item['url'] ); } - wp_add_inline_style( 'wp-block-library', $styles ); + $src .= ( 'data' === $item['format'] ) + ? ", url({$item['url']})" + : ", url('{$item['url']}') format('{$item['format']}')"; } - /** - * Gets the '@font-face' CSS styles for locally-hosted font files. - * - * @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 $src; + }; - return $css; + /** + * Compiles the font variation settings. + * + * @since 6.0.0 + * + * @param array $font_variation_settings Array of font variation settings. + * @return string The CSS. + */ + $fn_compile_variations = static function( array $font_variation_settings ) { + $variations = ''; + + foreach ( $font_variation_settings as $key => $value ) { + $variations .= "$key $value"; } - /** - * Orders 'src' items to optimize for browser support. - * - * @since 6.0.0 - * - * @param array $webfont Webfont to process. - * @return array Ordered `src` items. - */ - private function order_src( array $webfont ) { - $src = array(); - $src_ordered = array(); - - foreach ( $webfont['src'] as $url ) { - // Add data URIs first. - if ( str_starts_with( trim( $url ), 'data:' ) ) { - $src_ordered[] = array( - 'url' => $url, - 'format' => 'data', - ); - continue; - } - $format = pathinfo( $url, PATHINFO_EXTENSION ); - $src[ $format ] = $url; - } + return $variations; + }; - // Add woff2. - if ( ! empty( $src['woff2'] ) ) { - $src_ordered[] = array( - 'url' => sanitize_url( $src['woff2'] ), - 'format' => 'woff2', - ); - } + /** + * Builds the font-family's CSS. + * + * @since 6.0.0 + * + * @uses $fn_compile_src To run the function that compiles the src. + * @uses $fn_compile_variations To run the function that compiles the variations. + * + * @param array $webfont Webfont to process. + * @return string This font-family's CSS. + */ + $fn_build_font_face_css = static function( array $webfont ) use ( $fn_compile_src, $fn_compile_variations ) { + $css = ''; + + // Wrap font-family in quotes if it contains spaces. + if ( + str_contains( $webfont['font-family'], ' ' ) && + ! str_contains( $webfont['font-family'], '"' ) && + ! str_contains( $webfont['font-family'], "'" ) + ) { + $webfont['font-family'] = '"' . $webfont['font-family'] . '"'; + } - // Add woff. - if ( ! empty( $src['woff'] ) ) { - $src_ordered[] = array( - 'url' => sanitize_url( $src['woff'] ), - 'format' => 'woff', - ); + foreach ( $webfont as $key => $value ) { + /* + * Skip "provider", since it's for internal API use, + * and not a valid CSS property. + */ + if ( 'provider' === $key ) { + continue; } - // Add ttf. - if ( ! empty( $src['ttf'] ) ) { - $src_ordered[] = array( - 'url' => sanitize_url( $src['ttf'] ), - 'format' => 'truetype', - ); + // Compile the "src" parameter. + if ( 'src' === $key ) { + $value = $fn_compile_src( $webfont['font-family'], $value ); } - // Add eot. - if ( ! empty( $src['eot'] ) ) { - $src_ordered[] = array( - 'url' => sanitize_url( $src['eot'] ), - 'format' => 'embedded-opentype', - ); + // If font-variation-settings is an array, convert it to a string. + if ( 'font-variation-settings' === $key && is_array( $value ) ) { + $value = $fn_compile_variations( $value ); } - // Add otf. - if ( ! empty( $src['otf'] ) ) { - $src_ordered[] = array( - 'url' => sanitize_url( $src['otf'] ), - 'format' => 'opentype', - ); + if ( ! empty( $value ) ) { + $css .= "$key:$value;"; } - $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 ( - str_contains( $webfont['font-family'], ' ' ) && - ! str_contains( $webfont['font-family'], '"' ) && - ! str_contains( $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; - } + return $css; + }; - // 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 ); - } + /** + * Gets the '@font-face' CSS styles for locally-hosted font files. + * + * @since 6.0.0 + * + * @uses $registered_webfonts To access and update the registered webfonts registry (passed by reference). + * @uses $fn_order_src To run the function that orders the src. + * @uses $fn_build_font_face_css To run the function that builds the font-face CSS. + * + * @return string The `@font-face` CSS. + */ + $fn_get_css = static function() use ( &$registered_webfonts, $fn_order_src, $fn_build_font_face_css ) { + $css = ''; - if ( ! empty( $value ) ) { - $css .= "$key:$value;"; - } - } + foreach ( $registered_webfonts as $webfont ) { + // Order the webfont's `src` items to optimize for browser support. + $webfont = $fn_order_src( $webfont ); - return $css; + // Build the @font-face CSS for this webfont. + $css .= '@font-face{' . $fn_build_font_face_css( $webfont ) . '}'; } - /** - * 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 ( - str_starts_with( $item['url'], site_url() ) || - str_starts_with( $item['url'], home_url() ) - ) { - $item['url'] = wp_make_link_relative( $item['url'] ); - } + return $css; + }; - $src .= ( 'data' === $item['format'] ) - ? ", url({$item['url']})" - : ", url('{$item['url']}') format('{$item['format']}')"; - } + /** + * Generates and enqueues webfonts styles. + * + * @since 6.0.0 + * + * @uses $fn_get_css To run the function that gets the CSS. + */ + $fn_generate_and_enqueue_styles = static function() use ( $fn_get_css ) { + // Generate the styles. + $styles = $fn_get_css(); - return $src; + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; } - /** - * 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"; - } + // Enqueue the stylesheet. + wp_register_style( 'wp-webfonts', '' ); + wp_enqueue_style( 'wp-webfonts' ); - return $variations; - } + // Add the styles to the stylesheet. + wp_add_inline_style( 'wp-webfonts', $styles ); + }; + + /** + * Generates and enqueues editor styles. + * + * @since 6.0.0 + * + * @uses $fn_get_css To run the function that gets the CSS. + */ + $fn_generate_and_enqueue_editor_styles = static function() use ( $fn_get_css ) { + // Generate the styles. + $styles = $fn_get_css(); - /** - * Adds webfonts mime types. - * - * @since 6.0.0 - * - * @param array $mime_types Array of mime types. - * - * @return array Mime types with webfonts formats. - */ - public function add_mime_types( $mime_types ) { - // Webfonts formats. - $mime_types['woff2'] = 'font/woff2'; - $mime_types['woff'] = 'font/woff'; - $mime_types['ttf'] = 'font/ttf'; - $mime_types['eot'] = 'application/vnd.ms-fontobject'; - $mime_types['otf'] = 'application/x-font-opentype'; - - return $mime_types; + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; } - } - // Run the handler. - $handler = new Theme_JSON_Webfonts_Handler(); - $handler->init(); + wp_add_inline_style( 'wp-block-library', $styles ); + }; + + /** + * Adds webfonts mime types. + * + * @since 6.0.0 + * + * @param array $mime_types Array of mime types. + * @return array Mime types with webfonts formats. + */ + $fn_add_mime_types = static function( $mime_types ) { + // Webfonts formats. + $mime_types['woff2'] = 'font/woff2'; + $mime_types['woff'] = 'font/woff'; + $mime_types['ttf'] = 'font/ttf'; + $mime_types['eot'] = 'application/vnd.ms-fontobject'; + $mime_types['otf'] = 'application/x-font-opentype'; + + return $mime_types; + }; + + add_action( 'wp_loaded', $fn_register_webfonts ); + add_action( 'wp_enqueue_scripts', $fn_generate_and_enqueue_styles ); + add_action( 'admin_init', $fn_generate_and_enqueue_editor_styles ); + add_filter( 'mime_types', $fn_add_mime_types ); } } From fb00786b9e5b52b6e280068208f1bc80a29cb5ab Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 10:12:34 -0500 Subject: [PATCH 09/14] Comment out API functions in Resolver for testing stopgap. --- .../class-wp-theme-json-resolver-gutenberg.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php index 960ea659e8d2ee..9c11d888d92a46 100644 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -36,14 +36,26 @@ public static function get_theme_data( $deprecated = array(), $settings = array( 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 ); + + /* + * DO NOT MERGE THIS CODE CHANGE. + * + * Commenting this out to avoid fatal error when testing the stopgap. + */ + // $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 ); + + /* + * DO NOT MERGE THIS CODE CHANGE. + * + * Commenting this out to avoid fatal error when testing the stopgap. + */ + // $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. From 8295f151b7f7b7389e4b9bccd3204f563bc03931 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 10:46:27 -0500 Subject: [PATCH 10/14] Add webfonts defined in variations when in the editor. --- .../class-theme-json-webfonts-handler.php | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index 01abbc57a2e90a..b8ac0ec20a8d7e 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -40,17 +40,40 @@ function _wp_theme_json_webfonts_handler() { */ $fn_get_webfonts_from_theme_json = static function() { // Get settings from theme.json. - $theme_settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); + $settings = WP_Theme_JSON_Resolver::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::get_style_variations(); + foreach ( $variations as $variation ) { + // Skip if fontFamilies are not defined in the variation. + if ( + empty( $variation['settings'] ) || + empty( $variation['settings']['typography'] ) || + empty( $variation['settings']['typography']['fontFamilies'] ) + ) { + continue; + } + + $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'] ); + + // 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( $theme_settings['typography'] ) || empty( $theme_settings['typography']['fontFamilies'] ) ) { + if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { return array(); } $webfonts = array(); // Look for fontFamilies. - foreach ( $theme_settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $settings['typography']['fontFamilies'] as $font_families ) { foreach ( $font_families as $font_family ) { // Skip if fontFace is not defined. From 6fb945b84dacab47ddf4fb873640c0272cb46fb3 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 21 Apr 2022 11:26:59 -0500 Subject: [PATCH 11/14] Use WP_Theme_JSON_Resolver_6_0 instead of latest in WP Core trunk. --- lib/experimental/class-theme-json-webfonts-handler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/class-theme-json-webfonts-handler.php index b8ac0ec20a8d7e..28f4ba32e0d1fd 100644 --- a/lib/experimental/class-theme-json-webfonts-handler.php +++ b/lib/experimental/class-theme-json-webfonts-handler.php @@ -40,11 +40,11 @@ function _wp_theme_json_webfonts_handler() { */ $fn_get_webfonts_from_theme_json = static function() { // Get settings from theme.json. - $settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); + $settings = WP_Theme_JSON_Resolver_6_0::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::get_style_variations(); + $variations = WP_Theme_JSON_Resolver_6_0::get_style_variations(); foreach ( $variations as $variation ) { // Skip if fontFamilies are not defined in the variation. if ( From 66d182a32db93b6089e3e35189e9c9580b3a9cc5 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 22 Apr 2022 03:47:45 -0500 Subject: [PATCH 12/14] Rename file, no longer a class. --- ...-json-webfonts-handler.php => theme-json-webfonts-handler.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/experimental/{class-theme-json-webfonts-handler.php => theme-json-webfonts-handler.php} (100%) diff --git a/lib/experimental/class-theme-json-webfonts-handler.php b/lib/experimental/theme-json-webfonts-handler.php similarity index 100% rename from lib/experimental/class-theme-json-webfonts-handler.php rename to lib/experimental/theme-json-webfonts-handler.php From 5047488bd53da237a1bee0ff3450ed60c32c76f0 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 22 Apr 2022 03:49:17 -0500 Subject: [PATCH 13/14] Relocate file for backport. --- .../wordpress-6.0}/theme-json-webfonts-handler.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/{experimental => compat/wordpress-6.0}/theme-json-webfonts-handler.php (100%) diff --git a/lib/experimental/theme-json-webfonts-handler.php b/lib/compat/wordpress-6.0/theme-json-webfonts-handler.php similarity index 100% rename from lib/experimental/theme-json-webfonts-handler.php rename to lib/compat/wordpress-6.0/theme-json-webfonts-handler.php From 743ca6d53f2fcfc3ef88f9b2e26fdbcc64556fe9 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 22 Apr 2022 05:03:39 -0500 Subject: [PATCH 14/14] Remove reassignment of $settings array level init (micro-optimization). --- .../theme-json-webfonts-handler.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/compat/wordpress-6.0/theme-json-webfonts-handler.php b/lib/compat/wordpress-6.0/theme-json-webfonts-handler.php index 28f4ba32e0d1fd..35c9896a037bb3 100644 --- a/lib/compat/wordpress-6.0/theme-json-webfonts-handler.php +++ b/lib/compat/wordpress-6.0/theme-json-webfonts-handler.php @@ -55,13 +55,20 @@ function _wp_theme_json_webfonts_handler() { continue; } - $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'] ); + // Initialize the array structure. + if ( empty( $settings['typography'] ) ) { + $settings['typography'] = array(); + } + if ( empty( $settings['typography']['fontFamilies'] ) ) { + $settings['typography']['fontFamilies'] = array(); + } + if ( empty( $settings['typography']['fontFamilies']['theme'] ) ) { + $settings['typography']['fontFamilies']['theme'] = array(); + } - // Make sure there are no duplicates. - $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] ); + // Combine variations with settings. Remove duplicates. + $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] ); + $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] ); } }