PluginProbe
Gutenberg / 13.7.2
Gutenberg v13.7.2
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / experimental / class-wp-webfonts.php

class-wp-webfonts.php in Gutenberg 13.7.2, at lib/experimental/class-wp-webfonts.php

418 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Webfonts API class.
4 *
5 * @package WordPress
6 * @subpackage WebFonts
7 * @since 6.0.0
8 */
9
10 if ( class_exists( 'WP_Webfonts' ) ) {
11 return;
12 }
13
14 /**
15 * Class WP_Webfonts
16 *
17 * @since 6.0.0
18 */
19 class WP_Webfonts {
20
21 /**
22 * An array of registered webfonts.
23 *
24 * @since 6.0.0
25 *
26 * @var array[]
27 */
28 private $registered_webfonts = array();
29
30 /**
31 * An array of enqueued webfonts.
32 *
33 * @var array[]
34 */
35 private $enqueued_webfonts = array();
36
37 /**
38 * An array of registered providers.
39 *
40 * @since 6.0.0
41 *
42 * @var array
43 */
44 private $providers = array();
45
46 /**
47 * Stylesheet handle.
48 *
49 * @since 6.0.0
50 *
51 * @var string
52 */
53 private $stylesheet_handle = '';
54
55 /**
56 * Init.
57 *
58 * @since 6.0.0
59 */
60 public function init() {
61 // Register default providers.
62 $this->register_provider( 'local', 'WP_Webfonts_Provider_Local' );
63
64 // Register callback to generate and enqueue styles.
65 if ( did_action( 'wp_enqueue_scripts' ) ) {
66 $this->stylesheet_handle = 'webfonts-footer';
67 $hook = 'wp_print_footer_scripts';
68 } else {
69 $this->stylesheet_handle = 'webfonts';
70 $hook = 'wp_enqueue_scripts';
71 }
72 add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) );
73
74 // Enqueue webfonts in the block editor.
75 add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) );
76 }
77
78 /**
79 * Get the list of registered fonts.
80 *
81 * @since 6.0.0
82 *
83 * @return array[]
84 */
85 public function get_registered_webfonts() {
86 return $this->registered_webfonts;
87 }
88
89 /**
90 * Get the list of enqueued fonts.
91 *
92 * @return array[]
93 */
94 public function get_enqueued_webfonts() {
95 return $this->enqueued_webfonts;
96 }
97
98 /**
99 * Get the list of all fonts.
100 *
101 * @return array[]
102 */
103 public function get_all_webfonts() {
104 return array_merge( $this->get_registered_webfonts(), $this->get_enqueued_webfonts() );
105 }
106
107 /**
108 * Get the list of providers.
109 *
110 * @since 6.0.0
111 *
112 * @return WP_Webfonts_Provider[] All registered providers, each keyed by their unique ID.
113 */
114 public function get_providers() {
115 return $this->providers;
116 }
117
118 /**
119 * Register a webfont.
120 *
121 * @since 6.0.0
122 *
123 * @param array $webfont Webfont to be registered.
124 * @return string|false The font family slug if successfully registered, else false.
125 */
126 public function register_webfont( array $webfont ) {
127 $webfont = $this->validate_webfont( $webfont );
128
129 // If not valid, bail out.
130 if ( ! $webfont ) {
131 return false;
132 }
133
134 $slug = $this->get_font_slug( $webfont );
135
136 // Initialize a new font-family collection.
137 if ( ! isset( $this->registered_webfonts[ $slug ] ) ) {
138 $this->registered_webfonts[ $slug ] = array();
139 }
140
141 $this->registered_webfonts[ $slug ][] = $webfont;
142 return $slug;
143 }
144
145 /**
146 * Enqueue a font-family that has been already registered.
147 *
148 * @param string $font_family_name The font family name to be enqueued.
149 * @return bool True if successfully enqueued, else false.
150 */
151 public function enqueue_webfont( $font_family_name ) {
152 $slug = $this->get_font_slug( $font_family_name );
153
154 if ( isset( $this->enqueued_webfonts[ $slug ] ) ) {
155 return true;
156 }
157
158 if ( ! isset( $this->registered_webfonts[ $slug ] ) ) {
159 /* translators: %s unique slug to identify the font family of the webfont */
160 _doing_it_wrong( __METHOD__, sprintf( __( 'The "%s" font family is not registered.', 'gutenberg' ), $slug ), '6.0.0' );
161
162 return false;
163 }
164
165 $this->enqueued_webfonts[ $slug ] = $this->registered_webfonts[ $slug ];
166 unset( $this->registered_webfonts[ $slug ] );
167 return true;
168 }
169
170 /**
171 * Get the font slug.
172 *
173 * @since 6.0.0
174 *
175 * @param array|string $to_convert The value to convert into a slug. Expected as the web font's array
176 * or a font-family as a string.
177 * @return string|false The font slug on success, or false if the font-family cannot be determined.
178 */
179 public static function get_font_slug( $to_convert ) {
180 if ( is_array( $to_convert ) ) {
181 if ( isset( $to_convert['font-family'] ) ) {
182 $to_convert = $to_convert['font-family'];
183 } elseif ( isset( $to_convert['fontFamily'] ) ) {
184 $to_convert = $to_convert['fontFamily'];
185 } else {
186 _doing_it_wrong( __METHOD__, __( 'Could not determine the font family name.', 'gutenberg' ), '6.0.0' );
187 return false;
188 }
189 }
190
191 return sanitize_title( $to_convert );
192 }
193
194 /**
195 * Validate a webfont.
196 *
197 * @since 6.0.0
198 *
199 * @param array $webfont The webfont arguments.
200 *
201 * @return array|false The validated webfont arguments, or false if the webfont is invalid.
202 */
203 public function validate_webfont( $webfont ) {
204 $webfont = wp_parse_args(
205 $webfont,
206 array(
207 'provider' => 'local',
208 'font-family' => '',
209 'font-style' => 'normal',
210 'font-weight' => '400',
211 'font-display' => 'fallback',
212 )
213 );
214
215 // Check the font-family.
216 if ( empty( $webfont['font-family'] ) || ! is_string( $webfont['font-family'] ) ) {
217 trigger_error( __( 'Webfont font family must be a non-empty string.', 'gutenberg' ) );
218 return false;
219 }
220
221 // Local fonts need a "src".
222 if ( 'local' === $webfont['provider'] ) {
223 // Make sure that local fonts have 'src' defined.
224 if ( empty( $webfont['src'] ) || ( ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) ) ) {
225 trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.', 'gutenberg' ) );
226 return false;
227 }
228 }
229
230 // Validate the 'src' property.
231 if ( ! empty( $webfont['src'] ) ) {
232 foreach ( (array) $webfont['src'] as $src ) {
233 if ( empty( $src ) || ! is_string( $src ) ) {
234 trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) );
235 return false;
236 }
237 }
238 }
239
240 // Check the font-weight.
241 if ( ! is_string( $webfont['font-weight'] ) && ! is_int( $webfont['font-weight'] ) ) {
242 trigger_error( __( 'Webfont font weight must be a properly formatted string or integer.', 'gutenberg' ) );
243 return false;
244 }
245
246 // Check the font-display.
247 if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) {
248 $webfont['font-display'] = 'fallback';
249 }
250
251 $valid_props = array(
252 'ascend-override',
253 'descend-override',
254 'font-display',
255 'font-family',
256 'font-stretch',
257 'font-style',
258 'font-weight',
259 'font-variant',
260 'font-feature-settings',
261 'font-variation-settings',
262 'line-gap-override',
263 'size-adjust',
264 'src',
265 'unicode-range',
266
267 // Exceptions.
268 'provider',
269 );
270
271 foreach ( $webfont as $prop => $value ) {
272 if ( ! in_array( $prop, $valid_props, true ) ) {
273 unset( $webfont[ $prop ] );
274 }
275 }
276
277 return $webfont;
278 }
279
280 /**
281 * Register a provider.
282 *
283 * @since 6.0.0
284 *
285 * @param string $provider The provider name.
286 * @param string $class The provider class name.
287 * @return bool True if successfully registered, else false.
288 */
289 public function register_provider( $provider, $class ) {
290 if ( empty( $provider ) || empty( $class ) ) {
291 return false;
292 }
293 $this->providers[ $provider ] = $class;
294 return true;
295 }
296
297 /**
298 * Generate and enqueue webfonts styles.
299 *
300 * @since 6.0.0
301 */
302 public function generate_and_enqueue_styles() {
303 // Generate the styles.
304 $webfonts = $this->get_webfonts_by_provider( $this->get_enqueued_webfonts() );
305 $styles = $this->generate_styles( $webfonts );
306
307 // Bail out if there are no styles to enqueue.
308 if ( '' === $styles ) {
309 return;
310 }
311
312 // Enqueue the stylesheet.
313 wp_register_style( $this->stylesheet_handle, '' );
314 wp_enqueue_style( $this->stylesheet_handle );
315
316 // Add the styles to the stylesheet.
317 wp_add_inline_style( $this->stylesheet_handle, $styles );
318 }
319
320 /**
321 * Generate and enqueue editor styles.
322 *
323 * @since 6.0.0
324 */
325 public function generate_and_enqueue_editor_styles() {
326 // Generate the styles.
327 $webfonts = $this->get_webfonts_by_provider( $this->get_all_webfonts() );
328 $styles = $this->generate_styles( $webfonts );
329
330 // Bail out if there are no styles to enqueue.
331 if ( '' === $styles ) {
332 return;
333 }
334
335 wp_enqueue_style( 'wp-block-library' );
336 wp_add_inline_style( 'wp-block-library', $styles );
337 }
338
339 /**
340 * Generate styles for webfonts.
341 *
342 * @since 6.0.0
343 *
344 * @param array[] $webfonts_by_provider Webfonts organized by provider.
345 * @return string $styles Generated styles.
346 */
347 private function generate_styles( array $webfonts_by_provider ) {
348 $styles = '';
349 $providers = $this->get_providers();
350
351 /*
352 * Loop through each of the providers to get the CSS for their respective webfonts
353 * to incrementally generate the collective styles for all of them.
354 */
355 foreach ( $providers as $provider_id => $provider_class ) {
356
357 // Bail out if the provider class does not exist.
358 if ( ! class_exists( $provider_class ) ) {
359 /* translators: %s is the provider name. */
360 trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider_id ) );
361 continue;
362 }
363
364 $provider_webfonts = isset( $webfonts_by_provider[ $provider_id ] )
365 ? $webfonts_by_provider[ $provider_id ]
366 : array();
367
368 // If there are no registered webfonts for this provider, skip it.
369 if ( empty( $provider_webfonts ) ) {
370 continue;
371 }
372
373 /*
374 * Process the webfonts by first passing them to the provider via `set_webfonts()`
375 * and then getting the CSS from the provider.
376 */
377 $provider = new $provider_class();
378 $provider->set_webfonts( $provider_webfonts );
379 $styles .= $provider->get_css();
380 }
381
382 return $styles;
383 }
384
385
386 /**
387 * Reorganizes webfonts grouped by font-family into grouped by provider.
388 *
389 * @param array[] $font_families Font families and each of their webfonts.
390 * @return array[] Webfonts organized by providers.
391 */
392 private function get_webfonts_by_provider( array $font_families ) {
393 $providers = $this->get_providers();
394 $webfonts_by_provider = array();
395
396 foreach ( $font_families as $webfonts ) {
397 foreach ( $webfonts as $webfont ) {
398 $provider = $webfont['provider'];
399
400 // Skip if the provider is not registered.
401 if ( ! isset( $providers[ $provider ] ) ) {
402 /* translators: %s is the provider name. */
403 trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider ) );
404 continue;
405 }
406
407 // Initialize a new provider collection.
408 if ( ! isset( $webfonts_by_provider[ $provider ] ) ) {
409 $webfonts_by_provider[ $provider ] = array();
410 }
411 $webfonts_by_provider[ $provider ][] = $webfont;
412 }
413 }
414
415 return $webfonts_by_provider;
416 }
417 }
418