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

Font Library #5285

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
9a00814
add main backend files
matiasbenedetto Sep 22, 2023
6f9aefc
add comment about CDN link
matiasbenedetto Sep 22, 2023
d78d99b
load main files
matiasbenedetto Sep 22, 2023
1d74144
add routes from font library to routes test
matiasbenedetto Sep 22, 2023
42eeb48
adding PHP unit tests for font library
matiasbenedetto Sep 22, 2023
9e06a93
fix register post type for font family post type
matiasbenedetto Sep 22, 2023
b323b67
move the font library functions
matiasbenedetto Sep 22, 2023
715eea9
avoid loading file deleted
matiasbenedetto Sep 22, 2023
e54426a
add wp_register_default_font_collection function
matiasbenedetto Sep 22, 2023
1078767
load font-library functions file
matiasbenedetto Sep 22, 2023
699f45b
register default font collection with init hook
matiasbenedetto Sep 22, 2023
a6dca33
fix rest-schema test
matiasbenedetto Sep 22, 2023
9ba9187
Merge branch 'trunk' into port/font-library
matiasbenedetto Sep 23, 2023
5084180
settings the right 'supports' values for wp_font_family post type
matiasbenedetto Sep 23, 2023
46a1e9f
set hierarchical as false for wp_font_family post type
matiasbenedetto Sep 23, 2023
7e666e6
fix url in test
matiasbenedetto Sep 23, 2023
e6cce43
fix set_upload_dir unit test
matiasbenedetto Sep 23, 2023
673c042
fix unit test by using solvable host name
matiasbenedetto Sep 23, 2023
72bbec5
avoid using gutenberg class, use core class instead
matiasbenedetto Sep 23, 2023
1d8de84
add the font assets for the tests
matiasbenedetto Sep 23, 2023
d769cc5
Remove font files created after tests run
matiasbenedetto Sep 25, 2023
c4a31d2
removing unwanted echo line
matiasbenedetto Sep 25, 2023
57f05a1
Merge branch 'trunk' into port/font-library
matiasbenedetto Sep 25, 2023
8933cb8
add the custom post type routes to the routes test
matiasbenedetto Sep 25, 2023
9aeb0ba
Removing class re-declaration check
matiasbenedetto Sep 25, 2023
1725825
format php
matiasbenedetto Sep 25, 2023
d4bce59
updating wp-api-generated.js schema
matiasbenedetto Sep 25, 2023
89e888f
Merge branch 'trunk' into port/font-library
matiasbenedetto Sep 25, 2023
ab70608
Revert "updating wp-api-generated.js schema"
matiasbenedetto Sep 25, 2023
739edeb
moving font library rest controller to the endpoints folder
matiasbenedetto Sep 25, 2023
534264e
Removing 'gutenberg' translation domain
matiasbenedetto Sep 25, 2023
c2d38d6
add comment about mime types logic
matiasbenedetto Sep 25, 2023
2a715cd
using wordpress.org cdn to host google fonts collection data json file
matiasbenedetto Sep 25, 2023
6bf7be3
updating wp-api-generated.js fixtures
matiasbenedetto Sep 25, 2023
bbb09e9
avoid deprecation error in test
matiasbenedetto Sep 25, 2023
654dd1d
Moving default font collection registration to font.php file
matiasbenedetto Sep 25, 2023
635d870
moving font library files to the main fonts folder
matiasbenedetto Sep 25, 2023
8714206
remove path from deleted file
matiasbenedetto Sep 25, 2023
2e08b53
Merge branch 'trunk' into port/font-library
matiasbenedetto Sep 25, 2023
8c5ddec
replace strpos by str_contains
matiasbenedetto Sep 26, 2023
ae15e18
Merge branch 'trunk' into port/font-library
matiasbenedetto Sep 27, 2023
04fe719
Testeable mime type conditionals
matiasbenedetto Sep 27, 2023
839ff70
Change expected OTF files mime type
matiasbenedetto Sep 28, 2023
5116443
standardize the output of install and uninstall endpoints
matiasbenedetto Sep 29, 2023
d89e201
update install and uninstall enpoints tests for the new standard outp…
matiasbenedetto Sep 29, 2023
e4b2a85
remove class_exist check coming from gutenberg
matiasbenedetto Oct 2, 2023
8e3e711
remove unused line
matiasbenedetto Oct 2, 2023
947e2cb
add trailing dot
matiasbenedetto Oct 2, 2023
88ec9b7
se snake_case instead of camelCase on fontFamilies endpoint param
matiasbenedetto Oct 2, 2023
3e63080
Merge branch 'trunk' into port/font-library
matiasbenedetto Oct 2, 2023
e218ec8
using DIR_TESTDATA for file assets paths
matiasbenedetto Oct 2, 2023
02d6767
Remove extra new line
matiasbenedetto Oct 2, 2023
641e9f3
emoving an unnecessary line break
matiasbenedetto Oct 2, 2023
5178b1e
use self::factory() to insert post in test
matiasbenedetto Oct 2, 2023
4612703
avoid adding variable name in return comment
matiasbenedetto Oct 2, 2023
f69e6e5
update return type of function
matiasbenedetto Oct 2, 2023
17c9fd3
update comments
matiasbenedetto Oct 2, 2023
3021090
move property definition on top of the class
matiasbenedetto Oct 2, 2023
db11c1f
replace EDITABLE by CREATABLE
matiasbenedetto Oct 2, 2023
31af894
move uninstall endpoint schema
matiasbenedetto Oct 2, 2023
21e3d33
updating wp-api-generated.js file
matiasbenedetto Oct 2, 2023
9aa43bf
Rename function
matiasbenedetto Oct 2, 2023
f38bb59
make string translatable
matiasbenedetto Oct 2, 2023
b005321
Add id argument to the font collection.
anton-vlasenko Oct 2, 2023
2b29de4
remove triling comma
matiasbenedetto Oct 2, 2023
aaf3741
change WP_REST_Response by rest_ensure_response
matiasbenedetto Oct 2, 2023
d6534cc
Remove path_join() for constant in tests
hellofromtonya Oct 2, 2023
3b01b46
Change subpackage to Fonts
hellofromtonya Oct 2, 2023
f16ead0
Initialize properties to empty array
hellofromtonya Oct 2, 2023
30369c6
Empty line before end of function return.
hellofromtonya Oct 2, 2023
e1f0b58
1. Implement schema.
anton-vlasenko Oct 2, 2023
aec6c93
Removes passing PHP version to WP_Font_Library::get_font_mime_types().
hellofromtonya Oct 2, 2023
b34fe52
Implement the schema for the font collections endpoint.
anton-vlasenko Oct 2, 2023
e948c1b
Multiline comments props costdev
hellofromtonya Oct 2, 2023
0926426
Update wp-api-generated.js.
anton-vlasenko Oct 2, 2023
11b26db
PHPCS: Remove extra empty line at end of file
hellofromtonya Oct 2, 2023
feb1199
Remove isset() where empty() is also used. Prop costdev
hellofromtonya Oct 2, 2023
a776cee
Use $request['id'] - props spacedmonkey
hellofromtonya Oct 3, 2023
909da30
make install and uninstall endpoints singular.
matiasbenedetto Oct 3, 2023
fd706d1
rename font library enpoints and split font library controller in 2 d…
matiasbenedetto Oct 3, 2023
7081941
remove unwanted enpoint from WP_REST_Font_Families_Controller class
matiasbenedetto Oct 4, 2023
b49da9a
dont show custom post type in rest endpoints
matiasbenedetto Oct 4, 2023
2d369aa
use lowercase in error message
matiasbenedetto Oct 4, 2023
86dab9e
update wording
matiasbenedetto Oct 4, 2023
b167d7f
Implementing get_item and get_items for /font-families/ endpoints
matiasbenedetto Oct 4, 2023
1c23f09
Refactored font-family GET and DELETE api endpoints to use SLUG inste…
pbking Oct 4, 2023
4f7fd91
Added or refactored tests for Font Family createItem, deleteItem, get…
pbking Oct 4, 2023
14ca3bd
Refactored Font Family tests to use getPostBySlug
pbking Oct 4, 2023
8d9f496
use snake_case schema in font collections endpoints
matiasbenedetto Oct 4, 2023
1243b31
update api endpoint routes test and fixture
matiasbenedetto Oct 4, 2023
c5c6ec4
fix the font collections schema to accomodate arrays instead of a sin…
matiasbenedetto Oct 4, 2023
f398761
1. Remove the registerRoutes test.
anton-vlasenko Oct 4, 2023
d85e545
Fix CS.
anton-vlasenko Oct 4, 2023
07d01ff
Fix CS.
anton-vlasenko Oct 4, 2023
ec52113
grouping font-families endpoints and add response schemas.
matiasbenedetto Oct 5, 2023
3967726
add default order for font families
matiasbenedetto Oct 5, 2023
a19ac36
add since 6.4 comment
matiasbenedetto Oct 5, 2023
05d3ff7
Remove extra new line
matiasbenedetto Oct 5, 2023
963d226
remove extra spaces
matiasbenedetto Oct 5, 2023
e28134f
add missing 'since 6.4' comment
matiasbenedetto Oct 5, 2023
b2a96fc
Get rid of WP_REST_Font_Library_Controller_UnitTestCase.
anton-vlasenko Oct 5, 2023
640159e
Fix parent class name.
anton-vlasenko Oct 5, 2023
d44da25
fix typo in comment
matiasbenedetto Oct 5, 2023
603ee9e
Rename controller methods.
anton-vlasenko Oct 5, 2023
7382406
Fix 404 errors.
anton-vlasenko Oct 5, 2023
d9ffea9
Move the tests to Tests_Fonts_WpRestFontCollectionsController.
anton-vlasenko Oct 5, 2023
9edfb39
Fix the tests.
anton-vlasenko Oct 5, 2023
c4de43f
Improve PHPDOC blocks.
anton-vlasenko Oct 5, 2023
9deeb4a
Fix @return type.
anton-vlasenko Oct 5, 2023
6c9e856
Implement the test for Tests_Fonts_WpRestFontCollectionsController::t…
anton-vlasenko Oct 9, 2023
89cd000
Fix the test.
anton-vlasenko Oct 9, 2023
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
3 changes: 3 additions & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -735,4 +735,7 @@
// Font management.
add_action( 'wp_head', 'wp_print_font_faces', 50 );

// Font Library.
add_action( 'init', 'wp_register_default_font_collection' );

unset( $filter, $action );
34 changes: 34 additions & 0 deletions src/wp-includes/fonts.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,37 @@ function wp_print_font_faces( $fonts = array() ) {
$wp_font_face = new WP_Font_Face();
$wp_font_face->generate_and_print( $fonts );
}

/**
* Registers a new Font Collection in the Font Library.
*
* @since 6.4.0
*
* @param string[] $config {
* Font collection associative array of configuration options.
*
* @type string $id The font collection's unique ID.
* @type string $src The font collection's data JSON file.
* }
* @return WP_Font_Collection|WP_Error A font collection is it was registered
* successfully, else WP_Error.
*/
function wp_register_font_collection( $config ) {
return WP_Font_Library::register_font_collection( $config );
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do a unregister a font collection.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not currently possible but that work is already started: WordPress/gutenberg#54701

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Registers the default fonts collection for the Font Library.
*
* @since 6.4.0
*/
function wp_register_default_font_collection() {
wp_register_font_collection(
array(
'id' => 'default-font-collection',
'name' => 'Google Fonts',
'description' => __( 'Add from Google Fonts. Fonts are copied to and served from your site.' ),
'src' => 'https://s.w.org/images/fonts/16.7/collections/google-fonts-with-preview.json',
)
);
}
109 changes: 109 additions & 0 deletions src/wp-includes/fonts/class-wp-font-collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* Font Collection class.
*
* This file contains the Font Collection class definition.
*
* @package WordPress
* @subpackage Fonts
* @since 6.4.0
*/

/**
* Font Collection class.
*
* @since 6.4.0
*/
class WP_Font_Collection {

/**
* Font collection configuration.
*
* @since 6.4.0
*
* @var array
*/
private $config = array();

/**
* WP_Font_Collection constructor.
*
* @since 6.4.0
*
* @param array $config Font collection config options.
* See {@see wp_register_font_collection()} for the supported fields.
* @throws Exception If the required parameters are missing.
*/
public function __construct( $config ) {
if ( empty( $config ) || ! is_array( $config ) ) {
throw new Exception( 'Font Collection config options is required as a non-empty array.' );
}

if ( empty( $config['id'] ) || ! is_string( $config['id'] ) ) {
throw new Exception( 'Font Collection config ID is required as a non-empty string.' );
}

if ( empty( $config['name'] ) || ! is_string( $config['name'] ) ) {
throw new Exception( 'Font Collection config name is required as a non-empty string.' );
}

if ( empty( $config['src'] ) || ! is_string( $config['src'] ) ) {
throw new Exception( 'Font Collection config "src" option is required as a non-empty string.' );
}

$this->config = $config;
}

/**
* Gets the font collection config.
*
* @since 6.4.0
*
* @return array An array containing the font collection config.
*/
public function get_config() {
return $this->config;
}

/**
* Gets the font collection data.
*
* @since 6.4.0
*
* @return array|WP_Error An array containing the list of font families in theme.json format on success,
* else an instance of WP_Error on failure.
*/
public function get_data() {
// If the src is a URL, fetch the data from the URL.
if ( str_contains( $this->config['src'], 'http' ) && str_contains( $this->config['src'], '://' ) ) {
if ( ! wp_http_validate_url( $this->config['src'] ) ) {
return new WP_Error( 'font_collection_read_error', __( 'Invalid URL for font collection data.' ) );
}

$response = wp_remote_get( $this->config['src'] );
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
return new WP_Error( 'font_collection_read_error', __( 'Error fetching the font collection data from a URL.' ) );
}

$data = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $data ) ) {
return new WP_Error( 'font_collection_read_error', __( 'Error decoding the font collection data from the REST response JSON.' ) );
}
// If the src is a file path, read the data from the file.
} else {
if ( ! file_exists( $this->config['src'] ) ) {
return new WP_Error( 'font_collection_read_error', __( 'Font collection data JSON file does not exist.' ) );
}
$data = wp_json_file_decode( $this->config['src'], array( 'associative' => true ) );
if ( empty( $data ) ) {
return new WP_Error( 'font_collection_read_error', __( 'Error reading the font collection data JSON file contents.' ) );
}
}

$collection_data = $this->get_config();
$collection_data['data'] = $data;
unset( $collection_data['src'] );

return $collection_data;
}
}
89 changes: 89 additions & 0 deletions src/wp-includes/fonts/class-wp-font-family-utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
/**
* Font Family Utils class.
*
* This file contains utils for Font Family class.
*
* @package WordPress
* @subpackage Fonts
* @since 6.4.0
*/

/**
* A class of utilities for working with the Font Library.
*
* @since 6.4.0
*/
class WP_Font_Family_Utils {

/**
* Generates a filename for a font face asset.
*
* Creates a filename for a font face asset using font family, style, weight and
* extension information.
*
* @since 6.4.0
*
* @param string $font_slug The font slug to use in the filename.
* @param array $font_face The font face array containing 'fontFamily', 'fontStyle', and
* 'fontWeight' attributes.
* @param string $url The URL of the font face asset, used to derive the file extension.
* @param string $suffix Optional. The suffix added to the resulting filename. Default empty string.
* @return string The generated filename for the font face asset.
*/
public static function get_filename_from_font_face( $font_slug, $font_face, $url, $suffix = '' ) {
$extension = pathinfo( $url, PATHINFO_EXTENSION );
$filename = "{$font_slug}_{$font_face['fontStyle']}_{$font_face['fontWeight']}";
if ( '' !== $suffix ) {
$filename .= "_{$suffix}";
}

return sanitize_file_name( "{$filename}.{$extension}" );
}

/**
* Merges two fonts and their font faces.
*
* @since 6.4.0
*
* @param array $font1 The first font to merge.
* @param array $font2 The second font to merge.
* @return array|WP_Error The merged font or WP_Error if the fonts have different slugs.
*/
public static function merge_fonts_data( $font1, $font2 ) {
if ( $font1['slug'] !== $font2['slug'] ) {
return new WP_Error(
'fonts_must_have_same_slug',
__( 'Fonts must have the same slug to be merged.' )
);
}

$font_faces_1 = isset( $font1['fontFace'] ) ? $font1['fontFace'] : array();
$font_faces_2 = isset( $font2['fontFace'] ) ? $font2['fontFace'] : array();
$merged_font_faces = array_merge( $font_faces_1, $font_faces_2 );

$serialized_faces = array_map( 'serialize', $merged_font_faces );
$unique_serialized_faces = array_unique( $serialized_faces );
$unique_faces = array_map( 'unserialize', $unique_serialized_faces );
Comment on lines +65 to +67
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seralization can be expensive compute wise. Was json_decode considered?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this looks like a very JS-esque kinda way to make an array unique. Nested foreach loops or something would be more performant. Or if fonts are actual class instances instead of arrays, array_unique() + __toString() could be used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is JSON data, just serializing like this is incorrect since it doesn't follow JSON semantics regarding key order. We have rest_stabilize_value in Core for that.

Is this function called in a hot-path? I.e. does it happen on most page loads? Or is this something called during the ingestion(?) of a font, i.e. once. If it is in a hot-path I agree this should be done more performantly. If this only happens during the initial download, then I think serializing is fine, that's how we validate uniqueness in the REST API, see rest_validate_array_contains_unique_items.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s this function called in a hot-path? I.e. does it happen on most page loads? Or is this something called during the ingestion(?) of a font, i.e. once.

This is called only when a new font is installed and NOT on every page load.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no references to serialize in the current Gutenberg trunk files for the Font Library feature.

https://github.com/WordPress/gutenberg/tree/trunk/lib/compat/wordpress-6.5/fonts

Some "merging" happens here but the code has evolved significantly.


$merged_font = array_merge( $font1, $font2 );
$merged_font['fontFace'] = array_values( $unique_faces );

return $merged_font;
}

/**
* Returns whether the given file has a font MIME type.
*
* @since 6.4.0
*
* @param string $filepath The file to check.
* @return bool True if the file has a font MIME type, false otherwise.
*/
public static function has_font_mime_type( $filepath ) {
$allowed_mime_types = WP_Font_Library::get_font_mime_types();
$filetype = wp_check_filetype( $filepath, $allowed_mime_types );

return in_array( $filetype['type'], $allowed_mime_types, true );
}
}
Loading
Loading