Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / vendor / phenx / php-font-lib / src / FontLib / Glyph / OutlineSimple.php
1 <?php
2 /**
3  * @package php-font-lib
4  * @link    https://github.com/PhenX/php-font-lib
5  * @author  Fabien Ménager <fabien.menager@gmail.com>
6  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7  * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
8  */
9
10 namespace FontLib\Glyph;
11
12 /**
13  * `glyf` font table.
14  *
15  * @package php-font-lib
16  */
17 class OutlineSimple extends Outline {
18   const ON_CURVE       = 0x01;
19   const X_SHORT_VECTOR = 0x02;
20   const Y_SHORT_VECTOR = 0x04;
21   const REPEAT         = 0x08;
22   const THIS_X_IS_SAME = 0x10;
23   const THIS_Y_IS_SAME = 0x20;
24
25   public $instructions;
26   public $points;
27
28   function parseData() {
29     parent::parseData();
30
31     if (!$this->size) {
32       return;
33     }
34
35     $font = $this->getFont();
36
37     $noc = $this->numberOfContours;
38
39     if ($noc == 0) {
40       return;
41     }
42
43     $endPtsOfContours = $font->r(array(self::uint16, $noc));
44
45     $instructionLength  = $font->readUInt16();
46     $this->instructions = $font->r(array(self::uint8, $instructionLength));
47
48     $count = $endPtsOfContours[$noc - 1] + 1;
49
50     // Flags
51     $flags = array();
52     for ($index = 0; $index < $count; $index++) {
53       $flags[$index] = $font->readUInt8();
54
55       if ($flags[$index] & self::REPEAT) {
56         $repeats = $font->readUInt8();
57
58         for ($i = 1; $i <= $repeats; $i++) {
59           $flags[$index + $i] = $flags[$index];
60         }
61
62         $index += $repeats;
63       }
64     }
65
66     $points = array();
67     foreach ($flags as $i => $flag) {
68       $points[$i]["onCurve"]      = $flag & self::ON_CURVE;
69       $points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
70     }
71
72     // X Coords
73     $x = 0;
74     for ($i = 0; $i < $count; $i++) {
75       $flag = $flags[$i];
76
77       if ($flag & self::THIS_X_IS_SAME) {
78         if ($flag & self::X_SHORT_VECTOR) {
79           $x += $font->readUInt8();
80         }
81       }
82       else {
83         if ($flag & self::X_SHORT_VECTOR) {
84           $x -= $font->readUInt8();
85         }
86         else {
87           $x += $font->readInt16();
88         }
89       }
90
91       $points[$i]["x"] = $x;
92     }
93
94     // Y Coords
95     $y = 0;
96     for ($i = 0; $i < $count; $i++) {
97       $flag = $flags[$i];
98
99       if ($flag & self::THIS_Y_IS_SAME) {
100         if ($flag & self::Y_SHORT_VECTOR) {
101           $y += $font->readUInt8();
102         }
103       }
104       else {
105         if ($flag & self::Y_SHORT_VECTOR) {
106           $y -= $font->readUInt8();
107         }
108         else {
109           $y += $font->readInt16();
110         }
111       }
112
113       $points[$i]["y"] = $y;
114     }
115
116     $this->points = $points;
117   }
118
119   public function splitSVGPath($path) {
120     preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
121
122     return $matches[0];
123   }
124
125   public function makePoints($path) {
126     $path = $this->splitSVGPath($path);
127     $l    = count($path);
128     $i    = 0;
129
130     $points = array();
131
132     while ($i < $l) {
133       switch ($path[$i]) {
134         // moveTo
135         case "M":
136           $points[] = array(
137             "onCurve"      => true,
138             "x"            => $path[++$i],
139             "y"            => $path[++$i],
140             "endOfContour" => false,
141           );
142           break;
143
144         // lineTo
145         case "L":
146           $points[] = array(
147             "onCurve"      => true,
148             "x"            => $path[++$i],
149             "y"            => $path[++$i],
150             "endOfContour" => false,
151           );
152           break;
153
154         // quadraticCurveTo
155         case "Q":
156           $points[] = array(
157             "onCurve"      => false,
158             "x"            => $path[++$i],
159             "y"            => $path[++$i],
160             "endOfContour" => false,
161           );
162           $points[] = array(
163             "onCurve"      => true,
164             "x"            => $path[++$i],
165             "y"            => $path[++$i],
166             "endOfContour" => false,
167           );
168           break;
169
170         // closePath
171         /** @noinspection PhpMissingBreakStatementInspection */
172         case "z":
173           $points[count($points) - 1]["endOfContour"] = true;
174
175         default:
176           $i++;
177           break;
178       }
179     }
180
181     return $points;
182   }
183
184   function encode() {
185     if (empty($this->points)) {
186       return parent::encode();
187     }
188
189     return $this->size = $this->encodePoints($this->points);
190   }
191
192   public function encodePoints($points) {
193     $endPtsOfContours = array();
194     $flags            = array();
195     $coords_x         = array();
196     $coords_y         = array();
197
198     $last_x = 0;
199     $last_y = 0;
200     $xMin   = $yMin = 0xFFFF;
201     $xMax   = $yMax = -0xFFFF;
202     foreach ($points as $i => $point) {
203       $flag = 0;
204       if ($point["onCurve"]) {
205         $flag |= self::ON_CURVE;
206       }
207
208       if ($point["endOfContour"]) {
209         $endPtsOfContours[] = $i;
210       }
211
212       // Simplified, we could do some optimizations
213       if ($point["x"] == $last_x) {
214         $flag |= self::THIS_X_IS_SAME;
215       }
216       else {
217         $x          = intval($point["x"]);
218         $xMin       = min($x, $xMin);
219         $xMax       = max($x, $xMax);
220         $coords_x[] = $x - $last_x; // int16
221       }
222
223       // Simplified, we could do some optimizations
224       if ($point["y"] == $last_y) {
225         $flag |= self::THIS_Y_IS_SAME;
226       }
227       else {
228         $y          = intval($point["y"]);
229         $yMin       = min($y, $yMin);
230         $yMax       = max($y, $yMax);
231         $coords_y[] = $y - $last_y; // int16
232       }
233
234       $flags[] = $flag;
235       $last_x  = $point["x"];
236       $last_y  = $point["y"];
237     }
238
239     $font = $this->getFont();
240
241     $l = 0;
242     $l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
243     $l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
244     $l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
245     $l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
246     $l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
247
248     // Simple glyf
249     $l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
250     $l += $font->writeUInt16(0); // instructionLength
251     $l += $font->w(array(self::uint8, count($flags)), $flags); // flags
252     $l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
253     $l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
254     return $l;
255   }
256
257   public function getSVGContours($points = null) {
258     $path = "";
259
260     if (!$points) {
261       if (empty($this->points)) {
262         $this->parseData();
263       }
264
265       $points = $this->points;
266     }
267
268     $length     = count($points);
269     $firstIndex = 0;
270     $count      = 0;
271
272     for ($i = 0; $i < $length; $i++) {
273       $count++;
274
275       if ($points[$i]["endOfContour"]) {
276         $path .= $this->getSVGPath($points, $firstIndex, $count);
277         $firstIndex = $i + 1;
278         $count      = 0;
279       }
280     }
281
282     return $path;
283   }
284
285   protected function getSVGPath($points, $startIndex, $count) {
286     $offset = 0;
287     $path   = "";
288
289     while ($offset < $count) {
290       $point    = $points[$startIndex + $offset % $count];
291       $point_p1 = $points[$startIndex + ($offset + 1) % $count];
292
293       if ($offset == 0) {
294         $path .= "M{$point['x']},{$point['y']} ";
295       }
296
297       if ($point["onCurve"]) {
298         if ($point_p1["onCurve"]) {
299           $path .= "L{$point_p1['x']},{$point_p1['y']} ";
300           $offset++;
301         }
302         else {
303           $point_p2 = $points[$startIndex + ($offset + 2) % $count];
304
305           if ($point_p2["onCurve"]) {
306             $path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
307           }
308           else {
309             $path .= "Q{$point_p1['x']},{$point_p1['y']}," . $this->midValue($point_p1['x'], $point_p2['x']) . "," . $this->midValue($point_p1['y'], $point_p2['y']) . " ";
310           }
311
312           $offset += 2;
313         }
314       }
315       else {
316         if ($point_p1["onCurve"]) {
317           $path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
318         }
319         else {
320           $path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
321         }
322
323         $offset++;
324       }
325     }
326
327     $path .= "z ";
328
329     return $path;
330   }
331
332   function midValue($a, $b) {
333     return $a + ($b - $a) / 2;
334   }
335 }