diff --git a/src/Model/Model_Required_Fonts.php b/src/Model/Model_Required_Fonts.php index 052ee3517..dc46e9754 100644 --- a/src/Model/Model_Required_Fonts.php +++ b/src/Model/Model_Required_Fonts.php @@ -122,7 +122,8 @@ public function flush(): void { } /** - * A font key's files on the core manifest (none for bundled and custom fonts) + * A font key's files on the core manifest (none for bundled and custom fonts), with those of its `sip-ext` font, + * which mPDF loads with it for the SIP characters (Sun-ExtA's Sun-ExtB) * * @param mixed $key A font key * @@ -131,22 +132,55 @@ public function flush(): void { * @since 6.19.0 */ public function get_font_files( $key ): array { - $family = $this->font_sources->get_font_data()[ $this->normalise_font( $key ) ] ?? null; + $fontdata = $this->font_sources->get_font_data(); + $family = $fontdata[ $this->normalise_font( $key ) ] ?? null; if ( ! is_array( $family ) ) { return []; } + /* One level only, as mPDF doesn't follow a sip-ext font's own */ + $families = [ $family, $fontdata[ $this->get_sip_ext( $family ) ] ?? null ]; + $files = []; - foreach ( [ 'R', 'B', 'I', 'BI' ] as $style ) { - $file = is_string( $family[ $style ] ?? null ) ? $this->font_sources->get_manifest_file( $family[ $style ] ) : ''; - if ( $this->font_sources->is_core_font_file( $file ) ) { - $files[] = $file; + foreach ( array_filter( $families, 'is_array' ) as $linked ) { + foreach ( [ 'R', 'B', 'I', 'BI' ] as $style ) { + $file = is_string( $linked[ $style ] ?? null ) ? $this->font_sources->get_manifest_file( $linked[ $style ] ) : ''; + if ( $this->font_sources->is_core_font_file( $file ) ) { + $files[] = $file; + } } } return array_values( array_unique( $files ) ); } + /** + * The font keys whose `sip-ext` font is the key, so get_font_files() counts the key's files as theirs + * + * @return string[] + * + * @since 6.19.0 + */ + protected function get_sip_parents( string $key ): array { + $parents = []; + foreach ( $this->font_sources->get_font_data() as $parent => $family ) { + if ( is_array( $family ) && $this->get_sip_ext( $family ) === $key ) { + $parents[] = (string) $parent; + } + } + + return $parents; + } + + /** + * The font key mPDF loads with a family for its SIP characters, or an empty string + * + * @since 6.19.0 + */ + protected function get_sip_ext( array $family ): string { + return $this->normalise_font( $family['sip-ext'] ?? '' ); + } + /** * Files that should be on disk: default, language, required-source and PDF-selected fonts * @@ -237,13 +271,16 @@ static function ( array $reason ) use ( $none ): array { * @since 6.19.0 */ public function is_font_in_use( string $key, bool $network = false ): bool { - if ( isset( $this->get_settings_font_reasons()[ $key ] ) ) { + /* A font whose sip-ext font this is needs its files too */ + $keys = array_merge( [ $key ], $this->get_sip_parents( $key ) ); + + if ( array_intersect_key( $this->get_settings_font_reasons(), array_flip( $keys ) ) !== [] ) { return true; } $this->form_read_failed = false; foreach ( $this->each_pdf_font() as $font => $unused ) { - if ( $font === $key ) { + if ( in_array( $font, $keys, true ) ) { return true; } } @@ -259,7 +296,7 @@ public function is_font_in_use( string $key, bool $network = false ): bool { $sites = $this->get_site_font_records(); foreach ( $this->get_other_site_ids() as $site_id ) { - if ( ! isset( $sites[ $site_id ] ) || in_array( $key, $sites[ $site_id ], true ) ) { + if ( ! isset( $sites[ $site_id ] ) || array_intersect( $keys, $sites[ $site_id ] ) !== [] ) { return true; } } diff --git a/tests/phpunit/integration/Model/Test_Model_Font_Downloader.php b/tests/phpunit/integration/Model/Test_Model_Font_Downloader.php index 4a8c2b0ed..274fa9299 100644 --- a/tests/phpunit/integration/Model/Test_Model_Font_Downloader.php +++ b/tests/phpunit/integration/Model/Test_Model_Font_Downloader.php @@ -972,6 +972,16 @@ public function test_prune_replaces_needed_installer_sun_ext_fonts() { $this->assertSame( [ [] ], $this->get_scheduled_args() ); } + public function test_prune_replaces_an_installer_sun_extb_a_sun_exta_default_font_needs() { + GPDFAPI::get_options_class()->update_option( 'default_font', 'sun-exta' ); + wp_clear_scheduled_hook( Model_Font_Downloader::SYNC_EVENT ); + $this->set_installer_manifest( [ 'Sun-ExtB.ttf' => 'Chewy.ttf' ] ); + $this->copy_fixture( 'Chewy.ttf', 'Sun-ExtB.ttf' ); + + $this->assertSame( [ 'Sun-ExtB.ttf' ], $this->downloader->prune() ); + $this->assertSame( [ [] ], $this->get_scheduled_args() ); + } + public function test_the_replacement_for_an_installer_garuda_lands_under_its_plain_name() { $this->use_shipped_installer_manifest(); $this->serve_fixture_as( 'Garuda-v1.1.1.ttf', 'Garuda.ttf' ); diff --git a/tests/phpunit/integration/Model/Test_Model_Required_Fonts.php b/tests/phpunit/integration/Model/Test_Model_Required_Fonts.php index c9467616d..1beb6da1d 100644 --- a/tests/phpunit/integration/Model/Test_Model_Required_Fonts.php +++ b/tests/phpunit/integration/Model/Test_Model_Required_Fonts.php @@ -59,7 +59,8 @@ public function test_it_is_registered() { public function test_get_font_files_lists_a_core_keys_files_on_the_manifest() { $this->assertSame( [ 'DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf', 'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf' ], $this->required->get_font_files( ' DejaVuSerif ' ) ); - $this->assertSame( [ 'Sun-ExtA.ttf' ], $this->required->get_font_files( 'sun-exta' ) ); + $this->assertSame( [ 'Sun-ExtA.ttf', 'Sun-ExtB.ttf' ], $this->required->get_font_files( 'sun-exta' ) ); + $this->assertSame( [ 'Sun-ExtB.ttf' ], $this->required->get_font_files( 'sun-extb' ) ); $this->assertSame( [], $this->required->get_font_files( 'arimo' ) ); $this->assertSame( [], $this->required->get_font_files( 'notafont' ) ); } @@ -71,6 +72,47 @@ public function test_get_font_files_skips_a_custom_font() { $this->assertSame( [], $this->required->get_font_files( 'chewy' ) ); } + public function test_get_font_files_counts_a_sip_ext_font_one_level_deep() { + add_filter( + 'mpdf_font_data', + static function ( array $fontdata ): array { + $fontdata['dejavuserif']['sip-ext'] = ' FreeSerif '; + $fontdata['freeserif']['sip-ext'] = 'dejavuserif'; + $fontdata['garuda']['sip-ext'] = 'notafont'; + $fontdata['kaputaunicode']['sip-ext'] = [ 'freeserif' ]; + + return $fontdata; + }, + PHP_INT_MAX + ); + + $required = $this->required_with( $this->stub_font_sources() ); + $dejavu = [ 'DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf', 'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf' ]; + + $this->assertSame( array_merge( $dejavu, self::$freeserif ), $required->get_font_files( 'dejavuserif' ) ); + $this->assertSame( array_merge( self::$freeserif, $dejavu ), $required->get_font_files( 'freeserif' ) ); + $this->assertSame( self::GARUDA, $required->get_font_files( 'garuda' ) ); + $this->assertSame( [ 'kaputaunicode.ttf' ], $required->get_font_files( 'kaputaunicode' ) ); + } + + public function test_a_sun_exta_default_font_requires_sun_extb() { + GPDFAPI::get_options_class()->update_option( 'default_font', 'sun-exta' ); + + $this->assertSame( [ 'Sun-ExtA.ttf', 'Sun-ExtB.ttf' ], $this->required->get_required_font_files() ); + $this->assertSame( [ 'Sun-ExtA.ttf', 'Sun-ExtB.ttf' ], $this->required->get_install_font_files( [ 'sun-exta' ] ) ); + } + + public function test_a_sip_ext_font_is_in_use_while_a_font_linking_it_is() { + $this->assertFalse( $this->required->is_font_in_use( 'sun-extb' ) ); + + GPDFAPI::get_options_class()->update_option( 'default_font', 'sun-exta' ); + $this->assertTrue( $this->required->is_font_in_use( 'sun-extb' ) ); + + GPDFAPI::get_options_class()->delete_option( 'default_font' ); + $this->add_form_with_pdfs( [ [ 'font' => 'sun-exta' ] ] ); + $this->assertTrue( $this->required->is_font_in_use( 'sun-extb' ) ); + } + /** * @dataProvider provider_junk_font_data * @@ -1003,6 +1045,19 @@ public function test_a_site_without_a_usable_record_counts_as_using_every_core_f $this->assertTrue( $this->required->is_font_in_use( 'dejavuserif', true ) ); } + public function test_a_sip_ext_font_is_in_use_while_another_site_uses_a_font_linking_it() { + if ( ! is_multisite() ) { + $this->markTestSkipped( 'Multisite only' ); + } + + $site = self::factory()->blog->create(); + update_network_option( get_main_network_id(), Model_Required_Fonts::SITE_FONTS_PREFIX . $site, [ 'dejavuserif' ] ); + $this->assertFalse( $this->required->is_font_in_use( 'sun-extb', true ) ); + + update_network_option( get_main_network_id(), Model_Required_Fonts::SITE_FONTS_PREFIX . $site, [ 'sun-exta' ] ); + $this->assertTrue( $this->required->is_font_in_use( 'sun-extb', true ) ); + } + public function test_a_failed_form_read_records_nothing() { if ( ! is_multisite() ) { $this->markTestSkipped( 'Multisite only' );