PageRenderTime 80ms CodeModel.GetById 21ms RepoModel.GetById 5ms app.codeStats 0ms

/generate/lib/VMLPath.php

http://github.com/sorccu/cufon
PHP | 415 lines | 286 code | 55 blank | 74 comment | 17 complexity | 819343975a7e9a2c2e066b105169868d MD5 | raw file
  1. <?php
  2. class VMLPath {
  3. /**
  4. * @param string $type
  5. * @param array $coords
  6. * @return string
  7. */
  8. private static function commandToString($type, $coords)
  9. {
  10. if (!is_array($coords))
  11. {
  12. $coords = func_get_args();
  13. array_shift($coords);
  14. }
  15. return $type . implode(',', $coords);
  16. }
  17. /**
  18. * @param string $path
  19. * @return VMLPath
  20. */
  21. public static function fromSVG($path)
  22. {
  23. $vml = new VMLPath();
  24. $matches = array();
  25. if (!preg_match_all('/([a-zA-Z])([0-9. \-,]*)/', $path, $matches, PREG_SET_ORDER))
  26. {
  27. return $vml;
  28. }
  29. $at = (object) array('x' => 0, 'y' => 0);
  30. $cp = (object) array('x' => 0, 'y' => 0);
  31. $previous = null;
  32. $zm = false;
  33. for (; $set = current($matches); next($matches))
  34. {
  35. list($cmd, $coords) = array($set[1], array_map('floatval', preg_split('/(?:,|\s+)/', trim($set[2]))));
  36. switch ($cmd)
  37. {
  38. case 'z':
  39. case 'Z':
  40. if (strcasecmp($previous, 'm') === 0)
  41. {
  42. $vml->pop();
  43. }
  44. if ($zm) // ignore chained zm-commands
  45. {
  46. $vml->pop();
  47. }
  48. $vml->closePath();
  49. break;
  50. case 'M':
  51. if (strcasecmp($previous, 'm') === 0)
  52. {
  53. $vml->pop();
  54. }
  55. $vml->moveTo(
  56. $at->x = $coords[0],
  57. $at->y = $coords[1]
  58. );
  59. break;
  60. case 'L':
  61. $vml->lineTo(
  62. $at->x = $coords[0],
  63. $at->y = $coords[1]
  64. );
  65. break;
  66. case 'l':
  67. $vml->lineTo(
  68. $at->x += $coords[0],
  69. $at->y += $coords[1]
  70. );
  71. break;
  72. case 'H':
  73. $vml->lineTo(
  74. $at->x = $coords[0],
  75. $at->y
  76. );
  77. break;
  78. case 'h':
  79. $vml->lineTo(
  80. $at->x += $coords[0],
  81. $at->y
  82. );
  83. break;
  84. case 'V':
  85. $vml->lineTo(
  86. $at->x,
  87. $at->y = $coords[0]
  88. );
  89. break;
  90. case 'v':
  91. $vml->lineTo(
  92. $at->x,
  93. $at->y += $coords[0]
  94. );
  95. break;
  96. case 'C':
  97. $vml->bezierCurveTo(
  98. $coords[0],
  99. $coords[1],
  100. $cp->x = $coords[2],
  101. $cp->y = $coords[3],
  102. $at->x = $coords[4],
  103. $at->y = $coords[5]
  104. );
  105. break;
  106. case 'c':
  107. $vml->bezierCurveTo(
  108. $at->x + $coords[0],
  109. $at->y + $coords[1],
  110. $cp->x = $at->x + $coords[2],
  111. $cp->y = $at->y + $coords[3],
  112. $at->x += $coords[4],
  113. $at->y += $coords[5]
  114. );
  115. break;
  116. case 'S':
  117. if (!$previous || !preg_match('/^[CcSs]$/', $previous))
  118. {
  119. $cp->x = $at->x;
  120. $cp->y = $at->y;
  121. }
  122. $vml->bezierCurveTo(
  123. $at->x + ($at->x - $cp->x),
  124. $at->y + ($at->y - $cp->y),
  125. $cp->x = $coords[0],
  126. $cp->y = $coords[1],
  127. $at->x = $coords[2],
  128. $at->y = $coords[3]
  129. );
  130. break;
  131. case 's':
  132. if (!$previous || !preg_match('/^[CcSs]$/', $previous))
  133. {
  134. $cp->x = $at->x;
  135. $cp->y = $at->y;
  136. }
  137. $vml->bezierCurveTo(
  138. $at->x + ($at->x - $cp->x),
  139. $at->y + ($at->y - $cp->y),
  140. $cp->x = $at->x + $coords[0],
  141. $cp->y = $at->y + $coords[1],
  142. $at->x += $coords[2],
  143. $at->y += $coords[3]
  144. );
  145. break;
  146. case 'Q':
  147. $vml->quadraticCurveTo(
  148. $cp->x = $coords[0],
  149. $cp->y = $coords[1],
  150. $at->x = $coords[2],
  151. $at->y = $coords[3]
  152. );
  153. break;
  154. case 'q':
  155. $vml->quadraticCurveTo(
  156. $cp->x = $at->x + $coords[0],
  157. $cp->y = $at->y + $coords[1],
  158. $at->x += $coords[2],
  159. $at->y += $coords[3]
  160. );
  161. break;
  162. case 'T':
  163. if (!$previous || !preg_match('/^[QqTt]$/', $previous))
  164. {
  165. $cp->x = $at->x;
  166. $cp->y = $at->y;
  167. }
  168. $vml->quadraticCurveTo(
  169. $cp->x = $at->x + ($at->x - $cp->x),
  170. $cp->y = $at->y + ($at->y - $cp->y),
  171. $at->x = $coords[0],
  172. $at->y = $coords[1]
  173. );
  174. break;
  175. case 't':
  176. if (!$previous || !preg_match('/^[QqTt]$/', $previous))
  177. {
  178. $cp->x = $at->x;
  179. $cp->y = $at->y;
  180. }
  181. $vml->quadraticCurveTo(
  182. $cp->x = $at->x + ($at->x - $cp->x),
  183. $cp->y = $at->y + ($at->y - $cp->y),
  184. $at->x += $coords[0],
  185. $at->y += $coords[1]
  186. );
  187. break;
  188. case 'A':
  189. case 'a':
  190. break;
  191. }
  192. $zm = strcasecmp($cmd, 'm') === 0 && strcasecmp($previous, 'z') === 0;
  193. $previous = $cmd;
  194. }
  195. $vml->endPath();
  196. return $vml;
  197. }
  198. private $parts = array();
  199. private $atX = 0;
  200. private $atY = 0;
  201. private $cpX = 0;
  202. private $cpY = 0;
  203. private $remainderX = 0;
  204. private $remainderY = 0;
  205. /**
  206. * @return void
  207. */
  208. public function __construct()
  209. {
  210. }
  211. /**
  212. * @return string
  213. */
  214. public function __toString()
  215. {
  216. return implode('', $this->parts);
  217. }
  218. /**
  219. * @param float $cp1x
  220. * @param float $cp1y
  221. * @param float $cp2x
  222. * @param float $cp2y
  223. * @param float $toX
  224. * @param float $toY
  225. * @return VMLPath
  226. */
  227. public function bezierCurveTo($cp1x, $cp1y, $cp2x, $cp2y, $toX, $toY)
  228. {
  229. $this->parts[] = $this->draw('v', $cp1x, $cp1y, $cp2x, $cp2y, $toX, $toY);
  230. return $this;
  231. }
  232. /**
  233. * @return VMLPath
  234. */
  235. public function closePath()
  236. {
  237. $this->parts[] = 'x';
  238. return $this;
  239. }
  240. /**
  241. * Returns a shortened, relative VML path part
  242. *
  243. * @param string $type
  244. * @param mixed $coords
  245. * @return string
  246. */
  247. private function draw($type, $coords)
  248. {
  249. if (!is_array($coords))
  250. {
  251. $coords = func_get_args();
  252. array_shift($coords);
  253. }
  254. $toY = array_pop($coords);
  255. $toX = array_pop($coords);
  256. $parts = array();
  257. foreach ($coords as $i => $coord)
  258. {
  259. $parts[] = $coord === 0 ? '' : round($coord - ($i % 2 ? $this->atY : $this->atX));
  260. }
  261. $parts[] = $this->moveX($toX);
  262. $parts[] = $this->moveY($toY);
  263. return self::commandToString($type, $parts);
  264. }
  265. /**
  266. * @return VMLPath
  267. */
  268. public function endPath()
  269. {
  270. $this->parts[] = 'e';
  271. return $this;
  272. }
  273. /**
  274. * @param float $toX
  275. * @param float $toY
  276. * @return VMLPath
  277. */
  278. public function lineTo($toX, $toY)
  279. {
  280. $this->parts[] = $this->draw('r', $toX, $toY);
  281. return $this;
  282. }
  283. /**
  284. * @param float $toX
  285. * @param float $toY
  286. * @return VMLPath
  287. */
  288. public function moveTo($toX, $toY)
  289. {
  290. $this->moveX($toX);
  291. $this->moveY($toY);
  292. $this->parts[] = self::commandToString('m', $this->atX, $this->atY);
  293. return $this;
  294. }
  295. /**
  296. * @param float $to
  297. * @return int
  298. */
  299. private function moveX($to)
  300. {
  301. $delta = $to - $this->atX;
  302. $rounded = round($delta);
  303. $this->atX += $rounded;
  304. return $rounded;
  305. }
  306. /**
  307. * @param float $to
  308. * @return int
  309. */
  310. private function moveY($to)
  311. {
  312. $delta = $to - $this->atY;
  313. $rounded = round($delta);
  314. $this->atY += $rounded;
  315. return $rounded;
  316. }
  317. /**
  318. * Removes the last point from the path. Note: this does NOT
  319. * reset atX, atY and others to their prior values.
  320. *
  321. * @return VMLPath
  322. */
  323. public function pop()
  324. {
  325. array_pop($this->parts);
  326. return $this;
  327. }
  328. /**
  329. * IE has some trouble drawing quadratic beziers so we'll just convert
  330. * them to cubic ones which it can handle just fine.
  331. *
  332. * @link http://developer.mozilla.org/en/Canvas_tutorial/Drawing_shapes#Firefox_1.5_quadraticCurveTo()_bug_workaround
  333. * @param float $atX
  334. * @param float $atY
  335. * @param float $cpX
  336. * @param float $cpY
  337. * @param float $toX
  338. * @param float $toY
  339. * @return VMLPath
  340. */
  341. private function quadraticCurveTo($cpX, $cpY, $toX, $toY)
  342. {
  343. $cp1 = (object) array(
  344. 'x' => $this->atX + 2 / 3 * ($cpX - $this->atX),
  345. 'y' => $this->atY + 2 / 3 * ($cpY - $this->atY)
  346. );
  347. $cp2 = (object) array(
  348. 'x' => $cp1->x + ($toX - $this->atX) / 3,
  349. 'y' => $cp1->y + ($toY - $this->atY) / 3
  350. );
  351. return $this->bezierCurveTo(
  352. $cp1->x,
  353. $cp1->y,
  354. $cp2->x,
  355. $cp2->y,
  356. $toX,
  357. $toY
  358. );
  359. }
  360. }