Skip to content

Commit d2d43d0

Browse files
authored
Merge branch 'master' into NumberFormat_Improved-Docblocks
2 parents 58c1261 + 590e4c7 commit d2d43d0

32 files changed

+199
-7
lines changed

src/PhpSpreadsheet/Shared/Font.php

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,23 @@ class Font
200200
],
201201
];
202202

203+
/**
204+
* Array that can be used to supplement FONT_FILE_NAMES for calculating exact width.
205+
*
206+
* @var array
207+
*/
208+
private static $extraFontArray = [];
209+
210+
public static function setExtraFontArray(array $extraFontArray): void
211+
{
212+
self::$extraFontArray = $extraFontArray;
213+
}
214+
215+
public static function getExtraFontArray(): array
216+
{
217+
return self::$extraFontArray;
218+
}
219+
203220
/**
204221
* AutoSize method.
205222
*
@@ -403,10 +420,6 @@ public static function calculateColumnWidth(
403420
*/
404421
public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): int
405422
{
406-
if (!function_exists('imagettfbbox')) {
407-
throw new PhpSpreadsheetException('GD library needs to be enabled');
408-
}
409-
410423
// font size should really be supplied in pixels in GD2,
411424
// but since GD2 seems to assume 72dpi, pixels and points are the same
412425
$fontFile = self::getTrueTypeFontFileFromFont($font);
@@ -532,7 +545,8 @@ public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkP
532545
}
533546

534547
$name = $font->getName();
535-
if (!isset(self::FONT_FILE_NAMES[$name])) {
548+
$fontArray = array_merge(self::FONT_FILE_NAMES, self::$extraFontArray);
549+
if (!isset($fontArray[$name])) {
536550
throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file');
537551
}
538552
$bold = $font->getBold();
@@ -544,7 +558,7 @@ public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkP
544558
if ($italic) {
545559
$index .= 'i';
546560
}
547-
$fontFile = self::FONT_FILE_NAMES[$name][$index];
561+
$fontFile = $fontArray[$name][$index];
548562

549563
$separator = '';
550564
if (mb_strlen(self::$trueTypeFontPath) > 1 && mb_substr(self::$trueTypeFontPath, -1) !== '/' && mb_substr(self::$trueTypeFontPath, -1) !== '\\') {
@@ -554,7 +568,31 @@ public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkP
554568

555569
// Check if file actually exists
556570
if ($checkPath && !file_exists($fontFile)) {
557-
throw new PhpSpreadsheetException('TrueType Font file not found');
571+
$alternateName = $name;
572+
if ($index !== 'x' && $fontArray[$name][$index] !== $fontArray[$name]['x']) {
573+
// Bold but no italic:
574+
// Comic Sans
575+
// Tahoma
576+
// Neither bold nor italic:
577+
// Impact
578+
// Lucida Console
579+
// Lucida Sans Unicode
580+
// Microsoft Sans Serif
581+
// Symbol
582+
if ($index === 'xb') {
583+
$alternateName .= ' Bold';
584+
} elseif ($index === 'xi') {
585+
$alternateName .= ' Italic';
586+
} elseif ($fontArray[$name]['xb'] === $fontArray[$name]['xbi']) {
587+
$alternateName .= ' Bold';
588+
} else {
589+
$alternateName .= ' Bold Italic';
590+
}
591+
}
592+
$fontFile = self::$trueTypeFontPath . $separator . $alternateName . '.ttf';
593+
if (!file_exists($fontFile)) {
594+
throw new PhpSpreadsheetException('TrueType Font file not found');
595+
}
558596
}
559597

560598
return $fontFile;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Shared;
4+
5+
use PhpOffice\PhpSpreadsheet\Exception as SSException;
6+
use PhpOffice\PhpSpreadsheet\Shared\Font;
7+
use PhpOffice\PhpSpreadsheet\Style\Font as StyleFont;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class FontFileNameTest extends TestCase
11+
{
12+
private const DEFAULT_DIRECTORY = 'tests/data/Shared/FakeFonts/Default';
13+
private const MAC_DIRECTORY = 'tests/data/Shared/FakeFonts/Mac';
14+
15+
/** @var string */
16+
private $holdDirectory;
17+
18+
/** @var array */
19+
private $holdExtraFontArray;
20+
21+
protected function setUp(): void
22+
{
23+
$this->holdDirectory = Font::getTrueTypeFontPath();
24+
$this->holdExtraFontArray = Font::getExtraFontArray();
25+
Font::setExtraFontArray([
26+
'Extra Font' => [
27+
'x' => 'extrafont.ttf',
28+
'xb' => 'extrafontbd.ttf',
29+
'xi' => 'extrafonti.ttf',
30+
'xbi' => 'extrafontbi.ttf',
31+
],
32+
]);
33+
}
34+
35+
protected function tearDown(): void
36+
{
37+
Font::setTrueTypeFontPath($this->holdDirectory);
38+
Font::setExtraFontArray($this->holdExtraFontArray);
39+
}
40+
41+
/**
42+
* @dataProvider providerDefault
43+
*/
44+
public function testDefaultFilenames(string $expected, array $fontArray): void
45+
{
46+
if ($expected === 'exception') {
47+
$this->expectException(SSException::class);
48+
$this->expectExceptionMessage('TrueType Font file not found');
49+
}
50+
Font::setTrueTypeFontPath(self::DEFAULT_DIRECTORY);
51+
$font = (new StyleFont())->applyFromArray($fontArray);
52+
$result = Font::getTrueTypeFontFileFromFont($font);
53+
self::assertSame($expected, basename($result));
54+
}
55+
56+
public function providerDefault(): array
57+
{
58+
return [
59+
['arial.ttf', ['name' => 'Arial']],
60+
['arialbd.ttf', ['name' => 'Arial', 'bold' => true]],
61+
['ariali.ttf', ['name' => 'Arial', 'italic' => true]],
62+
['arialbi.ttf', ['name' => 'Arial', 'bold' => true, 'italic' => true]],
63+
['cour.ttf', ['name' => 'Courier New']],
64+
['courbd.ttf', ['name' => 'Courier New', 'bold' => true]],
65+
['couri.ttf', ['name' => 'Courier New', 'italic' => true]],
66+
['courbi.ttf', ['name' => 'Courier New', 'bold' => true, 'italic' => true]],
67+
['impact.ttf', ['name' => 'Impact']],
68+
'no bold impact' => ['impact.ttf', ['name' => 'Impact', 'bold' => true]],
69+
'no italic impact' => ['impact.ttf', ['name' => 'Impact', 'italic' => true]],
70+
'no bold italic impact' => ['impact.ttf', ['name' => 'Impact', 'bold' => true, 'italic' => true]],
71+
['tahoma.ttf', ['name' => 'Tahoma']],
72+
['tahomabd.ttf', ['name' => 'Tahoma', 'bold' => true]],
73+
'no italic tahoma' => ['tahoma.ttf', ['name' => 'Tahoma', 'italic' => true]],
74+
'no bold italic tahoma' => ['tahomabd.ttf', ['name' => 'Tahoma', 'bold' => true, 'italic' => true]],
75+
'Times New Roman not in directory for this test' => ['exception', ['name' => 'Times New Roman']],
76+
['extrafont.ttf', ['name' => 'Extra Font']],
77+
['extrafontbd.ttf', ['name' => 'Extra Font', 'bold' => true]],
78+
['extrafonti.ttf', ['name' => 'Extra Font', 'italic' => true]],
79+
['extrafontbi.ttf', ['name' => 'Extra Font', 'bold' => true, 'italic' => true]],
80+
];
81+
}
82+
83+
/**
84+
* @dataProvider providerMac
85+
*/
86+
public function testMacFilenames(string $expected, array $fontArray): void
87+
{
88+
if ($expected === 'exception') {
89+
$this->expectException(SSException::class);
90+
$this->expectExceptionMessage('TrueType Font file not found');
91+
}
92+
Font::setTrueTypeFontPath(self::MAC_DIRECTORY);
93+
$font = (new StyleFont())->applyFromArray($fontArray);
94+
$result = Font::getTrueTypeFontFileFromFont($font);
95+
self::assertSame($expected, ucfirst(basename($result))); // allow for Windows case-insensitivity
96+
}
97+
98+
public function providerMac(): array
99+
{
100+
return [
101+
['Arial.ttf', ['name' => 'Arial']],
102+
['Arial Bold.ttf', ['name' => 'Arial', 'bold' => true]],
103+
['Arial Italic.ttf', ['name' => 'Arial', 'italic' => true]],
104+
['Arial Bold Italic.ttf', ['name' => 'Arial', 'bold' => true, 'italic' => true]],
105+
['Courier New.ttf', ['name' => 'Courier New']],
106+
['Courier New Bold.ttf', ['name' => 'Courier New', 'bold' => true]],
107+
['Courier New Italic.ttf', ['name' => 'Courier New', 'italic' => true]],
108+
['Courier New Bold Italic.ttf', ['name' => 'Courier New', 'bold' => true, 'italic' => true]],
109+
['Impact.ttf', ['name' => 'Impact']],
110+
'no bold impact' => ['Impact.ttf', ['name' => 'Impact', 'bold' => true]],
111+
'no italic impact' => ['Impact.ttf', ['name' => 'Impact', 'italic' => true]],
112+
'no bold italic impact' => ['Impact.ttf', ['name' => 'Impact', 'bold' => true, 'italic' => true]],
113+
['Tahoma.ttf', ['name' => 'Tahoma']],
114+
['Tahoma Bold.ttf', ['name' => 'Tahoma', 'bold' => true]],
115+
'no italic tahoma' => ['Tahoma.ttf', ['name' => 'Tahoma', 'italic' => true]],
116+
'no bold italic tahoma' => ['Tahoma Bold.ttf', ['name' => 'Tahoma', 'bold' => true, 'italic' => true]],
117+
'Times New Roman not in directory for this test' => ['exception', ['name' => 'Times New Roman']],
118+
['Extra Font.ttf', ['name' => 'Extra Font']],
119+
['Extra Font Bold.ttf', ['name' => 'Extra Font', 'bold' => true]],
120+
['Extra Font Italic.ttf', ['name' => 'Extra Font', 'italic' => true]],
121+
['Extra Font Bold Italic.ttf', ['name' => 'Extra Font', 'bold' => true, 'italic' => true]],
122+
];
123+
}
124+
125+
/**
126+
* @dataProvider providerOverride
127+
*/
128+
public function testOverrideFilenames(string $expected, array $fontArray): void
129+
{
130+
Font::setTrueTypeFontPath(self::DEFAULT_DIRECTORY);
131+
Font::setExtraFontArray([
132+
'Arial' => [
133+
'x' => 'extrafont.ttf',
134+
'xb' => 'extrafontbd.ttf',
135+
'xi' => 'extrafonti.ttf',
136+
'xbi' => 'extrafontbi.ttf',
137+
],
138+
]);
139+
$font = (new StyleFont())->applyFromArray($fontArray);
140+
$result = Font::getTrueTypeFontFileFromFont($font);
141+
self::assertSame($expected, basename($result));
142+
}
143+
144+
public function providerOverride(): array
145+
{
146+
return [
147+
['extrafont.ttf', ['name' => 'Arial']],
148+
['extrafontbd.ttf', ['name' => 'Arial', 'bold' => true]],
149+
['extrafonti.ttf', ['name' => 'Arial', 'italic' => true]],
150+
['extrafontbi.ttf', ['name' => 'Arial', 'bold' => true, 'italic' => true]],
151+
['cour.ttf', ['name' => 'Courier New']],
152+
];
153+
}
154+
}

tests/data/Shared/FakeFonts/Default/arial.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/arialbd.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/arialbi.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/ariali.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/cour.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/courbd.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/courbi.ttf

Whitespace-only changes.

tests/data/Shared/FakeFonts/Default/couri.ttf

Whitespace-only changes.

0 commit comments

Comments
 (0)