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
10namespace FontLib\Glyph;
11
12/**
13 * Composite glyph outline
14 *
15 * @package php-font-lib
16 */
17class OutlineComposite extends Outline {
18  const ARG_1_AND_2_ARE_WORDS    = 0x0001;
19  const ARGS_ARE_XY_VALUES       = 0x0002;
20  const ROUND_XY_TO_GRID         = 0x0004;
21  const WE_HAVE_A_SCALE          = 0x0008;
22  const MORE_COMPONENTS          = 0x0020;
23  const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
24  const WE_HAVE_A_TWO_BY_TWO     = 0x0080;
25  const WE_HAVE_INSTRUCTIONS     = 0x0100;
26  const USE_MY_METRICS           = 0x0200;
27  const OVERLAP_COMPOUND         = 0x0400;
28
29  /**
30   * @var OutlineComponent[]
31   */
32  public $components = array();
33
34  function getGlyphIDs() {
35    if (empty($this->components)) {
36      $this->parseData();
37    }
38
39    $glyphIDs = array();
40    foreach ($this->components as $_component) {
41      $glyphIDs[] = $_component->glyphIndex;
42
43      $_glyph   = $this->table->data[$_component->glyphIndex];
44
45      if ($_glyph !== $this) {
46        $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
47      }
48    }
49
50    return $glyphIDs;
51  }
52
53  /*function parse() {
54    //$this->parseData();
55  }*/
56
57  function parseData() {
58    parent::parseData();
59
60    $font = $this->getFont();
61
62    do {
63      $flags      = $font->readUInt16();
64      $glyphIndex = $font->readUInt16();
65
66      $a = 1.0;
67      $b = 0.0;
68      $c = 0.0;
69      $d = 1.0;
70      $e = 0.0;
71      $f = 0.0;
72
73      $point_compound  = null;
74      $point_component = null;
75
76      $instructions = null;
77
78      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
79        if ($flags & self::ARGS_ARE_XY_VALUES) {
80          $e = $font->readInt16();
81          $f = $font->readInt16();
82        }
83        else {
84          $point_compound  = $font->readUInt16();
85          $point_component = $font->readUInt16();
86        }
87      }
88      else {
89        if ($flags & self::ARGS_ARE_XY_VALUES) {
90          $e = $font->readInt8();
91          $f = $font->readInt8();
92        }
93        else {
94          $point_compound  = $font->readUInt8();
95          $point_component = $font->readUInt8();
96        }
97      }
98
99      if ($flags & self::WE_HAVE_A_SCALE) {
100        $a = $d = $font->readInt16();
101      }
102      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
103        $a = $font->readInt16();
104        $d = $font->readInt16();
105      }
106      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
107        $a = $font->readInt16();
108        $b = $font->readInt16();
109        $c = $font->readInt16();
110        $d = $font->readInt16();
111      }
112
113      //if ($flags & self::WE_HAVE_INSTRUCTIONS) {
114      //
115      //}
116
117      $component                  = new OutlineComponent();
118      $component->flags           = $flags;
119      $component->glyphIndex      = $glyphIndex;
120      $component->a               = $a;
121      $component->b               = $b;
122      $component->c               = $c;
123      $component->d               = $d;
124      $component->e               = $e;
125      $component->f               = $f;
126      $component->point_compound  = $point_compound;
127      $component->point_component = $point_component;
128      $component->instructions    = $instructions;
129
130      $this->components[] = $component;
131    } while ($flags & self::MORE_COMPONENTS);
132  }
133
134  function encode() {
135    $font = $this->getFont();
136
137    $gids = $font->getSubset();
138
139    $size = $font->writeInt16(-1);
140    $size += $font->writeFWord($this->xMin);
141    $size += $font->writeFWord($this->yMin);
142    $size += $font->writeFWord($this->xMax);
143    $size += $font->writeFWord($this->yMax);
144
145    foreach ($this->components as $_i => $_component) {
146      $flags = 0;
147      if ($_component->point_component === null && $_component->point_compound === null) {
148        $flags |= self::ARGS_ARE_XY_VALUES;
149
150        if (abs($_component->e) > 0x7F || abs($_component->f) > 0x7F) {
151          $flags |= self::ARG_1_AND_2_ARE_WORDS;
152        }
153      }
154      elseif ($_component->point_component > 0xFF || $_component->point_compound > 0xFF) {
155        $flags |= self::ARG_1_AND_2_ARE_WORDS;
156      }
157
158      if ($_component->b == 0 && $_component->c == 0) {
159        if ($_component->a == $_component->d) {
160          if ($_component->a != 1.0) {
161            $flags |= self::WE_HAVE_A_SCALE;
162          }
163        }
164        else {
165          $flags |= self::WE_HAVE_AN_X_AND_Y_SCALE;
166        }
167      }
168      else {
169        $flags |= self::WE_HAVE_A_TWO_BY_TWO;
170      }
171
172      if ($_i < count($this->components) - 1) {
173        $flags |= self::MORE_COMPONENTS;
174      }
175
176      $size += $font->writeUInt16($flags);
177
178      $new_gid = array_search($_component->glyphIndex, $gids);
179      $size += $font->writeUInt16($new_gid);
180
181      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
182        if ($flags & self::ARGS_ARE_XY_VALUES) {
183          $size += $font->writeInt16($_component->e);
184          $size += $font->writeInt16($_component->f);
185        }
186        else {
187          $size += $font->writeUInt16($_component->point_compound);
188          $size += $font->writeUInt16($_component->point_component);
189        }
190      }
191      else {
192        if ($flags & self::ARGS_ARE_XY_VALUES) {
193          $size += $font->writeInt8($_component->e);
194          $size += $font->writeInt8($_component->f);
195        }
196        else {
197          $size += $font->writeUInt8($_component->point_compound);
198          $size += $font->writeUInt8($_component->point_component);
199        }
200      }
201
202      if ($flags & self::WE_HAVE_A_SCALE) {
203        $size += $font->writeInt16($_component->a);
204      }
205      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
206        $size += $font->writeInt16($_component->a);
207        $size += $font->writeInt16($_component->d);
208      }
209      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
210        $size += $font->writeInt16($_component->a);
211        $size += $font->writeInt16($_component->b);
212        $size += $font->writeInt16($_component->c);
213        $size += $font->writeInt16($_component->d);
214      }
215    }
216
217    return $size;
218  }
219
220  public function getSVGContours() {
221    $contours = array();
222
223    /** @var \FontLib\Table\Type\glyf $glyph_data */
224    $glyph_data = $this->getFont()->getTableObject("glyf");
225
226    /** @var Outline[] $glyphs */
227    $glyphs = $glyph_data->data;
228
229    foreach ($this->components as $component) {
230      $_glyph = $glyphs[$component->glyphIndex];
231
232      if ($_glyph !== $this) {
233        $contours[] = array(
234          "contours"  => $_glyph->getSVGContours(),
235          "transform" => $component->getMatrix(),
236        );
237      }
238    }
239
240    return $contours;
241  }
242}