Recurse Directories Searching for Font File

Fix #2809. User felt font files should be searched recursively. The problem could be overcome merely by specifying a more precise font location. However, user has a point - the file layout on user's system is pretty common, and can be integrated into PhpSpreadsheet code easily. As a bonus, the DocBlock for setTrueTypeFontPath, which the user felt was misleading, doesn't even have to change.
This commit is contained in:
oleibman
2023-12-15 17:30:08 -08:00
parent 3f20b2347a
commit 5a418ea1fb
7 changed files with 63 additions and 2 deletions
+37 -2
View File
@@ -6,6 +6,8 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Font as FontStyle;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
class Font
{
@@ -318,7 +320,8 @@ class Font
/**
* Set the path to the folder containing .ttf files. There should be a trailing slash.
* Typical locations on variout some platforms:
* Path will be recursively searched for font file.
* Typical locations on various platforms:
* <ul>
* <li>C:/Windows/Fonts/</li>
* <li>/usr/share/fonts/truetype/</li>
@@ -589,7 +592,7 @@ class Font
}
$fontFileAbsolute = preg_match('~^([A-Za-z]:)?[/\\\\]~', $fontFile) === 1;
if (!$fontFileAbsolute) {
$fontFile = self::$trueTypeFontPath . $separator . $fontFile;
$fontFile = self::findFontFile(self::$trueTypeFontPath, $fontFile) ?? self::$trueTypeFontPath . $separator . $fontFile;
}
// Check if file actually exists
@@ -698,4 +701,36 @@ class Font
return $rowHeight;
}
private static function findFontFile(string $startDirectory, string $desiredFont): ?string
{
$fontPath = null;
if ($startDirectory === '') {
return null;
}
if (file_exists("$startDirectory/$desiredFont")) {
$fontPath = "$startDirectory/$desiredFont";
} else {
$it = new RecursiveDirectoryIterator(
$startDirectory,
RecursiveDirectoryIterator::SKIP_DOTS
| RecursiveDirectoryIterator::FOLLOW_SYMLINKS
);
foreach (
new RecursiveIteratorIterator(
$it,
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CHILD_FIRST
) as $file
) {
if (basename($file) === $desiredFont) {
$fontPath = $file;
break;
}
}
}
return $fontPath;
}
}
@@ -13,6 +13,7 @@ class FontFileNameTest extends TestCase
{
private const DEFAULT_DIRECTORY = 'tests/data/Shared/FakeFonts/Default';
private const MAC_DIRECTORY = 'tests/data/Shared/FakeFonts/Mac';
private const RECURSE_DIRECTORY = 'tests/data/Shared/FakeFonts/Recurse';
private string $holdDirectory;
@@ -182,4 +183,29 @@ class FontFileNameTest extends TestCase
'non-absolute path uses TrueTypeFontPath' => ['cour.ttf', ['name' => 'Courier New']],
];
}
/**
* @dataProvider providerRecurse
*/
public function testRecurseFilenames(string $expected, array $fontArray): void
{
if ($expected === 'exception') {
$this->expectException(SSException::class);
$this->expectExceptionMessage('TrueType Font file not found');
}
Font::setTrueTypeFontPath(self::RECURSE_DIRECTORY);
$font = (new StyleFont())->applyFromArray($fontArray);
$result = Font::getTrueTypeFontFileFromFont($font);
self::assertSame($expected, basename($result));
}
public static function providerRecurse(): array
{
return [
'in subdirectory' => ['arial.ttf', ['name' => 'Arial']],
'in subdirectory bold' => ['arialbd.ttf', ['name' => 'Arial', 'bold' => true]],
'in main directory' => ['cour.ttf', ['name' => 'Courier New']],
'not in main or subdirectory' => ['exception', ['name' => 'Courier New', 'bold' => true]],
];
}
}