Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.16% covered (success)
95.16%
59 / 62
50.00% covered (warning)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
SegmentToDelta
95.16% covered (success)
95.16%
59 / 62
50.00% covered (warning)
50.00%
1 / 2
17
0.00% covered (danger)
0.00%
0 / 1
 parseData
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
1 / 1
12
 shiftToSigned
57.14% covered (warning)
57.14%
4 / 7
0.00% covered (danger)
0.00%
0 / 1
6.97
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 */
14namespace Pop\Pdf\Build\Font\TrueType\Table\Cmap;
15
16/**
17 * CMAP segment-to-delta table class
18 *
19 * @category   Pop
20 * @package    Pop\Pdf
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    5.0.0
25 */
26class SegmentToDelta
27{
28
29    /**
30     * Method to parse the Segment to Delta (Format 4) CMAP data
31     *
32     * @param  string $data
33     * @return array
34     */
35    public static function parseData(string $data): array
36    {
37        $ary = unpack(
38            'nsegCountx2/' .
39            'nsearchRange/' .
40            'nentrySelector/' .
41            'nrangeShift', substr($data, 0, 8)
42        );
43
44        $ary['segCount'] = $ary['segCountx2'] / 2;
45        $ary['endCount'] = array();
46
47        $bytePos = 8;
48        for ($i = 0; $i < $ary['segCount']; $i++) {
49            $ar = unpack('nendCount', substr($data, $bytePos, 2));
50            $ary['endCount'][$i] = $ar['endCount'];
51            $bytePos += 2;
52        }
53
54        $ar = unpack('nreservedPad', substr($data, $bytePos, 2));
55        $bytePos += 2;
56
57        $ary['reservedPad'] = $ar['reservedPad'];
58
59        $ary['startCount'] = array();
60
61        for ($i = 0; $i < $ary['segCount']; $i++) {
62            $ar = unpack('nstartCount', substr($data, $bytePos, 2));
63            $ary['startCount'][$i] = $ar['startCount'];
64            $bytePos += 2;
65        }
66
67        $ary['idDelta'] = array();
68
69        for ($i = 0; $i < $ary['segCount']; $i++) {
70            $ar = unpack('nidDelta', substr($data, $bytePos, 2));
71            $ary['idDelta'][$i] = self::shiftToSigned($ar['idDelta']);
72            $bytePos += 2;
73        }
74
75        $ary['idRangeOffset'] = array();
76
77        for ($i = 0; $i < $ary['segCount']; $i++) {
78            $ar = unpack('nidRangeOffset', substr($data, $bytePos, 2));
79            $ary['idRangeOffset'][$i] = $ar['idRangeOffset'] >> 1;
80            $bytePos += 2;
81        }
82
83        $ary['glyphIndexArray'] = array();
84
85        for (; $bytePos < strlen($data); $bytePos += 2) {
86            $ar = unpack('nglyphIndex', substr($data, $bytePos, 2));
87            $ary['glyphIndexArray'][] = $ar['glyphIndex'];
88        }
89
90        $ary['glyphNumbers'] = array();
91
92        for ($segmentNum = 0; $segmentNum < $ary['segCount']; $segmentNum++) {
93            if ($ary['idRangeOffset'][$segmentNum] == 0) {
94                $delta = $ary['idDelta'][$segmentNum];
95
96                for ($code = $ary['startCount'][$segmentNum];
97                     $code <= $ary['endCount'][$segmentNum];
98                     $code++) {
99                    $ary['glyphNumbers'][$code] = ($code + $delta) % 65536;
100                }
101            } else {
102                $code       = $ary['startCount'][$segmentNum];
103                $glyphIndex = $ary['idRangeOffset'][$segmentNum] - ($ary['segCount'] - $segmentNum) - 1;
104
105                while ($code <= $ary['endCount'][$segmentNum]) {
106                    if (isset($ary['glyphIndexArray'][$glyphIndex])) {
107                        $ary['glyphNumbers'][$code] = $ary['glyphIndexArray'][$glyphIndex];
108                    }
109
110                    $code++;
111                    $glyphIndex++;
112                }
113            }
114        }
115
116        $ary['mapData'] = str_repeat("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 8192);
117        // Fill the index
118        foreach ($ary['glyphNumbers'] as $charCode => $glyph) {
119            $ary['mapData'][$charCode * 2] = chr($glyph >> 8);
120            $ary['mapData'][$charCode * 2 + 1] = chr($glyph & 0xFF);
121        }
122
123        return $ary;
124    }
125
126    /**
127     * Method to shift an unpacked signed short from big endian to little endian
128     *
129     * @param  int|array $values
130     * @return int|array
131     */
132    public static function shiftToSigned(int|array $values): int|array
133    {
134        if (is_array($values)) {
135            foreach ($values as $key => $value) {
136                if ($value >= pow(2, 15)) {
137                    $values[$key] -= pow(2, 16);
138                }
139            }
140        } else {
141            if ($values >= pow(2, 15)) {
142                $values -= pow(2, 16);
143            }
144        }
145
146        return $values;
147    }
148
149}