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

Open
wants to merge 33 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 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
50 changes: 50 additions & 0 deletions lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,50 @@ public static function get_style_variations_from_directory( $directory, $scope =
return $variations;
}

/**
* Resolves relative paths in theme.json typography to theme absolute paths
* and returns them in an array that can be embedded
* as the value of `_link` object in REST API responses.
*
* @since 6.6.0
*
* @param
* @return array An array of resolved paths.
*/
private static function get_resolved_fonts_theme_uris( $theme_json_data ) {
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved
$resolved_theme_uris = array();

if ( ! empty( $theme_json_data['settings']['typography']['fontFamilies'] ) ) {

$font_families = ( $theme_json_data['settings']['typography']['fontFamilies']['theme'] ?? array() )
+ ( $theme_json_data['settings']['typography']['fontFamilies']['custom'] ?? array() )
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved
+ ( $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, 'file:' ) ) {
$resolved_theme_uris[] = array(
'name' => $source,
'href' => sanitize_url( get_theme_file_uri( str_replace( 'file:./', '', $source ) ) ),
'target' => "typography.fontFamilies.{$font_family['slug']}.fontFace.src",
);
}
}
}
}
}
}
}

return $resolved_theme_uris;
}


/**
* Resolves relative paths in theme.json styles to theme absolute paths
Expand All @@ -852,6 +896,12 @@ public static function get_resolved_theme_uris( $theme_json ) {

$theme_json_data = $theme_json->get_raw_data();

// Add font URIs.
$resolved_theme_uris = array_merge(
$resolved_theme_uris,
static::get_resolved_fonts_theme_uris( $theme_json_data )
);

// Using the same file convention when registering web fonts. See: WP_Font_Face_Resolver:: to_theme_file_uri.
$placeholder = 'file:./';

Expand Down
4 changes: 4 additions & 0 deletions packages/block-editor/src/components/editor-styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useSelect } from '@wordpress/data';
import transformStyles from '../../utils/transform-styles';
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import useEditorFontsResolver from '../use-editor-fonts-resolver';

extend( [ namesPlugin, a11yPlugin ] );

Expand Down Expand Up @@ -105,6 +106,9 @@ function EditorStyles( { styles, scope, transformOptions } ) {
<style
ref={ useDarkThemeBodyClassName( transformedStyles, scope ) }
/>

<style ref={ useEditorFontsResolver() } />

{ transformedStyles.map( ( css, index ) => (
<style key={ index }>{ css }</style>
) ) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* WordPress dependencies
*/
import { useState, useMemo, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { loadFontFaceInBrowser } from './utils';
import { store as blockEditorStore } from '../../store';
import { globalStylesLinksDataKey } from '../../store/private-keys';

function resolveThemeFontFaceSrc( src, _links ) {
const firstSrc = Array.isArray( src ) ? src[ 0 ] : src;
return _links.find( ( link ) => link.name === firstSrc )?.href || firstSrc;
}

function useEditorFontsResolver() {
const [ loadedFontUrls, setLoadedFontUrls ] = useState( new Set() );

const { _links = [], fontFamilies = [] } = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
const _settings = getSettings();
return {
_links: _settings[ globalStylesLinksDataKey ]?.[ 'wp:theme-file' ],
fontFamilies:
_settings?.__experimentalFeatures?.typography?.fontFamilies,
};
}, [] );

// eslint-disable-next-line no-console
console.log( 'fontFamilies', fontFamilies, 'links', _links );

const fontFaces = useMemo( () => {
return Object.values( fontFamilies )
.flat()
.map( ( family ) => family.fontFace )
.filter( Boolean )
.flat();
}, [ fontFamilies ] );

const loadFontFaceAsset = useCallback(
async ( fontFace, ownerDocument ) => {
if ( ! fontFace.src ) {
return;
}

const src = resolveThemeFontFaceSrc( fontFace.src, _links );
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved

if ( ! src || loadedFontUrls.has( src ) ) {
return;
}

loadFontFaceInBrowser( fontFace, src, ownerDocument );
setLoadedFontUrls( ( prevUrls ) => new Set( prevUrls ).add( src ) );
},
[ loadedFontUrls, _links ]
);

return useCallback(
( node ) => {
if ( ! node ) {
return;
}

const { ownerDocument } = node;
fontFaces.forEach( ( fontFace ) =>
loadFontFaceAsset( fontFace, ownerDocument )
);
},
[ fontFaces, loadFontFaceAsset ]
);
}

export default useEditorFontsResolver;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Format the font face name to use in the font-family property of a font face.
*
* The input can be a string with the font face name or a string with multiple font face names separated by commas.
* It removes the leading and trailing quotes from the font face name.
*
* @param {string} input - The font face name.
* @return {string} The formatted font face name.
*
* Example:
* formatFontFaceName("Open Sans") => "Open Sans"
* formatFontFaceName("'Open Sans', sans-serif") => "Open Sans"
* formatFontFaceName(", 'Open Sans', 'Helvetica Neue', sans-serif") => "Open Sans"
*/
function formatFontFaceName( input ) {
if ( ! input ) {
return '';
}

let output = input.trim();
if ( output.includes( ',' ) ) {
output = output
.split( ',' )
// Finds the first item that is not an empty string.
.find( ( item ) => item.trim() !== '' )
.trim();
}
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved
// Removes leading and trailing quotes.
output = output.replace( /^["']|["']$/g, '' );

// Firefox needs the font name to be wrapped in double quotes meanwhile other browsers don't.
if ( window.navigator.userAgent.toLowerCase().includes( 'firefox' ) ) {
output = `"${ output }"`;
}
matiasbenedetto marked this conversation as resolved.
Show resolved Hide resolved
return output;
}

/*
* Loads the font face from a URL and adds it to the browser.
* It also adds it to the iframe document.
*/
export async function loadFontFaceInBrowser( fontFace, source, documentRef ) {
if ( typeof source !== 'string' ) {
return;
}
const dataSource = `url(${ source })`;
const newFont = new window.FontFace(
formatFontFaceName( fontFace.fontFamily ),
dataSource,
{
style: fontFace.fontStyle,
weight: fontFace.fontWeight,
}
);

const loadedFace = await newFont.load();

// Add the font to the ref document.
documentRef.fonts.add( loadedFace );

// Add the font to the window document.
if ( documentRef !== window.document ) {
window.document.fonts.add( loadedFace );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
import { store as coreDataStore } from '@wordpress/core-data';

/**
* Internal dependencies
Expand All @@ -15,12 +16,18 @@ import { TEMPLATE_POST_TYPE } from '../../utils/constants';
const { useGlobalStylesOutput } = unlock( blockEditorPrivateApis );

function useGlobalStylesRenderer() {
const postType = useSelect( ( select ) => {
return select( editSiteStore ).getEditedPostType();
const { postType, currentTheme } = useSelect( ( select ) => {
return {
postType: select( editSiteStore ).getEditedPostType(),
currentTheme: select( coreDataStore ).getCurrentTheme(),
};
} );
const [ styles, settings ] = useGlobalStylesOutput(
postType !== TEMPLATE_POST_TYPE
);

settings.currentTheme = currentTheme;

const { getSettings } = useSelect( editSiteStore );
const { updateSettings } = useDispatch( editSiteStore );

Expand Down
Loading