Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.44% |
34 / 36 |
|
90.91% |
10 / 11 |
CRAP | |
0.00% |
0 / 1 |
Font | |
94.44% |
34 / 36 |
|
90.91% |
10 / 11 |
19.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
standardFonts | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setFont | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
getFont | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isStandard | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isEmbedded | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getStandardFonts | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getParsedFont | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getStringWidth | |
85.71% |
12 / 14 |
|
0.00% |
0 / 1 |
5.07 | |||
parser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Pdf\Document; |
15 | |
16 | use Pop\Pdf\Build\Font\AbstractFont; |
17 | use Pop\Pdf\Build\Font\Parser; |
18 | use InvalidArgumentException; |
19 | |
20 | /** |
21 | * Pdf font class |
22 | * |
23 | * @category Pop |
24 | * @package Pop\Pdf |
25 | * @author Nick Sagona, III <dev@nolainteractive.com> |
26 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
27 | * @license http://www.popphp.org/license New BSD License |
28 | * @version 5.0.0 |
29 | */ |
30 | class Font |
31 | { |
32 | |
33 | /** |
34 | * Standard font constants |
35 | */ |
36 | const ARIAL = 'Arial'; |
37 | const ARIAL_ITALIC = 'Arial,Italic'; |
38 | const ARIAL_BOLD = 'Arial,Bold'; |
39 | const ARIAL_BOLD_ITALIC = 'Arial,BoldItalic'; |
40 | const COURIER = 'Courier'; |
41 | const COURIER_OBLIQUE = 'Courier-Oblique'; |
42 | const COURIER_BOLD = 'Courier-Bold'; |
43 | const COURIER_BOLD_OBLIQUE = 'Courier-BoldOblique'; |
44 | const COURIER_NEW = 'CourierNew'; |
45 | const COURIER_NEW_ITALIC = 'CourierNew,Italic'; |
46 | const COURIER_NEW_BOLD = 'CourierNew,Bold'; |
47 | const COURIER_NEW_BOLD_ITALIC = 'CourierNew,BoldItalic'; |
48 | const HELVETICA = 'Helvetica'; |
49 | const HELVETICA_OBLIQUE = 'Helvetica-Oblique'; |
50 | const HELVETICA_BOLD = 'Helvetica-Bold'; |
51 | const HELVETICA_BOLD_OBLIQUE = 'Helvetica-BoldOblique'; |
52 | const SYMBOL = 'Symbol'; |
53 | const TIMES_ROMAN = 'Times-Roman'; |
54 | const TIMES_BOLD = 'Times-Bold'; |
55 | const TIMES_ITALIC = 'Times-Italic'; |
56 | const TIMES_BOLD_ITALIC = 'Times-BoldItalic'; |
57 | const TIMES_NEW_ROMAN = 'TimesNewRoman'; |
58 | const TIMES_NEW_ROMAN_ITALIC = 'TimesNewRoman,Italic'; |
59 | const TIMES_NEW_ROMAN_BOLD = 'TimesNewRoman,Bold'; |
60 | const TIMES_NEW_ROMAN_BOLDITALIC = 'TimesNewRoman,BoldItalic'; |
61 | const ZAPF_DINGBATS = 'ZapfDingbats'; |
62 | |
63 | /** |
64 | * Standard PDF fonts |
65 | * @var array |
66 | */ |
67 | protected array $standardFonts = [ |
68 | 'Arial', 'Arial,Italic', 'Arial,Bold', 'Arial,BoldItalic', 'Courier', 'CourierNew', 'Courier-Oblique', |
69 | 'CourierNew,Italic', 'Courier-Bold', 'CourierNew,Bold', 'Courier-BoldOblique', 'CourierNew,BoldItalic', |
70 | 'Helvetica', 'Helvetica-Oblique', 'Helvetica-Bold', 'Helvetica-BoldOblique', 'Symbol', 'Times-Roman', |
71 | 'Times-Bold', 'Times-Italic', 'Times-BoldItalic', 'TimesNewRoman', 'TimesNewRoman,Italic', |
72 | 'TimesNewRoman,Bold', 'TimesNewRoman,BoldItalic', 'ZapfDingbats' |
73 | ]; |
74 | |
75 | /** |
76 | * Font |
77 | * @var ?string |
78 | */ |
79 | protected ?string $font = null; |
80 | |
81 | /** |
82 | * Font name |
83 | * @var ?string |
84 | */ |
85 | protected ?string $name = null; |
86 | |
87 | /** |
88 | * Flag for a standard font |
89 | * @var bool |
90 | */ |
91 | protected bool $isStandard = false; |
92 | |
93 | /** |
94 | * Flag for an embedded font file |
95 | * @var bool |
96 | */ |
97 | protected bool$isEmbedded = false; |
98 | |
99 | /** |
100 | * Font parser |
101 | * @var ?Parser |
102 | */ |
103 | protected ?Parser $parser = null; |
104 | |
105 | /** |
106 | * Constructor |
107 | * |
108 | * Instantiate a PDF font. |
109 | * |
110 | * @param ?string $font |
111 | */ |
112 | public function __construct(?string $font = null) |
113 | { |
114 | if ($font !== null) { |
115 | $this->setFont($font); |
116 | } |
117 | } |
118 | |
119 | /** |
120 | * Get standard PDF fonts in an array |
121 | * |
122 | * @return array |
123 | */ |
124 | public static function standardFonts(): array |
125 | { |
126 | return (new self())->getStandardFonts(); |
127 | } |
128 | |
129 | /** |
130 | * Set font |
131 | * |
132 | * @param string $font |
133 | * @throws InvalidArgumentException|\Pop\Pdf\Build\Font\Exception |
134 | * @return Font |
135 | */ |
136 | public function setFont(string $font): Font |
137 | { |
138 | $this->font = $font; |
139 | if (in_array($font, $this->standardFonts)) { |
140 | $this->isStandard = true; |
141 | $this->name = $font; |
142 | } else if (file_exists($font)) { |
143 | $this->isEmbedded = true; |
144 | $this->parser = new Parser($this->font); |
145 | $this->name = $this->parser->getFontName(); |
146 | } else { |
147 | throw new InvalidArgumentException( |
148 | "Error: The font '" . $font . "' is not valid. It must be a standard PDF font or a font file." |
149 | ); |
150 | } |
151 | |
152 | return $this; |
153 | } |
154 | |
155 | /** |
156 | * Get font |
157 | * |
158 | * @return ?string |
159 | */ |
160 | public function getFont(): ?string |
161 | { |
162 | return $this->font; |
163 | } |
164 | |
165 | /** |
166 | * Get font name |
167 | * |
168 | * @return ?string |
169 | */ |
170 | public function getName(): ?string |
171 | { |
172 | return $this->name; |
173 | } |
174 | |
175 | /** |
176 | * Determine if the font is a standard font |
177 | * |
178 | * @return bool |
179 | */ |
180 | public function isStandard(): bool |
181 | { |
182 | return $this->isStandard; |
183 | } |
184 | |
185 | /** |
186 | * Determine if the font is an embedded font |
187 | * |
188 | * @return bool |
189 | */ |
190 | public function isEmbedded(): bool |
191 | { |
192 | return $this->isEmbedded; |
193 | } |
194 | |
195 | /** |
196 | * Get available standard fonts |
197 | * |
198 | * @return array |
199 | */ |
200 | public function getStandardFonts(): array |
201 | { |
202 | return $this->standardFonts; |
203 | } |
204 | |
205 | /** |
206 | * Get the font parser |
207 | * |
208 | * @return ?AbstractFont |
209 | */ |
210 | public function getParsedFont(): ?AbstractFont |
211 | { |
212 | return ($this->parser !== null) ? $this->parser->getFont() : null; |
213 | } |
214 | |
215 | /** |
216 | * Attempt to get string width |
217 | * |
218 | * @param string $string |
219 | * @param mixed $size |
220 | * @throws Exception |
221 | * @return mixed |
222 | */ |
223 | public function getStringWidth(string $string, string $size): mixed |
224 | { |
225 | if ($this->parser !== null) { |
226 | return $this->parser->getFont()->getStringWidth($string, $size); |
227 | } else { |
228 | $fontClass = '\Pop\Pdf\Build\Font\Standard\\' . str_replace([',', '-'], ['', ''], $this->name); |
229 | if (!class_exists($fontClass)) { |
230 | throw new Exception('Error: That standard font class was not found.'); |
231 | } |
232 | $font = new $fontClass(); |
233 | $widths = []; |
234 | |
235 | $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $string); |
236 | $characters = []; |
237 | |
238 | for ($i = 0; $i < strlen($drawingString); $i++) { |
239 | $characters[] = (ord($drawingString[$i++]) << 8 ) | ord($drawingString[$i]); |
240 | } |
241 | |
242 | foreach ($characters as $character) { |
243 | $widths[] = $font->getGlyphWidth($character); |
244 | } |
245 | |
246 | return (array_sum($widths) / $font->getUnitsPerEm()) * $size; |
247 | } |
248 | } |
249 | |
250 | /** |
251 | * Get the font parser |
252 | * |
253 | * @return ?Parser |
254 | */ |
255 | public function parser(): ?Parser |
256 | { |
257 | return $this->parser; |
258 | } |
259 | |
260 | } |