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 $
10 namespace FontLib\Glyph;
15 * @package php-font-lib
17 class OutlineSimple extends Outline {
18 const ON_CURVE = 0x01;
19 const X_SHORT_VECTOR = 0x02;
20 const Y_SHORT_VECTOR = 0x04;
22 const THIS_X_IS_SAME = 0x10;
23 const THIS_Y_IS_SAME = 0x20;
28 function parseData() {
35 $font = $this->getFont();
37 $noc = $this->numberOfContours;
43 $endPtsOfContours = $font->r(array(self::uint16, $noc));
45 $instructionLength = $font->readUInt16();
46 $this->instructions = $font->r(array(self::uint8, $instructionLength));
48 $count = $endPtsOfContours[$noc - 1] + 1;
52 for ($index = 0; $index < $count; $index++) {
53 $flags[$index] = $font->readUInt8();
55 if ($flags[$index] & self::REPEAT) {
56 $repeats = $font->readUInt8();
58 for ($i = 1; $i <= $repeats; $i++) {
59 $flags[$index + $i] = $flags[$index];
67 foreach ($flags as $i => $flag) {
68 $points[$i]["onCurve"] = $flag & self::ON_CURVE;
69 $points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
74 for ($i = 0; $i < $count; $i++) {
77 if ($flag & self::THIS_X_IS_SAME) {
78 if ($flag & self::X_SHORT_VECTOR) {
79 $x += $font->readUInt8();
83 if ($flag & self::X_SHORT_VECTOR) {
84 $x -= $font->readUInt8();
87 $x += $font->readInt16();
91 $points[$i]["x"] = $x;
96 for ($i = 0; $i < $count; $i++) {
99 if ($flag & self::THIS_Y_IS_SAME) {
100 if ($flag & self::Y_SHORT_VECTOR) {
101 $y += $font->readUInt8();
105 if ($flag & self::Y_SHORT_VECTOR) {
106 $y -= $font->readUInt8();
109 $y += $font->readInt16();
113 $points[$i]["y"] = $y;
116 $this->points = $points;
119 public function splitSVGPath($path) {
120 preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
125 public function makePoints($path) {
126 $path = $this->splitSVGPath($path);
140 "endOfContour" => false,
150 "endOfContour" => false,
160 "endOfContour" => false,
166 "endOfContour" => false,
171 /** @noinspection PhpMissingBreakStatementInspection */
173 $points[count($points) - 1]["endOfContour"] = true;
185 if (empty($this->points)) {
186 return parent::encode();
189 return $this->size = $this->encodePoints($this->points);
192 public function encodePoints($points) {
193 $endPtsOfContours = array();
200 $xMin = $yMin = 0xFFFF;
201 $xMax = $yMax = -0xFFFF;
202 foreach ($points as $i => $point) {
204 if ($point["onCurve"]) {
205 $flag |= self::ON_CURVE;
208 if ($point["endOfContour"]) {
209 $endPtsOfContours[] = $i;
212 // Simplified, we could do some optimizations
213 if ($point["x"] == $last_x) {
214 $flag |= self::THIS_X_IS_SAME;
217 $x = intval($point["x"]);
218 $xMin = min($x, $xMin);
219 $xMax = max($x, $xMax);
220 $coords_x[] = $x - $last_x; // int16
223 // Simplified, we could do some optimizations
224 if ($point["y"] == $last_y) {
225 $flag |= self::THIS_Y_IS_SAME;
228 $y = intval($point["y"]);
229 $yMin = min($y, $yMin);
230 $yMax = max($y, $yMax);
231 $coords_y[] = $y - $last_y; // int16
235 $last_x = $point["x"];
236 $last_y = $point["y"];
239 $font = $this->getFont();
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
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
257 public function getSVGContours($points = null) {
261 if (empty($this->points)) {
265 $points = $this->points;
268 $length = count($points);
272 for ($i = 0; $i < $length; $i++) {
275 if ($points[$i]["endOfContour"]) {
276 $path .= $this->getSVGPath($points, $firstIndex, $count);
277 $firstIndex = $i + 1;
285 protected function getSVGPath($points, $startIndex, $count) {
289 while ($offset < $count) {
290 $point = $points[$startIndex + $offset % $count];
291 $point_p1 = $points[$startIndex + ($offset + 1) % $count];
294 $path .= "M{$point['x']},{$point['y']} ";
297 if ($point["onCurve"]) {
298 if ($point_p1["onCurve"]) {
299 $path .= "L{$point_p1['x']},{$point_p1['y']} ";
303 $point_p2 = $points[$startIndex + ($offset + 2) % $count];
305 if ($point_p2["onCurve"]) {
306 $path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
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']) . " ";
316 if ($point_p1["onCurve"]) {
317 $path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
320 $path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
332 function midValue($a, $b) {
333 return $a + ($b - $a) / 2;