Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.53% covered (success)
89.53%
77 / 86
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
TrueType
89.53% covered (success)
89.53%
77 / 86
60.00% covered (warning)
60.00%
3 / 5
28.90
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 parseTtfTable
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
2
 parseName
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 parseCommonTables
93.33% covered (success)
93.33%
42 / 45
0.00% covered (danger)
0.00%
0 / 1
16.08
 parseRequiredTables
45.45% covered (warning)
45.45%
5 / 11
0.00% covered (danger)
0.00%
0 / 1
6.60
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-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Pdf\Build\Font;
15
16use Pop\Utils\ArrayObject as Data;
17
18/**
19 * TrueType font class
20 *
21 * @category   Pop
22 * @package    Pop\Pdf
23 * @author     Nick Sagona, III <dev@nolainteractive.com>
24 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
25 * @license    http://www.popphp.org/license     New BSD License
26 * @version    4.2.0
27 */
28class TrueType extends AbstractFont
29{
30
31    /**
32     * Font properties
33     * @var array
34     */
35    protected $properties = [
36        'info'             => null,
37        'bBox'             => null,
38        'ascent'           => 0,
39        'descent'          => 0,
40        'numberOfGlyphs'   => 0,
41        'glyphWidths'      => [],
42        'rawGlyphWidths'   => [],
43        'cmap'             => ['glyphIndexArray' => [], 'glyphNumbers' => []],
44        'missingWidth'     => 0,
45        'numberOfHMetrics' => 0,
46        'italicAngle'      => 0,
47        'capHeight'        => 0,
48        'stemH'            => 0,
49        'stemV'            => 0,
50        'unitsPerEm'       => 1000,
51        'flags'            => null,
52        'embeddable'       => true,
53        'header'           => null,
54        'ttfHeader'        => null,
55        'ttfTable'         => null,
56        'tables'           => [],
57        'tableInfo'        => []
58    ];
59
60    /**
61     * Constructor
62     *
63     * Instantiate a TrueType font file object based on a pre-existing font file on disk.
64     *
65     * @param  string $fontFile
66     * @param  string $fontStream
67     */
68    public function __construct($fontFile = null, $fontStream = null)
69    {
70        parent::__construct($fontFile, $fontStream);
71
72        $this->parseTtfTable();
73        $this->parseName();
74        $this->parseCommonTables();
75        $this->parseRequiredTables();
76    }
77
78    /**
79     * Method to parse the TTF header and table of the TrueType font file.
80     *
81     * @return void
82     */
83    protected function parseTtfTable()
84    {
85        $ttfHeader = unpack(
86            'nmajorVersion/' .
87            'nminorVersion/' .
88            'nnumberOfTables/' .
89            'nsearchRange/' .
90            'nentrySelector/' .
91            'nrangeShift', $this->read(0, 12)
92        );
93
94        $tableName = $this->read(12, 4);
95
96        $ttfTable = unpack(
97            'Nchecksum/' .
98            'Noffset/' .
99            'Nlength', $this->read(16, 12)
100        );
101
102        $ttfTable['name'] = $tableName;
103
104        $this->properties['ttfHeader'] = new Data($ttfHeader);
105        $this->properties['ttfTable']  = new Data($ttfTable);
106
107        $nameByteOffset  = 28;
108        $tableByteOffset = 32;
109
110        for ($i = 0; $i < $this->properties['ttfHeader']['numberOfTables']; $i++) {
111            $ttfTableName = $this->read($nameByteOffset, 4);
112            $ttfTable     = unpack(
113                'Nchecksum/' .
114                'Noffset/' .
115                'Nlength', $this->read($tableByteOffset, 12)
116            );
117
118            $this->properties['tableInfo'][trim($ttfTableName)] = new Data($ttfTable);
119
120            $nameByteOffset = $tableByteOffset + 12;
121            $tableByteOffset = $nameByteOffset + 4;
122        }
123    }
124
125    /**
126     * Method to parse the TTF info of the TrueType font file from the name table.
127     *
128     * @return void
129     */
130    protected function parseName()
131    {
132        if (isset($this->properties['tableInfo']['name'])) {
133            $this->properties['tables']['name'] = new TrueType\Table\Name($this);
134            $this->properties['info'] = $this->properties['tables']['name'];
135            if ((stripos($this->properties['tables']['name']['fontFamily'], 'bold') !== false) ||
136                (stripos($this->properties['tables']['name']['fullName'], 'bold') !== false) ||
137                (stripos($this->properties['tables']['name']['postscriptName'], 'bold') !== false)) {
138                $this->properties['stemV'] = 120;
139            } else {
140                $this->properties['stemV'] = 70;
141            }
142        }
143    }
144
145    /**
146     * Method to parse the common tables of the TrueType font file.
147     *
148     * @return void
149     */
150    protected function parseCommonTables()
151    {
152        // head
153        if (isset($this->properties['tableInfo']['head'])) {
154            $this->properties['tables']['head'] = new TrueType\Table\Head($this);
155
156            $this->properties['unitsPerEm'] = $this->properties['tables']['head']['unitsPerEm'];
157
158            $this->properties['tables']['head']['xMin'] = $this->toEmSpace($this->properties['tables']['head']['xMin']);
159            $this->properties['tables']['head']['yMin'] = $this->toEmSpace($this->properties['tables']['head']['yMin']);
160            $this->properties['tables']['head']['xMax'] = $this->toEmSpace($this->properties['tables']['head']['xMax']);
161            $this->properties['tables']['head']['yMax'] = $this->toEmSpace($this->properties['tables']['head']['yMax']);
162
163            $this->properties['bBox'] = new Data([
164                'xMin' => $this->properties['tables']['head']['xMin'],
165                'yMin' => $this->properties['tables']['head']['yMin'],
166                'xMax' => $this->properties['tables']['head']['xMax'],
167                'yMax' => $this->properties['tables']['head']['yMax']
168            ]);
169
170            $this->properties['header'] = $this->properties['tables']['head'];
171        }
172
173        // hhea
174        if (isset($this->properties['tableInfo']['hhea'])) {
175            $this->properties['tables']['hhea'] = new TrueType\Table\Hhea($this);
176            $this->properties['ascent']           = $this->properties['tables']['hhea']['ascent'];
177            $this->properties['descent']          = $this->properties['tables']['hhea']['descent'];
178            $this->properties['capHeight']        = $this->properties['ascent'] + $this->properties['descent'];
179            $this->properties['numberOfHMetrics'] = $this->properties['tables']['hhea']['numberOfHMetrics'];
180        }
181
182        // maxp
183        if (isset($this->properties['tableInfo']['maxp'])) {
184            $this->properties['tables']['maxp'] = new TrueType\Table\Maxp($this);
185            $this->properties['numberOfGlyphs'] = $this->properties['tables']['maxp']['numberOfGlyphs'];
186        }
187
188        // post
189        if (isset($this->properties['tableInfo']['post'])) {
190            $this->properties['tables']['post'] = new TrueType\Table\Post($this);
191
192            if ($this->properties['tables']['post']['italicAngle'] != 0) {
193                $this->properties['flags']['isItalic'] = true;
194                $this->properties['italicAngle'] = $this->properties['tables']['post']['italicAngle'];
195            }
196
197            if ($this->properties['tables']['post']['fixed'] != 0) {
198                $this->properties['flags']['isFixedPitch'] = true;
199            }
200        }
201
202        // hmtx
203        if (isset($this->properties['tableInfo']['hmtx'])) {
204            $this->properties['tables']['hmtx'] = new TrueType\Table\Hmtx($this);
205            $this->properties['glyphWidths'] = $this->properties['tables']['hmtx']['glyphWidths'];
206            if (isset($this->properties['glyphWidths'][0])) {
207                $this->properties['missingWidth'] = round((1000 / $this->properties['unitsPerEm']) * $this->properties['glyphWidths'][0]);
208            }
209
210            foreach ($this->properties['glyphWidths'] as $key => $value) {
211                $this->properties['rawGlyphWidths'][$key] = $value;
212                $this->properties['glyphWidths'][$key]    = round((1000 / $this->properties['unitsPerEm']) * $value);
213            }
214        }
215
216        // cmap
217        if (isset($this->properties['tableInfo']['cmap'])) {
218            $this->properties['tables']['cmap'] = new TrueType\Table\Cmap($this);
219            if (isset($this->properties['tables']['cmap']['subTables']) && isset($this->properties['tables']['cmap']['subTables'][0]) &&
220                isset($this->properties['tables']['cmap']['subTables'][0]['parsed'])) {
221                if (isset($this->properties['tables']['cmap']['subTables'][0]['parsed']['glyphIndexArray'])) {
222                    $this->properties['cmap']['glyphIndexArray'] = $this->properties['tables']['cmap']['subTables'][0]['parsed']['glyphIndexArray'];
223                }
224                if (isset($this->properties['tables']['cmap']['subTables'][0]['parsed']['glyphNumbers'])) {
225                    $this->properties['cmap']['glyphNumbers'] = $this->properties['tables']['cmap']['subTables'][0]['parsed']['glyphNumbers'];
226                }
227            }
228        }
229    }
230
231    /**
232     * Method to parse the required tables of the TrueType font file.
233     *
234     * @return void
235     */
236    protected function parseRequiredTables()
237    {
238        // loca
239        if (isset($this->properties['tableInfo']['loca'])) {
240            $this->properties['tables']['loca'] = new TrueType\Table\Loca($this);
241        }
242
243        // glyf
244        if (isset($this->properties['tableInfo']['glyf'])) {
245            $this->properties['tables']['glyf'] = new TrueType\Table\Glyf($this);
246        }
247
248        // OS/2 (Optional in a TTF font file)
249        if (isset($this->properties['tableInfo']['OS/2'])) {
250            $this->properties['tables']['OS/2']         = new TrueType\Table\Os2($this);
251            $this->properties['flags']['isSerif']       = $this->properties['tables']['OS/2']['flags']['isSerif'];
252            $this->properties['flags']['isScript']      = $this->properties['tables']['OS/2']['flags']['isScript'];
253            $this->properties['flags']['isSymbolic']    = $this->properties['tables']['OS/2']['flags']['isSymbolic'];
254            $this->properties['flags']['isNonSymbolic'] = $this->properties['tables']['OS/2']['flags']['isNonSymbolic'];
255            $this->properties['embeddable']             = $this->properties['tables']['OS/2']['embeddable'];
256        }
257    }
258
259}