Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load variation font faces with theme relative urls using backend provided full urls (_links) #65019

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6219a6d
add editor font face resolver component
matiasbenedetto Sep 3, 2024
1e8ef55
remove code not needed
matiasbenedetto Sep 4, 2024
65f3754
default value when font families aren't defined
matiasbenedetto Sep 5, 2024
77f7638
comments formatting
matiasbenedetto Sep 9, 2024
0bed01d
use callback for loadFontFaceAsset
matiasbenedetto Sep 9, 2024
48bfe65
improve syntax
matiasbenedetto Sep 9, 2024
65d147a
Merge branch 'trunk' into add/editor-font-face-resolver
matiasbenedetto Sep 11, 2024
c6a32f9
Move EditorFontsResolver inside EditorStyles, use fontFamilies data f…
matiasbenedetto Sep 13, 2024
5a9e2d0
use a ref to reference the current document
matiasbenedetto Sep 13, 2024
27b5e64
currentTheme default to empty object
matiasbenedetto Sep 16, 2024
463fa95
revert changes on useDarkThemeBodyClassName, refactor useEditorFontsR…
matiasbenedetto Sep 18, 2024
9bd3378
revert not needed change
matiasbenedetto Sep 18, 2024
5cecd18
Merge branch 'trunk' into add/editor-font-face-resolver
matiasbenedetto Sep 20, 2024
c262896
Merge branch 'trunk' into add/editor-font-face-resolver
matiasbenedetto Sep 20, 2024
8ced871
try adding currentTheme to the editor settings
matiasbenedetto Sep 20, 2024
ee98b6e
add theme fonts uris
matiasbenedetto Sep 26, 2024
1461989
use _links in the font resolver
matiasbenedetto Sep 27, 2024
8172039
Merge branch 'trunk' into add/editor-font-face-resolver
matiasbenedetto Sep 30, 2024
7cb4c01
Fix PHP linting
getdave Oct 7, 2024
20a00e3
Use correct alias
getdave Oct 7, 2024
f2ad8e7
load the fonts as styles
matiasbenedetto Oct 9, 2024
5a3b172
optional chain
matiasbenedetto Oct 9, 2024
db89bf6
remove default not needed
matiasbenedetto Oct 9, 2024
32868cd
use array_merge instead of +
matiasbenedetto Oct 9, 2024
efd9562
get the theme font uris inside get_resolved_theme_uris function
matiasbenedetto Oct 9, 2024
905f0d8
Merge branch 'trunk' into add/editor-font-face-resolver
matiasbenedetto Oct 9, 2024
e85076a
remove unwanted changes
matiasbenedetto Oct 9, 2024
a1cdf2c
remove unwwanted changes
matiasbenedetto Oct 9, 2024
2250a0c
lint
matiasbenedetto Oct 9, 2024
59ce5a4
lint
matiasbenedetto Oct 9, 2024
d94e2f2
Adding a unit test to cover the font file resolution in WP_Theme_JSON…
ramonjd Oct 11, 2024
607740a
update comment
matiasbenedetto Oct 14, 2024
b69ae26
add function comment
matiasbenedetto Oct 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ public static function get_style_variations_from_directory( $directory, $scope =
* as the value of `_link` object in REST API responses.
*
* @since 6.6.0
* @since 6.7.0 Added support for resolving block styles.
* @since 6.7.0 Added support for resolving block style and font face URIs.
*
* @param WP_Theme_JSON_Gutenberg $theme_json A theme json instance.
* @return array An array of resolved paths.
Expand All @@ -855,6 +855,35 @@ public static function get_resolved_theme_uris( $theme_json ) {
// Using the same file convention when registering web fonts. See: WP_Font_Face_Resolver:: to_theme_file_uri.
$placeholder = 'file:./';

// Add font URIs.
if ( ! empty( $theme_json_data['settings']['typography']['fontFamilies'] ) ) {
$font_families = array_merge(
$theme_json_data['settings']['typography']['fontFamilies']['theme'] ?? array(),
$theme_json_data['settings']['typography']['fontFamilies']['custom'] ?? array(),
$theme_json_data['settings']['typography']['fontFamilies']['default'] ?? array()
);
foreach ( $font_families as $font_family ) {
if ( ! empty( $font_family['fontFace'] ) ) {
foreach ( $font_family['fontFace'] as $font_face ) {
if ( ! empty( $font_face['src'] ) ) {
$sources = is_string( $font_face['src'] )
? array( $font_face['src'] )
: $font_face['src'];
foreach ( $sources as $source ) {
if ( str_starts_with( $source, $placeholder ) ) {
$resolved_theme_uris[] = array(
'name' => $source,
'href' => sanitize_url( get_theme_file_uri( str_replace( $placeholder, '', $source ) ) ),
'target' => "typography.fontFamilies.{$font_family['slug']}.fontFace.src",
);
}
}
}
}
}
}
}

// Top level styles.
$background_image_url = $theme_json_data['styles']['background']['backgroundImage']['url'] ?? null;
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
appendToSelector,
getBlockStyleVariationSelector,
getResolvedValue,
getResolvedThemeFilePath,
} from './utils';
import { getBlockCSSSelector } from './get-block-css-selector';
import { getTypographyFontSizeValue } from './typography-utils';
Expand Down Expand Up @@ -884,6 +885,91 @@ export const toCustomProperties = ( tree, blockSelectors ) => {
return ruleset;
};

/**
* Generates CSS @font-face declarations based on font settings in the theme.json tree.
*
* This function processes font families defined in the theme's typography settings,
* creating @font-face rules for each font face. It handles both file-based and URL-based
* font sources, resolving file paths to URLs when necessary.
*
* @param {Object} tree - The theme.json tree containing typography settings.
* @return {string} A string of CSS @font-face rules for the defined fonts.
*
* @example
* // Example output:
* // @font-face {
* // font-family: "Open Sans";
* // font-style: normal;
* // font-weight: 400;
* // src: url('https://example.com/wp-content/themes/example/assets/fonts/open-sans-regular.woff2') format('woff2');
* // }
*/
const getFontFaceDeclarations = ( tree ) => {
const fonts = tree?.settings?.typography?.fontFamilies;
let ruleset = '';

if ( ! fonts ) {
return ruleset;
}

const themeFileURIs = tree?._links?.[ 'wp:theme-file' ] ?? [];

// Iterate over main origins (theme, custom, default)
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved
for ( const origin in fonts ) {
if ( Array.isArray( fonts[ origin ] ) ) {
fonts[ origin ].forEach( ( font ) => {
if ( font.fontFace ) {
font.fontFace.forEach( ( face ) => {
ruleset += '@font-face {\n';

// Optional properties
for ( const [ key, value ] of Object.entries( face ) ) {
if ( key !== 'src' ) {
ruleset += ` ${ kebabCase(
key
) }: ${ value };\n`;
}
}

if ( face.src ) {
const srcs = (
face.src && Array.isArray( face.src )
? face.src
: [ face.src ]
).map( ( src ) => {
if ( src.startsWith( 'file:' ) ) {
// Convert file path to URL and assume format based on extension
const resolvedSrc =
getResolvedThemeFilePath(
src,
themeFileURIs
);
const format = resolvedSrc
.split( '.' )
.pop();
return `url('${ resolvedSrc.replace(
'file:',
''
) }') format('${ format }')`;
}
// Assume it's already a URL and format is provided
return `url('${ src }')`;
} );
ruleset += ` src: ${ srcs.join(
',\n '
) };\n`;
}

ruleset += '}\n\n';
} );
}
} );
}
}

return ruleset;
};

export const toStyles = (
tree,
blockSelectors,
Expand Down Expand Up @@ -913,6 +999,8 @@ export const toStyles = (

let ruleset = '';

ruleset += getFontFaceDeclarations( tree );
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved

if ( options.presets && ( contentSize || wideSize ) ) {
ruleset += `${ ROOT_CSS_PROPERTIES_SELECTOR } {`;
ruleset = contentSize
Expand Down Expand Up @@ -1399,7 +1487,6 @@ export function useGlobalStylesOutputWithConfig(
} );

const { getBlockStyles } = useSelect( blocksStore );

return useMemo( () => {
if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) {
return [];
Expand All @@ -1415,7 +1502,6 @@ export function useGlobalStylesOutputWithConfig(
updatedConfig,
blockSelectors
);

const globalStyles = toStyles(
updatedConfig,
blockSelectors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Subtitle from '../subtitle';
import Variation from './variation';

export default function TypographyVariations( { title, gap = 2 } ) {
const propertiesToFilter = [ 'typography' ];
const propertiesToFilter = [ 'typography', '_links' ];
const typographyVariations =
useCurrentMergeThemeStyleVariationsWithUserConfig( propertiesToFilter );

Expand Down
50 changes: 48 additions & 2 deletions phpunit/class-wp-theme-json-resolver-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -1286,8 +1286,44 @@ public function test_resolve_theme_file_uris() {
public function test_get_resolved_theme_uris() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
'styles' => array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved
'settings' => array(
'typography' => array(
'fontFamilies' => array(
array(
'fontFace' => array(
array(
'fontFamily' => 'Tocco',
'fontStyle' => 'normal',
'fontWeight' => '400',
'src' => array(
'file:./example/fonts/tocco/tocco-400-normal.woff2',
),
),
),
'fontFamily' => 'Tocco, system-ui',
'name' => 'Tocco',
'slug' => 'secondary',
),
array(
'fontFace' => array(
array(
'fontFamily' => '"Strozzapreti"',
'fontStyle' => 'normal',
'fontWeight' => '400',
'src' => array(
'file:./example/fonts/strozzapreti/strozzapreti-400-normal.woff2',
),
),
),
'fontFamily' => '"Strozzapreti", cursive',
'name' => 'Strozzapreti',
'slug' => 'primary',
),
),
),
),
'styles' => array(
'background' => array(
'backgroundImage' => array(
'url' => 'file:./example/img/image.png',
Expand All @@ -1314,6 +1350,16 @@ public function test_get_resolved_theme_uris() {
);

$expected_data = array(
array(
'name' => 'file:./example/fonts/tocco/tocco-400-normal.woff2',
'href' => 'https://example.org/wp-content/themes/example-theme/example/fonts/tocco/tocco-400-normal.woff2',
'target' => 'typography.fontFamilies.secondary.fontFace.src',
),
array(
'name' => 'file:./example/fonts/strozzapreti/strozzapreti-400-normal.woff2',
'href' => 'https://example.org/wp-content/themes/example-theme/example/fonts/strozzapreti/strozzapreti-400-normal.woff2',
'target' => 'typography.fontFamilies.primary.fontFace.src',
),
array(
'name' => 'file:./example/img/image.png',
'href' => 'https://example.org/wp-content/themes/example-theme/example/img/image.png',
Expand Down
Loading