暂无描述

OutlineSimple.php 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. namespace FontLib\Glyph;
  10. /**
  11. * `glyf` font table.
  12. *
  13. * @package php-font-lib
  14. */
  15. class OutlineSimple extends Outline {
  16. const ON_CURVE = 0x01;
  17. const X_SHORT_VECTOR = 0x02;
  18. const Y_SHORT_VECTOR = 0x04;
  19. const REPEAT = 0x08;
  20. const THIS_X_IS_SAME = 0x10;
  21. const THIS_Y_IS_SAME = 0x20;
  22. public $instructions;
  23. public $points;
  24. function parseData() {
  25. parent::parseData();
  26. if (!$this->size) {
  27. return;
  28. }
  29. $font = $this->getFont();
  30. $noc = $this->numberOfContours;
  31. if ($noc == 0) {
  32. return;
  33. }
  34. $endPtsOfContours = $font->r(array(self::uint16, $noc));
  35. $instructionLength = $font->readUInt16();
  36. $this->instructions = $font->r(array(self::uint8, $instructionLength));
  37. $count = $endPtsOfContours[$noc - 1] + 1;
  38. // Flags
  39. $flags = array();
  40. for ($index = 0; $index < $count; $index++) {
  41. $flags[$index] = $font->readUInt8();
  42. if ($flags[$index] & self::REPEAT) {
  43. $repeats = $font->readUInt8();
  44. for ($i = 1; $i <= $repeats; $i++) {
  45. $flags[$index + $i] = $flags[$index];
  46. }
  47. $index += $repeats;
  48. }
  49. }
  50. $points = array();
  51. foreach ($flags as $i => $flag) {
  52. $points[$i]["onCurve"] = $flag & self::ON_CURVE;
  53. $points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
  54. }
  55. // X Coords
  56. $x = 0;
  57. for ($i = 0; $i < $count; $i++) {
  58. $flag = $flags[$i];
  59. if ($flag & self::THIS_X_IS_SAME) {
  60. if ($flag & self::X_SHORT_VECTOR) {
  61. $x += $font->readUInt8();
  62. }
  63. }
  64. else {
  65. if ($flag & self::X_SHORT_VECTOR) {
  66. $x -= $font->readUInt8();
  67. }
  68. else {
  69. $x += $font->readInt16();
  70. }
  71. }
  72. $points[$i]["x"] = $x;
  73. }
  74. // Y Coords
  75. $y = 0;
  76. for ($i = 0; $i < $count; $i++) {
  77. $flag = $flags[$i];
  78. if ($flag & self::THIS_Y_IS_SAME) {
  79. if ($flag & self::Y_SHORT_VECTOR) {
  80. $y += $font->readUInt8();
  81. }
  82. }
  83. else {
  84. if ($flag & self::Y_SHORT_VECTOR) {
  85. $y -= $font->readUInt8();
  86. }
  87. else {
  88. $y += $font->readInt16();
  89. }
  90. }
  91. $points[$i]["y"] = $y;
  92. }
  93. $this->points = $points;
  94. }
  95. public function splitSVGPath($path) {
  96. preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
  97. return $matches[0];
  98. }
  99. public function makePoints($path) {
  100. $path = $this->splitSVGPath($path);
  101. $l = count($path);
  102. $i = 0;
  103. $points = array();
  104. while ($i < $l) {
  105. switch ($path[$i]) {
  106. // moveTo
  107. case "M":
  108. $points[] = array(
  109. "onCurve" => true,
  110. "x" => $path[++$i],
  111. "y" => $path[++$i],
  112. "endOfContour" => false,
  113. );
  114. break;
  115. // lineTo
  116. case "L":
  117. $points[] = array(
  118. "onCurve" => true,
  119. "x" => $path[++$i],
  120. "y" => $path[++$i],
  121. "endOfContour" => false,
  122. );
  123. break;
  124. // quadraticCurveTo
  125. case "Q":
  126. $points[] = array(
  127. "onCurve" => false,
  128. "x" => $path[++$i],
  129. "y" => $path[++$i],
  130. "endOfContour" => false,
  131. );
  132. $points[] = array(
  133. "onCurve" => true,
  134. "x" => $path[++$i],
  135. "y" => $path[++$i],
  136. "endOfContour" => false,
  137. );
  138. break;
  139. // closePath
  140. /** @noinspection PhpMissingBreakStatementInspection */
  141. case "z":
  142. $points[count($points) - 1]["endOfContour"] = true;
  143. default:
  144. $i++;
  145. break;
  146. }
  147. }
  148. return $points;
  149. }
  150. function encode() {
  151. if (empty($this->points)) {
  152. return parent::encode();
  153. }
  154. return $this->size = $this->encodePoints($this->points);
  155. }
  156. public function encodePoints($points) {
  157. $endPtsOfContours = array();
  158. $flags = array();
  159. $coords_x = array();
  160. $coords_y = array();
  161. $last_x = 0;
  162. $last_y = 0;
  163. $xMin = $yMin = 0xFFFF;
  164. $xMax = $yMax = -0xFFFF;
  165. foreach ($points as $i => $point) {
  166. $flag = 0;
  167. if ($point["onCurve"]) {
  168. $flag |= self::ON_CURVE;
  169. }
  170. if ($point["endOfContour"]) {
  171. $endPtsOfContours[] = $i;
  172. }
  173. // Simplified, we could do some optimizations
  174. if ($point["x"] == $last_x) {
  175. $flag |= self::THIS_X_IS_SAME;
  176. }
  177. else {
  178. $x = intval($point["x"]);
  179. $xMin = min($x, $xMin);
  180. $xMax = max($x, $xMax);
  181. $coords_x[] = $x - $last_x; // int16
  182. }
  183. // Simplified, we could do some optimizations
  184. if ($point["y"] == $last_y) {
  185. $flag |= self::THIS_Y_IS_SAME;
  186. }
  187. else {
  188. $y = intval($point["y"]);
  189. $yMin = min($y, $yMin);
  190. $yMax = max($y, $yMax);
  191. $coords_y[] = $y - $last_y; // int16
  192. }
  193. $flags[] = $flag;
  194. $last_x = $point["x"];
  195. $last_y = $point["y"];
  196. }
  197. $font = $this->getFont();
  198. $l = 0;
  199. $l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
  200. $l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
  201. $l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
  202. $l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
  203. $l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
  204. // Simple glyf
  205. $l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
  206. $l += $font->writeUInt16(0); // instructionLength
  207. $l += $font->w(array(self::uint8, count($flags)), $flags); // flags
  208. $l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
  209. $l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
  210. return $l;
  211. }
  212. public function getSVGContours($points = null) {
  213. $path = "";
  214. if (!$points) {
  215. if (empty($this->points)) {
  216. $this->parseData();
  217. }
  218. $points = $this->points;
  219. }
  220. $length = (empty($points) ? 0 : count($points));
  221. $firstIndex = 0;
  222. $count = 0;
  223. for ($i = 0; $i < $length; $i++) {
  224. $count++;
  225. if ($points[$i]["endOfContour"]) {
  226. $path .= $this->getSVGPath($points, $firstIndex, $count);
  227. $firstIndex = $i + 1;
  228. $count = 0;
  229. }
  230. }
  231. return $path;
  232. }
  233. protected function getSVGPath($points, $startIndex, $count) {
  234. $offset = 0;
  235. $path = "";
  236. while ($offset < $count) {
  237. $point = $points[$startIndex + $offset % $count];
  238. $point_p1 = $points[$startIndex + ($offset + 1) % $count];
  239. if ($offset == 0) {
  240. $path .= "M{$point['x']},{$point['y']} ";
  241. }
  242. if ($point["onCurve"]) {
  243. if ($point_p1["onCurve"]) {
  244. $path .= "L{$point_p1['x']},{$point_p1['y']} ";
  245. $offset++;
  246. }
  247. else {
  248. $point_p2 = $points[$startIndex + ($offset + 2) % $count];
  249. if ($point_p2["onCurve"]) {
  250. $path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
  251. }
  252. else {
  253. $path .= "Q{$point_p1['x']},{$point_p1['y']}," . $this->midValue($point_p1['x'], $point_p2['x']) . "," . $this->midValue($point_p1['y'], $point_p2['y']) . " ";
  254. }
  255. $offset += 2;
  256. }
  257. }
  258. else {
  259. if ($point_p1["onCurve"]) {
  260. $path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
  261. }
  262. else {
  263. $path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
  264. }
  265. $offset++;
  266. }
  267. }
  268. $path .= "z ";
  269. return $path;
  270. }
  271. function midValue($a, $b) {
  272. return $a + ($b - $a) / 2;
  273. }
  274. }