PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 2ms

/src/scenegraph/svg_attributes.c

https://github.com/svettom/gpac-1
C | 6443 lines | 5960 code | 325 blank | 158 comment | 1469 complexity | a2132d966357a269fa240b28914370a7 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * GPAC Multimedia Framework
  3. *
  4. * Authors: Cyril Concolato - Jean le Feuvre - Jean-Claude Moissinac
  5. * Copyright (c) 2005-200X ENST
  6. * All rights reserved
  7. *
  8. * This file is part of GPAC / SVG Loader module
  9. *
  10. * GPAC is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published by
  12. * the Free Software Foundation; either version 2, or (at your option)
  13. * any later version.
  14. *
  15. * GPAC is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; see the file COPYING. If not, write to
  22. * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23. *
  24. */
  25. #include <gpac/base_coding.h>
  26. #include <gpac/events.h>
  27. #include <gpac/nodes_svg.h>
  28. #ifndef GPAC_DISABLE_SVG
  29. #include <gpac/internal/scenegraph_dev.h>
  30. #define DUMP_COORDINATES 1
  31. static const struct dom_event_def {u32 event; const char *name; u32 category; } defined_dom_events [] =
  32. {
  33. { GF_EVENT_ABORT, "abort", GF_DOM_EVENT_DOM },
  34. { GF_EVENT_ERROR, "error", GF_DOM_EVENT_DOM },
  35. { GF_EVENT_LOAD, "load", GF_DOM_EVENT_DOM },
  36. { GF_EVENT_UNLOAD, "unload", GF_DOM_EVENT_DOM },
  37. /*focus - we differentiate from UI/key events to avoid browing focus if no listener is on place*/
  38. { GF_EVENT_FOCUSIN, "DOMFocusIn", GF_DOM_EVENT_FOCUS },
  39. { GF_EVENT_FOCUSIN, "focusin", GF_DOM_EVENT_FOCUS },
  40. { GF_EVENT_FOCUSOUT, "DOMFocusOut", GF_DOM_EVENT_FOCUS },
  41. { GF_EVENT_FOCUSOUT, "focusout", GF_DOM_EVENT_FOCUS },
  42. { GF_EVENT_CHANGE, "change", GF_DOM_EVENT_FOCUS },
  43. { GF_EVENT_FOCUS, "focus", GF_DOM_EVENT_FOCUS },
  44. { GF_EVENT_BLUR, "blur", GF_DOM_EVENT_FOCUS },
  45. /*key events*/
  46. { GF_EVENT_KEYDOWN, "keydown", GF_DOM_EVENT_KEY },
  47. { GF_EVENT_KEYDOWN, "accesskey", GF_DOM_EVENT_KEY },
  48. { GF_EVENT_KEYDOWN, "keypress", GF_DOM_EVENT_KEY },
  49. { GF_EVENT_KEYUP, "keyup", GF_DOM_EVENT_KEY },
  50. { GF_EVENT_LONGKEYPRESS, "longaccesskey", GF_DOM_EVENT_KEY },
  51. { GF_EVENT_CLICK, "click", GF_DOM_EVENT_MOUSE },
  52. { GF_EVENT_DBLCLICK, "dblclick", GF_DOM_EVENT_MOUSE },
  53. { GF_EVENT_MOUSEDOWN, "mousedown", GF_DOM_EVENT_MOUSE },
  54. { GF_EVENT_MOUSEMOVE, "mousemove", GF_DOM_EVENT_MOUSE },
  55. { GF_EVENT_MOUSEOUT, "mouseout", GF_DOM_EVENT_MOUSE },
  56. { GF_EVENT_MOUSEOVER, "mouseover", GF_DOM_EVENT_MOUSE },
  57. { GF_EVENT_MOUSEUP, "mouseup", GF_DOM_EVENT_MOUSE },
  58. { GF_EVENT_MOUSEWHEEL, "wheel", GF_DOM_EVENT_MOUSE },
  59. { GF_EVENT_MOUSEWHEEL, "SVGMousewheel", GF_DOM_EVENT_MOUSE },
  60. /*activate is not a basic DOM but a MOUSE and KEY event*/
  61. { GF_EVENT_ACTIVATE, "activate", GF_DOM_EVENT_MOUSE | GF_DOM_EVENT_KEY },
  62. { GF_EVENT_ACTIVATE, "DOMActivate", GF_DOM_EVENT_MOUSE | GF_DOM_EVENT_KEY },
  63. /*text events*/
  64. { GF_EVENT_TEXTINPUT, "textInput", GF_DOM_EVENT_TEXT },
  65. { GF_EVENT_TEXTSELECT, "select", GF_DOM_EVENT_TEXT },
  66. /*SMIL events*/
  67. { GF_EVENT_BEGIN, "begin", GF_DOM_EVENT_FAKE },
  68. { GF_EVENT_BEGIN_EVENT, "beginEvent", GF_DOM_EVENT_SMIL },
  69. { GF_EVENT_END, "end", GF_DOM_EVENT_FAKE },
  70. { GF_EVENT_END_EVENT, "endEvent", GF_DOM_EVENT_SMIL },
  71. { GF_EVENT_REPEAT, "repeat", GF_DOM_EVENT_FAKE },
  72. { GF_EVENT_REPEAT_EVENT, "repeatEvent", GF_DOM_EVENT_SMIL },
  73. /*all SVG/HTML/... UI events*/
  74. { GF_EVENT_RESIZE, "resize", GF_DOM_EVENT_UI },
  75. { GF_EVENT_SCROLL, "scroll", GF_DOM_EVENT_UI },
  76. { GF_EVENT_ZOOM, "zoom", GF_DOM_EVENT_UI },
  77. { GF_EVENT_LOAD, "SVGLoad", GF_DOM_EVENT_DOM },
  78. { GF_EVENT_RESIZE, "SVGResize", GF_DOM_EVENT_UI },
  79. { GF_EVENT_SCROLL, "SVGScroll", GF_DOM_EVENT_UI },
  80. { GF_EVENT_ZOOM, "SVGZoom", GF_DOM_EVENT_UI },
  81. /*mutation events and DCCI*/
  82. { GF_EVENT_TREE_MODIFIED, "DOMSubtreeModified", GF_DOM_EVENT_MUTATION },
  83. { GF_EVENT_NODE_INSERTED, "DOMNodeInserted", GF_DOM_EVENT_MUTATION },
  84. { GF_EVENT_NODE_REMOVED, "DOMNodeRemoved", GF_DOM_EVENT_MUTATION },
  85. { GF_EVENT_NODE_REMOVED_DOC, "DOMNodeRemovedFromDocument", GF_DOM_EVENT_MUTATION },
  86. { GF_EVENT_NODE_INSERTED_DOC, "DOMNodeInsertedIntoDocument", GF_DOM_EVENT_MUTATION },
  87. { GF_EVENT_ATTR_MODIFIED, "DOMAttrModified", GF_DOM_EVENT_MUTATION },
  88. { GF_EVENT_CHAR_DATA_MODIFIED, "DOMCharacterDataModified", GF_DOM_EVENT_MUTATION },
  89. { GF_EVENT_NODE_NAME_CHANGED, "DOMElementNameChanged", GF_DOM_EVENT_MUTATION },
  90. { GF_EVENT_ATTR_NAME_CHANGED, "DOMAttributeNameChanged", GF_DOM_EVENT_MUTATION },
  91. { GF_EVENT_DCCI_PROP_CHANGE, "DCCI-prop-change", GF_DOM_EVENT_MUTATION },
  92. /*LASeR events - some events are attached to other categorues*/
  93. { GF_EVENT_ACTIVATED, "activatedEvent", GF_DOM_EVENT_LASER },
  94. { GF_EVENT_DEACTIVATED, "deactivatedEvent", GF_DOM_EVENT_LASER },
  95. { GF_EVENT_EXECUTION_TIME, "executionTime", GF_DOM_EVENT_FAKE },
  96. { GF_EVENT_PAUSE, "pause", GF_DOM_EVENT_SMIL },
  97. { GF_EVENT_PAUSED_EVENT, "pausedEvent", GF_DOM_EVENT_SMIL },
  98. { GF_EVENT_PLAY, "play", GF_DOM_EVENT_SMIL },
  99. { GF_EVENT_RESUME_EVENT, "resumedEvent", GF_DOM_EVENT_SMIL },
  100. { GF_EVENT_REPEAT_KEY, "repeatKey", GF_DOM_EVENT_KEY },
  101. { GF_EVENT_SHORT_ACCESSKEY, "shortAccessKey", GF_DOM_EVENT_KEY },
  102. /*LASeR unofficial events*/
  103. { GF_EVENT_BATTERY, "battery", GF_DOM_EVENT_LASER },
  104. { GF_EVENT_CPU, "cpu", GF_DOM_EVENT_LASER },
  105. /*MediaAccess events*/
  106. #if 0
  107. { GF_EVENT_MEDIA_BEGIN_SESSION_SETUP, "BeginSessionSetup", GF_DOM_EVENT_MEDIA_ACCESS },
  108. { GF_EVENT_MEDIA_END_SESSION_SETUP, "EndSessionSetup", GF_DOM_EVENT_MEDIA_ACCESS },
  109. { GF_EVENT_MEDIA_DATA_REQUEST, "DataRequest", GF_DOM_EVENT_MEDIA_ACCESS },
  110. { GF_EVENT_MEDIA_PLAYABLE, "Playable", GF_DOM_EVENT_MEDIA_ACCESS },
  111. { GF_EVENT_MEDIA_NOT_PLAYABLE, "NotPlayable", GF_DOM_EVENT_MEDIA_ACCESS },
  112. { GF_EVENT_MEDIA_DATA_PROGRESS, "DataReceptionProgress", GF_DOM_EVENT_MEDIA_ACCESS },
  113. { GF_EVENT_MEDIA_END_OF_DATA, "EndOfDataReception", GF_DOM_EVENT_MEDIA_ACCESS },
  114. { GF_EVENT_MEDIA_STOP, "Stop", GF_DOM_EVENT_MEDIA_ACCESS },
  115. { GF_EVENT_MEDIA_ERROR, "Error", GF_DOM_EVENT_MEDIA_ACCESS },
  116. #endif
  117. { GF_EVENT_MEDIA_SETUP_BEGIN, "setupbegin", GF_DOM_EVENT_MEDIA},
  118. { GF_EVENT_MEDIA_SETUP_DONE, "setupdone", GF_DOM_EVENT_MEDIA},
  119. { GF_EVENT_MEDIA_LOAD_START, "loadstart", GF_DOM_EVENT_MEDIA },
  120. { GF_EVENT_MEDIA_LOAD_DONE, "loaddone", GF_DOM_EVENT_MEDIA },
  121. { GF_EVENT_MEDIA_PROGRESS, "progress", GF_DOM_EVENT_MEDIA },
  122. { GF_EVENT_MEDIA_SUSPEND, "suspend", GF_DOM_EVENT_MEDIA },
  123. { GF_EVENT_ABORT, "abort", GF_DOM_EVENT_MEDIA },
  124. { GF_EVENT_ERROR, "error", GF_DOM_EVENT_MEDIA },
  125. { GF_EVENT_MEDIA_EMPTIED, "emptied", GF_DOM_EVENT_MEDIA },
  126. { GF_EVENT_MEDIA_STALLED, "stalled", GF_DOM_EVENT_MEDIA },
  127. { GF_EVENT_MEDIA_LOADED_METADATA, "loadedmetadata", GF_DOM_EVENT_MEDIA },
  128. { GF_EVENT_MEDIA_LODADED_DATA, "loadeddata", GF_DOM_EVENT_MEDIA },
  129. { GF_EVENT_MEDIA_CANPLAY, "canplay", GF_DOM_EVENT_MEDIA },
  130. { GF_EVENT_MEDIA_CANPLAYTHROUGH, "canplaythrough", GF_DOM_EVENT_MEDIA },
  131. { GF_EVENT_MEDIA_PLAYING, "playing", GF_DOM_EVENT_MEDIA },
  132. { GF_EVENT_MEDIA_WAITING, "waiting", GF_DOM_EVENT_MEDIA },
  133. { GF_EVENT_MEDIA_SEEKING, "seeking", GF_DOM_EVENT_MEDIA },
  134. { GF_EVENT_MEDIA_SEEKED, "seeked", GF_DOM_EVENT_MEDIA },
  135. { GF_EVENT_MEDIA_ENDED, "ended", GF_DOM_EVENT_MEDIA },
  136. { GF_EVENT_MEDIA_DURATION_CHANGED, "durationchanged", GF_DOM_EVENT_MEDIA },
  137. { GF_EVENT_MEDIA_TIME_UPDATE, "timeupdate", GF_DOM_EVENT_MEDIA },
  138. { GF_EVENT_PLAY, "play", GF_DOM_EVENT_MEDIA },
  139. { GF_EVENT_PAUSE, "pause", GF_DOM_EVENT_MEDIA },
  140. { GF_EVENT_MEDIA_RATECHANGE, "ratechange", GF_DOM_EVENT_MEDIA },
  141. { GF_EVENT_MEDIA_VOLUME_CHANGED, "volumechange", GF_DOM_EVENT_MEDIA },
  142. /*GPAC internals*/
  143. { GF_EVENT_SCENE_ATTACHED, "gpac_scene_attached", GF_DOM_EVENT_DOM },
  144. { GF_EVENT_VP_RESIZE, "gpac_vp_changed", GF_DOM_EVENT_DOM },
  145. };
  146. GF_EXPORT
  147. u32 gf_dom_event_type_by_name(const char *name)
  148. {
  149. u32 i, count;
  150. count = sizeof(defined_dom_events) / sizeof(struct dom_event_def);
  151. if (!name) return GF_EVENT_UNKNOWN;
  152. if ((name[0]=='o') && (name[1]=='n')) name += 2;
  153. for (i=0;i<count;i++) {
  154. if (!strcmp(name, defined_dom_events[i].name))
  155. return defined_dom_events[i].event;
  156. }
  157. return GF_EVENT_UNKNOWN;
  158. }
  159. const char *gf_dom_event_get_name(u32 type)
  160. {
  161. u32 i, count;
  162. count = sizeof(defined_dom_events) / sizeof(struct dom_event_def);
  163. for (i=0;i<count;i++) {
  164. if (defined_dom_events[i].event == type)
  165. return defined_dom_events[i].name;
  166. }
  167. return "unknown";
  168. }
  169. u32 gf_dom_event_get_category(u32 type)
  170. {
  171. u32 i, count;
  172. count = sizeof(defined_dom_events) / sizeof(struct dom_event_def);
  173. for (i=0;i<count;i++) {
  174. if (defined_dom_events[i].event == type)
  175. return defined_dom_events[i].category;
  176. }
  177. return 0;
  178. }
  179. static const struct predef_keyid {u32 key_code; const char *name; } predefined_key_identifiers[] =
  180. {
  181. { GF_KEY_ACCEPT, "Accept" },
  182. { GF_KEY_AGAIN, "Again" },
  183. { GF_KEY_ALLCANDIDATES, "AllCandidates" },
  184. { GF_KEY_ALPHANUM, "Alphanumeric" },
  185. { GF_KEY_ALT, "Alt" },
  186. { GF_KEY_ALTGRAPH, "AltGraph" },
  187. { GF_KEY_APPS, "Apps" },
  188. { GF_KEY_ATTN, "Attn" },
  189. { GF_KEY_BROWSERBACK, "BrowserBack" },
  190. { GF_KEY_BROWSERFAVORITES, "BrowserFavorites" },
  191. { GF_KEY_BROWSERFORWARD, "BrowserForward" },
  192. { GF_KEY_BROWSERHOME, "BrowserHome" },
  193. { GF_KEY_BROWSERREFRESH, "BrowserRefresh" },
  194. { GF_KEY_BROWSERSEARCH, "BrowserSearch" },
  195. { GF_KEY_BROWSERSTOP, "BrowserStop" },
  196. { GF_KEY_CAPSLOCK, "CapsLock" },
  197. { GF_KEY_CLEAR, "Clear" },
  198. { GF_KEY_CODEINPUT, "CodeInput" },
  199. { GF_KEY_COMPOSE, "Compose" },
  200. { GF_KEY_CONTROL, "Control" },
  201. { GF_KEY_CRSEL, "Crsel" },
  202. { GF_KEY_CONVERT, "Convert" },
  203. { GF_KEY_COPY, "Copy" },
  204. { GF_KEY_CUT, "Cut" },
  205. { GF_KEY_DOWN, "Down" },
  206. { GF_KEY_END, "End" },
  207. { GF_KEY_ENTER, "Enter" },
  208. { GF_KEY_ERASEEOF, "EraseEof" },
  209. { GF_KEY_EXECUTE, "Execute" },
  210. { GF_KEY_EXSEL, "Exsel" },
  211. { GF_KEY_F1, "F1" },
  212. { GF_KEY_F2, "F2" },
  213. { GF_KEY_F3, "F3" },
  214. { GF_KEY_F4, "F4" },
  215. { GF_KEY_F5, "F5" },
  216. { GF_KEY_F6, "F6" },
  217. { GF_KEY_F7, "F7" },
  218. { GF_KEY_F8, "F8" },
  219. { GF_KEY_F9, "F9" },
  220. { GF_KEY_F10, "F10" },
  221. { GF_KEY_F11, "F11" },
  222. { GF_KEY_F12, "F12" },
  223. { GF_KEY_F13, "F13" },
  224. { GF_KEY_F14, "F14" },
  225. { GF_KEY_F15, "F15" },
  226. { GF_KEY_F16, "F16" },
  227. { GF_KEY_F17, "F17" },
  228. { GF_KEY_F18, "F18" },
  229. { GF_KEY_F19, "F19" },
  230. { GF_KEY_F20, "F20" },
  231. { GF_KEY_F21, "F21" },
  232. { GF_KEY_F22, "F22" },
  233. { GF_KEY_F23, "F23" },
  234. { GF_KEY_F24, "F24" },
  235. { GF_KEY_FINALMODE, "FinalMode" },
  236. { GF_KEY_FIND, "Find" },
  237. { GF_KEY_FULLWIDTH, "FullWidth" },
  238. { GF_KEY_HALFWIDTH, "HalfWidth" },
  239. { GF_KEY_HANGULMODE, "HangulMode" },
  240. { GF_KEY_HANJAMODE, "HanjaMode" },
  241. { GF_KEY_HELP, "Help" },
  242. { GF_KEY_HIRAGANA, "Hiragana" },
  243. { GF_KEY_HOME, "Home" },
  244. { GF_KEY_INSERT, "Insert" },
  245. { GF_KEY_JAPANESEHIRAGANA, "JapaneseHiragana" },
  246. { GF_KEY_JAPANESEKATAKANA, "JapaneseKatakana" },
  247. { GF_KEY_JAPANESEROMAJI, "JapaneseRomaji" },
  248. { GF_KEY_JUNJAMODE, "JunjaMode" },
  249. { GF_KEY_KANAMODE, "KanaMode" },
  250. { GF_KEY_KANJIMODE, "KanjiMode" },
  251. { GF_KEY_KATAKANA, "Katakana" },
  252. { GF_KEY_LAUNCHAPPLICATION1, "LaunchApplication1" },
  253. { GF_KEY_LAUNCHAPPLICATION2, "LaunchApplication2" },
  254. { GF_KEY_LAUNCHMAIL, "LaunchMail" },
  255. { GF_KEY_LEFT, "Left" },
  256. { GF_KEY_META, "Meta" },
  257. { GF_KEY_MEDIANEXTTRACK, "MediaNextTrack" },
  258. { GF_KEY_MEDIAPLAYPAUSE, "MediaPlayPause" },
  259. { GF_KEY_MEDIAPREVIOUSTRACK, "MediaPreviousTrack" },
  260. { GF_KEY_MEDIASTOP, "MediaStop" },
  261. { GF_KEY_MODECHANGE, "ModeChange" },
  262. { GF_KEY_NONCONVERT, "Nonconvert" },
  263. { GF_KEY_NUMLOCK, "NumLock" },
  264. { GF_KEY_PAGEDOWN, "PageDown" },
  265. { GF_KEY_PAGEUP, "PageUp" },
  266. { GF_KEY_PASTE, "Paste" },
  267. { GF_KEY_PAUSE, "Pause" },
  268. { GF_KEY_PLAY, "Play" },
  269. { GF_KEY_PREVIOUSCANDIDATE, "PreviousCandidate" },
  270. { GF_KEY_PRINTSCREEN, "PrintScreen" },
  271. { GF_KEY_PROCESS, "Process" },
  272. { GF_KEY_PROPS, "Props" },
  273. { GF_KEY_RIGHT, "Right" },
  274. { GF_KEY_ROMANCHARACTERS, "RomanCharacters" },
  275. { GF_KEY_SCROLL, "Scroll" },
  276. { GF_KEY_SELECT, "Select" },
  277. { GF_KEY_SELECTMEDIA, "SelectMedia" },
  278. { GF_KEY_SHIFT, "Shift" },
  279. { GF_KEY_STOP, "Stop" },
  280. { GF_KEY_UP, "Up" },
  281. { GF_KEY_UNDO, "Undo" },
  282. { GF_KEY_VOLUMEDOWN, "VolumeDown" },
  283. { GF_KEY_VOLUMEMUTE, "VolumeMute" },
  284. { GF_KEY_VOLUMEUP, "VolumeUp" },
  285. { GF_KEY_WIN, "Win" },
  286. { GF_KEY_ZOOM, "Zoom" },
  287. { GF_KEY_BACKSPACE, "U+0008" },
  288. { GF_KEY_TAB, "U+0009" },
  289. { GF_KEY_CANCEL, "U+0018" },
  290. { GF_KEY_ESCAPE, "U+001B" },
  291. { GF_KEY_SPACE, "U+0020" },
  292. { GF_KEY_EXCLAMATION, "U+0021" },
  293. { GF_KEY_QUOTATION, "U+0022" },
  294. { GF_KEY_NUMBER, "U+0023" },
  295. { GF_KEY_DOLLAR, "U+0024" },
  296. { GF_KEY_AMPERSAND, "U+0026" },
  297. { GF_KEY_APOSTROPHE, "U+0027" },
  298. { GF_KEY_LEFTPARENTHESIS, "U+0028" },
  299. { GF_KEY_RIGHTPARENTHESIS, "U+0029" },
  300. { GF_KEY_STAR, "U+002A" },
  301. { GF_KEY_PLUS, "U+002B" },
  302. { GF_KEY_COMMA, "U+002C" },
  303. { GF_KEY_HYPHEN, "U+002D" },
  304. { GF_KEY_FULLSTOP, "U+002E" },
  305. { GF_KEY_SLASH, "U+002F" },
  306. { GF_KEY_0, "U+0030" },
  307. { GF_KEY_1, "U+0031" },
  308. { GF_KEY_2, "U+0032" },
  309. { GF_KEY_3, "U+0033" },
  310. { GF_KEY_4, "U+0034" },
  311. { GF_KEY_5, "U+0035" },
  312. { GF_KEY_6, "U+0036" },
  313. { GF_KEY_7, "U+0037" },
  314. { GF_KEY_8, "U+0038" },
  315. { GF_KEY_9, "U+0039" },
  316. { GF_KEY_COLON, "U+003A" },
  317. { GF_KEY_SEMICOLON, "U+003B" },
  318. { GF_KEY_LESSTHAN, "U+003C" },
  319. { GF_KEY_EQUALS, "U+003D" },
  320. { GF_KEY_GREATERTHAN, "U+003E" },
  321. { GF_KEY_QUESTION, "U+003F" },
  322. { GF_KEY_AT, "U+0040" },
  323. { GF_KEY_A, "U+0041" },
  324. { GF_KEY_B, "U+0042" },
  325. { GF_KEY_C, "U+0043" },
  326. { GF_KEY_D, "U+0044" },
  327. { GF_KEY_E, "U+0045" },
  328. { GF_KEY_F, "U+0046" },
  329. { GF_KEY_G, "U+0047" },
  330. { GF_KEY_H, "U+0048" },
  331. { GF_KEY_I, "U+0049" },
  332. { GF_KEY_J, "U+004A" },
  333. { GF_KEY_K, "U+004B" },
  334. { GF_KEY_L, "U+004C" },
  335. { GF_KEY_M, "U+004D" },
  336. { GF_KEY_N, "U+004E" },
  337. { GF_KEY_O, "U+004F" },
  338. { GF_KEY_P, "U+0050" },
  339. { GF_KEY_Q, "U+0051" },
  340. { GF_KEY_R, "U+0052" },
  341. { GF_KEY_S, "U+0053" },
  342. { GF_KEY_T, "U+0054" },
  343. { GF_KEY_U, "U+0055" },
  344. { GF_KEY_V, "U+0056" },
  345. { GF_KEY_W, "U+0057" },
  346. { GF_KEY_X, "U+0058" },
  347. { GF_KEY_Y, "U+0059" },
  348. { GF_KEY_Z, "U+005A" },
  349. { GF_KEY_LEFTSQUAREBRACKET, "U+005B" },
  350. { GF_KEY_BACKSLASH, "U+005C" },
  351. { GF_KEY_RIGHTSQUAREBRACKET, "U+005D" },
  352. { GF_KEY_CIRCUM, "U+005E" },
  353. { GF_KEY_UNDERSCORE, "U+005F" },
  354. { GF_KEY_GRAVEACCENT, "U+0060" },
  355. { GF_KEY_LEFTCURLYBRACKET, "U+007B" },
  356. { GF_KEY_PIPE, "U+007C" },
  357. { GF_KEY_RIGHTCURLYBRACKET, "U+007D" },
  358. { GF_KEY_DEL, "U+007F" },
  359. { GF_KEY_INVERTEXCLAMATION, "U+00A1" },
  360. { GF_KEY_DEADGRAVE, "U+0300" },
  361. { GF_KEY_DEADEACUTE, "U+0301" },
  362. { GF_KEY_DEADCIRCUM, "U+0302" },
  363. { GF_KEY_DEADTILDE, "U+0303" },
  364. { GF_KEY_DEADMACRON, "U+0304" },
  365. { GF_KEY_DEADBREVE, "U+0306" },
  366. { GF_KEY_DEADABOVEDOT, "U+0307" },
  367. { GF_KEY_DEADDIARESIS, "U+0308" },
  368. { GF_KEY_DEADRINGABOVE, "U+030A" },
  369. { GF_KEY_DEADDOUBLEACUTE, "U+030B" },
  370. { GF_KEY_DEADCARON, "U+030C" },
  371. { GF_KEY_DEADCEDILLA, "U+0327" },
  372. { GF_KEY_DEADOGONEK, "U+0328" },
  373. { GF_KEY_DEADIOTA, "U+0345" },
  374. { GF_KEY_EURO, "U+20AC" },
  375. { GF_KEY_DEADVOICESOUND, "U+3099" },
  376. { GF_KEY_DEADSEMIVOICESOUND, "U+309A" },
  377. { GF_KEY_CHANNELUP, "ChannelUp" },
  378. { GF_KEY_CHANNELDOWN, "ChannelDown" },
  379. { GF_KEY_TEXT, "Text" },
  380. { GF_KEY_INFO, "Info" },
  381. { GF_KEY_EPG, "EPG" },
  382. { GF_KEY_RECORD, "Record" },
  383. { GF_KEY_BEGINPAGE, "BeginPage" }
  384. };
  385. GF_EXPORT
  386. const char *gf_dom_get_key_name(u32 key_identifier)
  387. {
  388. u32 count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid);
  389. if (!key_identifier || count<=key_identifier) return "Unknown";
  390. return predefined_key_identifiers[key_identifier-1].name;
  391. }
  392. u32 gf_dom_get_key_type(char *key_name)
  393. {
  394. if (strlen(key_name) == 1) {
  395. char c[2];
  396. c[0] = key_name[0];
  397. c[1] = 0;
  398. strupr(c);
  399. if (c[0] >= 'A' && c[0] <= 'Z')
  400. return (GF_KEY_A + (c[0] - 'A') );
  401. if (c[0] >= '0' && c[0] <= '9')
  402. return ( GF_KEY_0 + (c[0] - '0') );
  403. switch (c[0]) {
  404. case '@': return GF_KEY_AT;
  405. case '*': return GF_KEY_STAR;
  406. case '#': return GF_KEY_NUMBER;
  407. case ' ': return GF_KEY_SPACE;
  408. case '!': return GF_KEY_EXCLAMATION;
  409. case '"': return GF_KEY_QUOTATION;
  410. case '$': return GF_KEY_DOLLAR;
  411. case '&': return GF_KEY_AMPERSAND;
  412. case '\'': return GF_KEY_APOSTROPHE;
  413. case '(': return GF_KEY_LEFTPARENTHESIS;
  414. case ')': return GF_KEY_RIGHTPARENTHESIS;
  415. case '+': return GF_KEY_PLUS;
  416. case ',': return GF_KEY_COMMA;
  417. case '-': return GF_KEY_HYPHEN;
  418. case '.': return GF_KEY_FULLSTOP;
  419. case '/': return GF_KEY_SLASH;
  420. case ':': return GF_KEY_COLON;
  421. case ';': return GF_KEY_SEMICOLON;
  422. case '<': return GF_KEY_LESSTHAN;
  423. case '=': return GF_KEY_EQUALS;
  424. case '>': return GF_KEY_GREATERTHAN;
  425. case '?': return GF_KEY_QUESTION;
  426. case '[': return GF_KEY_LEFTSQUAREBRACKET;
  427. case '\\': return GF_KEY_BACKSLASH;
  428. case ']': return GF_KEY_RIGHTSQUAREBRACKET;
  429. case '^': return GF_KEY_CIRCUM;
  430. case '_': return GF_KEY_UNDERSCORE;
  431. case '`': return GF_KEY_GRAVEACCENT;
  432. case '{': return GF_KEY_LEFTCURLYBRACKET;
  433. case '|': return GF_KEY_PIPE;
  434. case '}': return GF_KEY_RIGHTCURLYBRACKET;
  435. case 'Ą': return GF_KEY_INVERTEXCLAMATION;
  436. default: return GF_KEY_UNIDENTIFIED;
  437. }
  438. } else {
  439. u32 i, count;
  440. count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid);
  441. for (i=0; i<count; i++) {
  442. if (!stricmp(key_name, predefined_key_identifiers[i].name)) {
  443. return predefined_key_identifiers[i].key_code;
  444. }
  445. }
  446. return GF_KEY_UNIDENTIFIED;
  447. }
  448. }
  449. /* Basic SVG datatype parsing functions */
  450. static const struct predef_col { const char *name; u8 r; u8 g; u8 b; } predefined_colors[] =
  451. {
  452. {"aliceblue",240, 248, 255},
  453. {"antiquewhite",250, 235, 215},
  454. {"aqua", 0, 255, 255},
  455. {"aquamarine",127, 255, 212},
  456. {"azure",240, 255, 255},
  457. {"beige",245, 245, 220},
  458. {"bisque",255, 228, 196},
  459. {"black", 0, 0, 0},
  460. {"blanchedalmond",255, 235, 205},
  461. {"blue", 0, 0, 255},
  462. {"blueviolet",138, 43, 226},
  463. {"brown",165, 42, 42},
  464. {"burlywood",222, 184, 135},
  465. {"cadetblue", 95, 158, 160},
  466. {"chartreuse",127, 255, 0},
  467. {"chocolate",210, 105, 30},
  468. {"coral",255, 127, 80},
  469. {"lightpink",255, 182, 193},
  470. {"lightsalmon",255, 160, 122},
  471. {"lightseagreen", 32, 178, 170},
  472. {"lightskyblue",135, 206, 250},
  473. {"lightslategray",119, 136, 153},
  474. {"lightslategrey",119, 136, 153},
  475. {"lightsteelblue",176, 196, 222},
  476. {"lightyellow",255, 255, 224},
  477. {"lime", 0, 255, 0},
  478. {"limegreen", 50, 205, 50},
  479. {"linen",250, 240, 230},
  480. {"magenta",255, 0, 255},
  481. {"maroon",128, 0, 0},
  482. {"mediumaquamarine",102, 205, 170},
  483. {"mediumblue", 0, 0, 205},
  484. {"mediumorchid",186, 85, 211},
  485. {"cornflowerblue",100, 149, 237},
  486. {"cornsilk",255, 248, 220},
  487. {"crimson",220, 20, 60},
  488. {"cyan", 0, 255, 255},
  489. {"darkblue", 0, 0, 139},
  490. {"darkcyan", 0, 139, 139},
  491. {"darkgoldenrod",184, 134, 11},
  492. {"darkgray",169, 169, 169},
  493. {"darkgreen", 0, 100, 0},
  494. {"darkgrey",169, 169, 169},
  495. {"darkkhaki",189, 183, 107},
  496. {"darkmagenta",139, 0, 139},
  497. {"darkolivegreen", 85, 107, 47},
  498. {"darkorange",255, 140, 0},
  499. {"darkorchid",153, 50, 204},
  500. {"darkred",139, 0, 0},
  501. {"darksalmon",233, 150, 122},
  502. {"darkseagreen",143, 188, 143},
  503. {"darkslateblue", 72, 61, 139},
  504. {"darkslategray", 47, 79, 79},
  505. {"darkslategrey", 47, 79, 79},
  506. {"darkturquoise", 0, 206, 209},
  507. {"darkviolet",148, 0, 211},
  508. {"deeppink",255, 20, 147},
  509. {"deepskyblue", 0, 191, 255},
  510. {"dimgray",105, 105, 105},
  511. {"dimgrey",105, 105, 105},
  512. {"dodgerblue", 30, 144, 255},
  513. {"firebrick",178, 34, 34},
  514. {"floralwhite",255, 250, 240},
  515. {"forestgreen", 34, 139, 34},
  516. {"fuchsia",255, 0, 255},
  517. {"gainsboro",220, 220, 220},
  518. {"ghostwhite",248, 248, 255},
  519. {"gold",255, 215, 0},
  520. {"goldenrod",218, 165, 32},
  521. {"gray",128, 128, 128},
  522. {"grey",128, 128, 128},
  523. {"green", 0, 128, 0},
  524. {"greenyellow",173, 255, 47},
  525. {"honeydew",240, 255, 240},
  526. {"hotpink",255, 105, 180},
  527. {"indianred",205, 92, 92},
  528. {"indigo", 75, 0, 130},
  529. {"ivory",255, 255, 240},
  530. {"khaki",240, 230, 140},
  531. {"lavender",230, 230, 25},
  532. {"lavenderblush",255, 240, 245},
  533. {"mediumpurple",147, 112, 219},
  534. {"mediumseagreen", 60, 179, 113},
  535. {"mediumslateblue",123, 104, 238},
  536. {"mediumspringgreen", 0, 250, 154},
  537. {"mediumturquoise", 72, 209, 204},
  538. {"mediumvioletred",199, 21, 133},
  539. {"midnightblue", 25, 25, 112},
  540. {"mintcream",245, 255, 250},
  541. {"mistyrose",255, 228, 225},
  542. {"moccasin",255, 228, 181},
  543. {"navajowhite",255, 222, 173},
  544. {"navy", 0, 0, 128},
  545. {"oldlace",253, 245, 230},
  546. {"olive",128, 128, 0},
  547. {"olivedrab",107, 142, 35},
  548. {"orange",255, 165, 0},
  549. {"orangered",255, 69, 0},
  550. {"orchid",218, 112, 214},
  551. {"palegoldenrod",238, 232, 170},
  552. {"palegreen",152, 251, 152},
  553. {"paleturquoise",175, 238, 238},
  554. {"palevioletred",219, 112, 147},
  555. {"papayawhip",255, 239, 213},
  556. {"peachpuff",255, 218, 185},
  557. {"peru",205, 133, 63},
  558. {"pink",255, 192, 203},
  559. {"plum",221, 160, 221},
  560. {"powderblue",176, 224, 230},
  561. {"purple",128, 0, 128},
  562. {"red",255, 0, 0},
  563. {"rosybrown",188, 143, 143},
  564. {"royalblue", 65, 105, 225},
  565. {"saddlebrown",139, 69, 19},
  566. {"salmon",250, 128, 114},
  567. {"sandybrown",244, 164, 96},
  568. {"seagreen", 46, 139, 87},
  569. {"seashell",255, 245, 238},
  570. {"sienna",160, 82, 45},
  571. {"silver",192, 192, 192},
  572. {"skyblue",135, 206, 235},
  573. {"slateblue",106, 90, 205},
  574. {"slategray",112, 128, 144},
  575. {"slategrey",112, 128, 144},
  576. {"snow",255, 250, 250},
  577. {"springgreen", 0, 255, 127},
  578. {"steelblue", 70, 130, 180},
  579. {"tan",210, 180, 140},
  580. {"teal", 0, 128, 128},
  581. {"lawngreen",124, 252, 0},
  582. {"lemonchiffon",255, 250, 205},
  583. {"lightblue",173, 216, 230},
  584. {"lightcoral",240, 128, 128},
  585. {"lightcyan",224, 255, 255},
  586. {"lightgoldenrodyellow",250, 250, 210},
  587. {"lightgray",211, 211, 211},
  588. {"lightgreen",144, 238, 144},
  589. {"lightgrey",211, 211, 211},
  590. {"thistle",216, 191, 216},
  591. {"tomato",255, 99, 71},
  592. {"turquoise", 64, 224, 208},
  593. {"violet",238, 130, 238},
  594. {"wheat",245, 222, 179},
  595. {"white",255, 255, 255},
  596. {"whitesmoke",245, 245, 245},
  597. {"yellow",255, 255, 0},
  598. {"yellowgreen",154, 205, 50}
  599. };
  600. /* Basic SVG datatype parsing functions */
  601. static const struct sys_col { const char *name; u8 type; } system_colors[] =
  602. {
  603. {"ActiveBorder", SVG_COLOR_ACTIVE_BORDER},
  604. {"ActiveCaption", SVG_COLOR_ACTIVE_CAPTION},
  605. {"AppWorkspace", SVG_COLOR_APP_WORKSPACE},
  606. {"Background", SVG_COLOR_BACKGROUND},
  607. {"ButtonFace", SVG_COLOR_BUTTON_FACE},
  608. {"ButtonHighlight", SVG_COLOR_BUTTON_HIGHLIGHT},
  609. {"ButtonShadow", SVG_COLOR_BUTTON_SHADOW},
  610. {"ButtonText", SVG_COLOR_BUTTON_TEXT},
  611. {"CaptionText", SVG_COLOR_CAPTION_TEXT},
  612. {"GrayText", SVG_COLOR_GRAY_TEXT},
  613. {"Highlight", SVG_COLOR_HIGHLIGHT},
  614. {"HighlightText", SVG_COLOR_HIGHLIGHT_TEXT},
  615. {"InactiveBorder", SVG_COLOR_INACTIVE_BORDER},
  616. {"InactiveCaption", SVG_COLOR_INACTIVE_CAPTION},
  617. {"InactiveCaptionText", SVG_COLOR_INACTIVE_CAPTION_TEXT},
  618. {"InfoBackground", SVG_COLOR_INFO_BACKGROUND},
  619. {"InfoText", SVG_COLOR_INFO_TEXT},
  620. {"Menu", SVG_COLOR_MENU},
  621. {"MenuText", SVG_COLOR_MENU_TEXT},
  622. {"Scrollbar", SVG_COLOR_SCROLLBAR},
  623. {"ThreeDDarkShadow", SVG_COLOR_3D_DARK_SHADOW},
  624. {"ThreeDFace", SVG_COLOR_3D_FACE},
  625. {"ThreeDHighlight", SVG_COLOR_3D_HIGHLIGHT},
  626. {"ThreeDLightShadow", SVG_COLOR_3D_LIGHT_SHADOW},
  627. {"ThreeDShadow", SVG_COLOR_3D_SHADOW},
  628. {"Window", SVG_COLOR_WINDOW},
  629. {"WindowFrame", SVG_COLOR_WINDOW_FRAME},
  630. {"WindowText", SVG_COLOR_WINDOW_TEXT},
  631. };
  632. /* parses an color from a named color HTML or CSS 2 */
  633. static void svg_parse_named_color(SVG_Color *col, char *attribute_content)
  634. {
  635. u32 i, count;
  636. count = sizeof(predefined_colors) / sizeof(struct predef_col);
  637. for (i=0; i<count; i++) {
  638. if (!strcmp(attribute_content, predefined_colors[i].name)) {
  639. col->red = INT2FIX(predefined_colors[i].r) / 255;
  640. col->green = INT2FIX(predefined_colors[i].g) / 255;
  641. col->blue = INT2FIX(predefined_colors[i].b) / 255;
  642. col->type = SVG_COLOR_RGBCOLOR;
  643. return;
  644. }
  645. }
  646. count = sizeof(system_colors) / sizeof(struct sys_col);
  647. for (i=0; i<count; i++) {
  648. if (!strcmp(attribute_content, system_colors[i].name)) {
  649. col->type = system_colors[i].type;
  650. return;
  651. }
  652. }
  653. }
  654. const char *gf_svg_get_system_paint_server_name(u32 paint_type)
  655. {
  656. u32 i, count;
  657. count = sizeof(system_colors) / sizeof(struct sys_col);
  658. for (i=0; i<count; i++) {
  659. if (paint_type == system_colors[i].type) return system_colors[i].name;
  660. }
  661. return "undefined";
  662. }
  663. u32 gf_svg_get_system_paint_server_type(const char *name)
  664. {
  665. u32 i, count;
  666. count = sizeof(system_colors) / sizeof(struct sys_col);
  667. for (i=0; i<count; i++) {
  668. if (!strcmp(name, system_colors[i].name)) return system_colors[i].type;
  669. }
  670. return 0;
  671. }
  672. /* Reads an SVG Color
  673. either #RRGGBB, #RGB, rgb(r,g,b) in [0,255] , colorname, or 'r g b' in [0,1]
  674. ignores any space, comma, semi-column before and any space after
  675. TODO:
  676. transform the char into char and duplicate the input, instead of modifying it
  677. be more robust to errors in color description ex rgb(0 0 0)
  678. */
  679. static void svg_parse_color(SVG_Color *col, char *attribute_content)
  680. {
  681. char *str = attribute_content;
  682. while (str[strlen(attribute_content)-1] == ' ') str[strlen(attribute_content)-1] = 0;
  683. while (*str != 0 && (*str == ' ' || *str == ',' || *str == ';')) str++;
  684. if (!strcmp(str, "currentColor")) {
  685. col->type = SVG_COLOR_CURRENTCOLOR;
  686. return;
  687. } else if (!strcmp(str, "inherit")) {
  688. col->type = SVG_COLOR_INHERIT;
  689. return;
  690. } else if (str[0]=='#') {
  691. u32 val;
  692. sscanf(str+1, "%x", &val);
  693. if (strlen(str) == 7) {
  694. col->red = INT2FIX((val>>16) & 0xFF) / 255;
  695. col->green = INT2FIX((val>>8) & 0xFF) / 255;
  696. col->blue = INT2FIX(val & 0xFF) / 255;
  697. } else {
  698. col->red = INT2FIX((val>>8) & 0xF) / 15;
  699. col->green = INT2FIX((val>>4) & 0xF) / 15;
  700. col->blue = INT2FIX(val & 0xF) / 15;
  701. }
  702. col->type = SVG_COLOR_RGBCOLOR;
  703. } else if (strstr(str, "rgb(") || strstr(str, "RGB(")) {
  704. Float _val;
  705. u8 is_percentage= 0;
  706. if (strstr(str, "%")) is_percentage = 1;
  707. str = strstr(str, "(");
  708. str++;
  709. sscanf(str, "%f", &_val); col->red = FLT2FIX(_val);
  710. str = strstr(str, ",");
  711. if (!str) {
  712. /* space separated colors/percentages are not allowed neither in SVG 1.1 nor in SVG T1.2 */
  713. col->red = col->green = col->blue = 0;
  714. return;
  715. }
  716. str++;
  717. sscanf(str, "%f", &_val); col->green = FLT2FIX(_val);
  718. str = strstr(str, ",");
  719. if (!str) {
  720. /* space separated colors/percentages are not allowed neither in SVG 1.1 nor in SVG T1.2 */
  721. col->red = col->green = col->blue = 0;
  722. return;
  723. }
  724. str++;
  725. sscanf(str, "%f", &_val); col->blue = FLT2FIX(_val);
  726. if (is_percentage) {
  727. col->red /= 100;
  728. col->green /= 100;
  729. col->blue /= 100;
  730. } else {
  731. col->red /= 255;
  732. col->green /= 255;
  733. col->blue /= 255;
  734. }
  735. col->type = SVG_COLOR_RGBCOLOR;
  736. } else if ((str[0] >= 'a' && str[0] <= 'z')
  737. || (str[0] >= 'A' && str[0] <= 'Z')) {
  738. svg_parse_named_color(col, str);
  739. } else {
  740. Float _r, _g, _b;
  741. sscanf(str, "%f %f %f", &_r, &_g, &_b);
  742. col->red = FLT2FIX(_r);
  743. col->green = FLT2FIX(_g);
  744. col->blue = FLT2FIX(_b);
  745. col->type = SVG_COLOR_RGBCOLOR;
  746. }
  747. }
  748. /*
  749. Reads a number (i.e. without unit) according to the CSS syntax (same as SVG paths and transforms)
  750. trims any space, comma, semi-column before or after (TODO: fix this)
  751. reads an optional + or -
  752. then reads a digit between 0 and 9
  753. optionally followed by an '.' and digits between 0 and 9
  754. optionally followed by e or E and digits between 0 and 9
  755. Returns the number of chars read in d
  756. */
  757. static u32 svg_parse_number(char *d, Fixed *f, Bool is_angle)
  758. {
  759. u32 nb_digit_before = 0;
  760. u32 nb_digit_after = 0;
  761. Bool has_fractional = 0;
  762. Bool is_negative = 0;
  763. Float _val = 0;
  764. u32 i = 0;
  765. /* warning the comma and semicolumn should not be there when parsing a number in a path */
  766. while ((d[i] != 0) && strchr(" ,;\r\n\t", d[i])) i++;
  767. if (!d[i]) {
  768. GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Parsing] Parsing number with empty string or only spaces: %s\n", d));
  769. return 0;
  770. }
  771. if (d[i] == '+') {
  772. i++;
  773. } else if (d[i] == '-') {
  774. is_negative = 1;
  775. i++;
  776. }
  777. /* Warning: this is not normal, should be detected somehow by checking the BNF */
  778. /* if ((d[i]=='N') && (d[i+1]=='a') && (d[i+2]=='N')) {
  779. i+= 3;
  780. _val = 0;
  781. goto end;
  782. }*/
  783. /* read the digit-sequence token of the BNF */
  784. while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) {
  785. _val = _val*10 + (d[i]-'0');
  786. nb_digit_before++;
  787. i++;
  788. }
  789. if (d[i] == '.') {
  790. has_fractional = 1;
  791. i++;
  792. while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) {
  793. _val = _val*10 + (d[i]-'0');
  794. nb_digit_after++;
  795. i++;
  796. }
  797. if (nb_digit_after) {
  798. _val /= (Float)pow(10,nb_digit_after);
  799. } else if (nb_digit_before == 0) {
  800. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error in parsing number (expecting digits before or after a '.': %s\n", d));
  801. return 0;
  802. } else {
  803. /* dangling '.' without digits after. This is allowed by the BNF */
  804. }
  805. }
  806. if ((nb_digit_before == 0) && (has_fractional == 0)) {
  807. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error in parsing number (expecting digits):%s\n", d));
  808. return 0;
  809. }
  810. /* reading the exponent */
  811. if (d[i] == 'e' || d[i] == 'E') {
  812. Bool neg_exp = 0;
  813. u32 nb_exp_digits = 0;
  814. s32 exp = 0;
  815. i++;
  816. if (d[i] == '+') i++;
  817. else if (d[i] == '-') {
  818. i++;
  819. neg_exp=1;
  820. }
  821. while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) {
  822. exp = exp*10 + (d[i]-'0');
  823. nb_exp_digits++;
  824. i++;
  825. }
  826. if (nb_exp_digits) {
  827. _val *= (Float)pow(10, neg_exp ? -exp : exp);
  828. } else {
  829. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error in parsing exponent, 'e' or 'E' should be followed by digits: %s\n", d));
  830. return 0;
  831. }
  832. }
  833. /* We can now produce the final number */
  834. if (is_negative) _val *= -1;
  835. if (is_angle) {
  836. _val/=180;
  837. (*f) = gf_mulfix(FLT2FIX(_val), GF_PI);
  838. } else {
  839. (*f) = FLT2FIX(_val);
  840. }
  841. /* warning the comma and semicolumn should not be there when parsing a path number */
  842. while (d[i] != 0 && (d[i] == ' ' || d[i] == ',' || d[i] == ';')) i++;
  843. return i;
  844. }
  845. /*
  846. Parse an Offset Value, i.e +/- Clock Value
  847. */
  848. static GF_Err svg_parse_clock_value(char *d, Double *clock_value)
  849. {
  850. char *tmp;
  851. s32 sign = 1;
  852. if (!d) return GF_BAD_PARAM;
  853. if (!d[0]) return GF_BAD_PARAM;
  854. if (d[0] == '+') d++;
  855. else if (d[0] == '-') { sign = -1; d++; }
  856. if (!d[0]) return GF_BAD_PARAM;
  857. /* According to SVG, the following are invalid syntaxes (see animate-elem-225-t.svg)
  858. '+-2s'
  859. '1++s' even though sscanf returns the right values
  860. */
  861. if (strchr(d, '+') || strchr(d, '-')) return GF_BAD_PARAM;
  862. /* No embedded white space is allowed in clock values,
  863. although leading and trailing white space characters will be ignored.*/
  864. while (*d == ' ') d++;
  865. if ((tmp = strchr(d, ':'))) {
  866. /* Full or Partial Clock value */
  867. tmp++;
  868. if ((tmp = strchr(tmp, ':'))) {
  869. /* Full Clock value : hh:mm:ss(.frac) */
  870. u32 hours;
  871. u32 minutes;
  872. Float seconds;
  873. if (sscanf(d, "%u:%u:%f", &hours, &minutes, &seconds) < 3) return GF_BAD_PARAM;
  874. *clock_value = hours*3600 + minutes*60 + seconds;
  875. } else {
  876. /* Partial Clock value : mm:ss(.frac) */
  877. s32 minutes;
  878. Float seconds;
  879. if (sscanf(d, "%d:%f", &minutes, &seconds) < 2) return GF_BAD_PARAM;
  880. *clock_value = minutes*60 + seconds;
  881. }
  882. } else if ((tmp = strstr(d, "h"))) {
  883. Float f;
  884. if (sscanf(d, "%fh", &f) == 0) return GF_BAD_PARAM;
  885. *clock_value = 3600*f;
  886. } else if (strstr(d, "min")) {
  887. Float f;
  888. if (sscanf(d, "%fmin", &f) == 0) return GF_BAD_PARAM;
  889. *clock_value = 60*f;
  890. } else if ((tmp = strstr(d, "ms"))) {
  891. Float f;
  892. if (sscanf(d, "%fms", &f) == 0) return GF_BAD_PARAM;
  893. *clock_value = f/1000;
  894. } else if (strchr(d, 's')) {
  895. Float f;
  896. if (sscanf(d, "%fs", &f) == 0) return GF_BAD_PARAM;
  897. *clock_value = f;
  898. } else {
  899. Float f;
  900. if (sscanf(d, "%f", &f) == 0) return GF_BAD_PARAM;
  901. *clock_value = f;
  902. }
  903. *clock_value *= sign;
  904. return GF_OK;
  905. }
  906. /* Parses one SVG time value:
  907. indefinite,
  908. element_id.event_name
  909. wallclock,
  910. accessKey,
  911. events,
  912. clock value.
  913. */
  914. static GF_Err smil_parse_time(GF_Node *elt, SMIL_Time *v, char *d)
  915. {
  916. GF_Err e = GF_OK;
  917. char *tmp;
  918. /* Offset Values */
  919. if ((d[0] >= '0' && d[0] <= '9') || d[0] == '+' || d[0] == '-'){
  920. v->type = GF_SMIL_TIME_CLOCK;
  921. return svg_parse_clock_value(d, &(v->clock));
  922. }
  923. /* Indefinite Values */
  924. else if (!strcmp(d, "indefinite")) {
  925. v->type = GF_SMIL_TIME_INDEFINITE;
  926. return GF_OK;
  927. }
  928. /* Wallclock Values */
  929. else if ((tmp = strstr(d, "wallclock("))) {
  930. u32 year, month, day;
  931. u32 hours, minutes;
  932. u32 nhours, nminutes;
  933. Float seconds;
  934. char *tmp1, *tmp2;
  935. v->type = GF_SMIL_TIME_WALLCLOCK;
  936. tmp += 10;
  937. if ((tmp1 = strchr(tmp, 'T')) ) {
  938. /* From tmp to wallStartTime, we parse a date */
  939. sscanf(tmp, "%u-%u-%dT", &year, &month, &day);
  940. tmp1++;
  941. tmp = tmp1;
  942. }
  943. if ((tmp1 = strchr(tmp, ':')) ) {
  944. if ((tmp2 = strchr(tmp1, ':')) ) {
  945. /* HHMMSS */
  946. sscanf(tmp, "%u:%u:%f", &hours, &minutes, &seconds);
  947. } else {
  948. /* HHMM */
  949. sscanf(tmp, "%u:%u", &hours, &minutes);
  950. }
  951. }
  952. if (strchr(tmp, 'Z')) {
  953. return GF_OK;
  954. } else {
  955. if ( (tmp1 = strchr(tmp, '+')) ) {
  956. sscanf(tmp1, "%u:%u", &nhours, &nminutes);
  957. } else if ( (tmp1 = strchr(tmp, '-')) ) {
  958. sscanf(tmp1, "%u:%u", &nhours, &nminutes);
  959. }
  960. }
  961. return GF_OK;
  962. }
  963. /* AccessKey Values */
  964. else if ((tmp = strstr(d, "accessKey("))) {
  965. char *sep;
  966. v->type = GF_SMIL_TIME_EVENT;
  967. v->event.type = GF_EVENT_KEYDOWN;
  968. v->element = elt->sgprivate->scenegraph->RootNode;
  969. tmp+=10;
  970. sep = strchr(d, ')');
  971. sep[0] = 0;
  972. v->event.parameter = gf_dom_get_key_type(tmp);
  973. sep++;
  974. if ((tmp = strchr(sep, '+')) || (tmp = strchr(sep, '-'))) {
  975. char c = *tmp;
  976. tmp++;
  977. e = svg_parse_clock_value(tmp, &(v->clock));
  978. if (c == '-') v->clock *= -1;
  979. }
  980. return e;
  981. }
  982. else {
  983. Bool had_param = 0;
  984. char *tmp2;
  985. v->type = GF_SMIL_TIME_EVENT;
  986. if ((tmp = strchr(d, '.'))) {
  987. tmp[0] = 0;
  988. if (strlen(d) == 0) {
  989. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] expecting an id before '.' in SMIL Time .%s\n", tmp+1));
  990. return GF_BAD_PARAM;
  991. }
  992. v->element_id = gf_strdup(d);
  993. tmp[0] = '.';
  994. tmp++;
  995. } else {
  996. tmp = d;
  997. }
  998. if ((tmp2 = strchr(tmp, '('))) {
  999. tmp2[0] = 0;
  1000. v->event.type = gf_dom_event_type_by_name(tmp);
  1001. tmp2[0] = '(';
  1002. tmp2++;
  1003. had_param = 1;
  1004. v->event.parameter = atoi(tmp2);
  1005. tmp = strchr(tmp2, ')');
  1006. if (!tmp) {
  1007. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] expecting ')' in SMIL Time %s\n", d));
  1008. return GF_BAD_PARAM;
  1009. }
  1010. tmp++;
  1011. }
  1012. if ((tmp2 = strchr(tmp, '+')) || (tmp2 = strchr(tmp, '-'))) {
  1013. char c = *tmp2;
  1014. char *tmp3 = tmp2;
  1015. tmp2[0] = 0;
  1016. tmp3--;
  1017. while (*tmp3==' ') { *tmp3=0; tmp3--; }
  1018. if (v->event.type == 0) v->event.type = gf_dom_event_type_by_name(tmp);
  1019. if (!had_param && (v->event.type == GF_EVENT_REPEAT || v->event.type == GF_EVENT_REPEAT_EVENT))
  1020. v->event.parameter = 1;
  1021. tmp2[0] = c;
  1022. tmp2++;
  1023. e = svg_parse_clock_value(tmp2, &(v->clock));
  1024. if (c == '-') v->clock *= -1;
  1025. return e;
  1026. } else {
  1027. if (v->event.type == 0) v->event.type = gf_dom_event_type_by_name(tmp);
  1028. if (!had_param && (v->event.type == GF_EVENT_REPEAT || v->event.type == GF_EVENT_REPEAT_EVENT))
  1029. v->event.parameter = 1;
  1030. }
  1031. }
  1032. return GF_OK;
  1033. }
  1034. /* Parses a list of SVG transformations and collapses them in the given matrix */
  1035. Bool gf_svg_parse_transformlist(GF_Matrix2D *mat, char *attribute_content)
  1036. {
  1037. GF_Matrix2D tmp;
  1038. char *str;
  1039. u32 read_chars;
  1040. u32 i;
  1041. gf_mx2d_init(*mat);
  1042. str = attribute_content;
  1043. i = 0;
  1044. while (str[i] != 0) {
  1045. while (str[i] == ' ') i++;
  1046. if (str[i] == ',') i++;
  1047. while (str[i] == ' ') i++;
  1048. if (strstr(str+i, "scale")==str+i) {
  1049. i += 5;
  1050. while(str[i] == ' ') i++;
  1051. if (str[i] == '(') {
  1052. Fixed sx, sy;
  1053. i++;
  1054. read_chars = svg_parse_number(&(str[i]), &sx, 0);
  1055. if (!read_chars) {
  1056. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading sx component in scale: %s\n", attribute_content));
  1057. return 0;
  1058. }
  1059. i += read_chars;
  1060. if (str[i] == ')') {
  1061. sy = sx;
  1062. } else {
  1063. read_chars = svg_parse_number(&(str[i]), &sy, 0);
  1064. if (!read_chars) {
  1065. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading sy component in scale: %s\n", attribute_content));
  1066. return 0;
  1067. }
  1068. i += read_chars;
  1069. }
  1070. gf_mx2d_init(tmp);
  1071. gf_mx2d_add_scale(&tmp, sx, sy);
  1072. gf_mx2d_add_matrix(&tmp, mat);
  1073. gf_mx2d_copy(*mat, tmp);
  1074. while(str[i] == ' ') i++;
  1075. if (str[i] == ')') i++;
  1076. else {
  1077. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1078. return 0;
  1079. }
  1080. } else {
  1081. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
  1082. return 0;
  1083. }
  1084. } else if (strstr(str+i, "translate")==str+i) {
  1085. i += 9;
  1086. while(str[i] == ' ') i++;
  1087. if (str[i] == '(') {
  1088. Fixed tx, ty;
  1089. i++;
  1090. read_chars = svg_parse_number(&(str[i]), &tx, 0);
  1091. if (!read_chars) {
  1092. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading tx component in translate: %s\n", attribute_content));
  1093. return 0;
  1094. }
  1095. i += read_chars;
  1096. if (str[i] == ')') {
  1097. ty = 0;
  1098. } else {
  1099. read_chars = svg_parse_number(&(str[i]), &ty, 0);
  1100. if (!read_chars) {
  1101. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading ty component in translate: %s\n", attribute_content));
  1102. return 0;
  1103. }
  1104. i += read_chars;
  1105. }
  1106. gf_mx2d_init(tmp);
  1107. gf_mx2d_add_translation(&tmp, tx, ty);
  1108. gf_mx2d_add_matrix(&tmp, mat);
  1109. gf_mx2d_copy(*mat, tmp);
  1110. while(str[i] == ' ') i++;
  1111. if (str[i] == ')') i++;
  1112. else {
  1113. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1114. return 0;
  1115. }
  1116. } else {
  1117. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
  1118. return 0;
  1119. }
  1120. } else if (strstr(str+i, "rotate")==str+i) {
  1121. i += 6;
  1122. while(str[i] == ' ') i++;
  1123. if (str[i] == '(') {
  1124. Fixed angle, cx, cy;
  1125. i++;
  1126. read_chars = svg_parse_number(&(str[i]), &angle, 1);
  1127. if (!read_chars) {
  1128. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading angle component in rotate: %s\n", attribute_content));
  1129. return 0;
  1130. }
  1131. i += read_chars;
  1132. if (str[i] == ')') {
  1133. cx = cy = 0;
  1134. } else {
  1135. read_chars = svg_parse_number(&(str[i]), &cx, 0);
  1136. if (!read_chars) {
  1137. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading cx component in rotate: %s\n", attribute_content));
  1138. return 0;
  1139. }
  1140. i += read_chars;
  1141. read_chars = svg_parse_number(&(str[i]), &cy, 0);
  1142. if (!read_chars) {
  1143. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading cy component in rotate: %s\n", attribute_content));
  1144. return 0;
  1145. }
  1146. i += read_chars;
  1147. }
  1148. gf_mx2d_init(tmp);
  1149. gf_mx2d_add_rotation(&tmp, cx, cy, angle);
  1150. gf_mx2d_add_matrix(&tmp, mat);
  1151. gf_mx2d_copy(*mat, tmp);
  1152. while(str[i] == ' ') i++;
  1153. if (str[i] == ')') i++;
  1154. else {
  1155. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1156. return 0;
  1157. }
  1158. } else {
  1159. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
  1160. return 0;
  1161. }
  1162. } else if (strstr(str+i, "skewX")==str+i) {
  1163. i += 5;
  1164. while(str[i] == ' ') i++;
  1165. if (str[i] == '(') {
  1166. Fixed angle;
  1167. i++;
  1168. read_chars = svg_parse_number(&(str[i]), &angle, 1);
  1169. if (!read_chars) {
  1170. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading angle in skewX: %s\n", attribute_content));
  1171. return 0;
  1172. }
  1173. i += read_chars;
  1174. gf_mx2d_init(tmp);
  1175. gf_mx2d_add_skew_x(&tmp, angle);
  1176. gf_mx2d_add_matrix(&tmp, mat);
  1177. gf_mx2d_copy(*mat, tmp);
  1178. while(str[i] == ' ') i++;
  1179. if (str[i] == ')') i++;
  1180. else {
  1181. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1182. return 0;
  1183. }
  1184. } else {
  1185. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
  1186. return 0;
  1187. }
  1188. } else if (strstr(str+i, "skewY")==str+i) {
  1189. i += 5;
  1190. while(str[i] == ' ') i++;
  1191. if (str[i] == '(') {
  1192. Fixed angle;
  1193. i++;
  1194. read_chars = svg_parse_number(&(str[i]), &angle, 1);
  1195. if (!read_chars) {
  1196. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading angle component in skewY: %s\n", attribute_content));
  1197. return 0;
  1198. }
  1199. i += read_chars;
  1200. gf_mx2d_init(tmp);
  1201. gf_mx2d_add_skew_y(&tmp, angle);
  1202. gf_mx2d_add_matrix(&tmp, mat);
  1203. gf_mx2d_copy(*mat, tmp);
  1204. while(str[i] == ' ') i++;
  1205. if (str[i] == ')') i++;
  1206. else {
  1207. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1208. return 0;
  1209. }
  1210. } else {
  1211. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
  1212. return 0;
  1213. }
  1214. } else if (strstr(str+i, "matrix")==str+i) {
  1215. i+=6;
  1216. while(str[i] == ' ') i++;
  1217. if (str[i] == '(') {
  1218. i++;
  1219. read_chars = svg_parse_number(&(str[i]), &(tmp.m[0]), 0);
  1220. if (!read_chars) {
  1221. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient a in matrix: %s\n", attribute_content));
  1222. return 0;
  1223. }
  1224. i += read_chars;
  1225. read_chars = svg_parse_number(&(str[i]), &(tmp.m[3]), 0);
  1226. if (!read_chars) {
  1227. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient b in matrix: %s\n", attribute_content));
  1228. return 0;
  1229. }
  1230. i += read_chars;
  1231. read_chars = svg_parse_number(&(str[i]), &(tmp.m[1]), 0);
  1232. if (!read_chars) {
  1233. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient c in matrix: %s\n", attribute_content));
  1234. return 0;
  1235. }
  1236. i += read_chars;
  1237. read_chars = svg_parse_number(&(str[i]), &(tmp.m[4]), 0);
  1238. if (!read_chars) {
  1239. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient d in matrix: %s\n", attribute_content));
  1240. return 0;
  1241. }
  1242. i += read_chars;
  1243. read_chars = svg_parse_number(&(str[i]), &(tmp.m[2]), 0);
  1244. if (!read_chars) {
  1245. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient e in matrix: %s\n", attribute_content));
  1246. return 0;
  1247. }
  1248. i += read_chars;
  1249. read_chars = svg_parse_number(&(str[i]), &(tmp.m[5]), 0);
  1250. if (!read_chars) {
  1251. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient f in matrix: %s\n", attribute_content));
  1252. return 0;
  1253. }
  1254. i += read_chars;
  1255. gf_mx2d_add_matrix(&tmp, mat);
  1256. gf_mx2d_copy(*mat, tmp);
  1257. while(str[i] == ' ') i++;
  1258. if (str[i] == ')') i++;
  1259. else {
  1260. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1261. return 0;
  1262. }
  1263. } else {
  1264. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
  1265. return 0;
  1266. }
  1267. } else {
  1268. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Unrecognized transofrm type in attribute %s\n", attribute_content));
  1269. return 0;
  1270. }
  1271. /*for svgView parsing*/
  1272. if (str[i] == ')') i++;
  1273. }
  1274. return 1;
  1275. }
  1276. /* Parses an SVG transform attribute and collapses all in the given matrix */
  1277. static Bool svg_parse_transform(SVG_Transform *t, char *attribute_content)
  1278. {
  1279. char *str;
  1280. u32 i;
  1281. u32 read_chars;
  1282. str = attribute_content;
  1283. i = 0;
  1284. if ((str = strstr(attribute_content, "ref"))) {
  1285. t->is_ref = 1;
  1286. gf_mx2d_init(t->mat);
  1287. str+=2;
  1288. while (str[i] == ' ') i++;
  1289. if (str[i] == '(') {
  1290. i++;
  1291. while (str[i] == ' ') i++;
  1292. if (str[i] == 's' && str[i+1] == 'v' && str[i+2] == 'g') {
  1293. i+=3;
  1294. while (str[i] == ' ') i++;
  1295. if (str[i] == ',') {
  1296. i++;
  1297. } else if (str[i] == ')') {
  1298. i++;
  1299. return GF_OK;
  1300. }
  1301. read_chars = svg_parse_number(&(str[i]), &(t->mat.m[2]), 0);
  1302. if (!read_chars) {
  1303. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient tx in ref transform: %s\n", attribute_content));
  1304. return GF_BAD_PARAM;
  1305. }
  1306. i += read_chars;
  1307. read_chars = svg_parse_number(&(str[i]), &(t->mat.m[5]), 0);
  1308. if (!read_chars) {
  1309. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient ty in ref transform: %s\n", attribute_content));
  1310. return GF_BAD_PARAM;
  1311. }
  1312. i += read_chars;
  1313. } else {
  1314. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Unsupported syntax for ref transform attribute"));
  1315. }
  1316. while (str[i] == ' ') i++;
  1317. if (str[i] == ')') i++;
  1318. else {
  1319. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
  1320. }
  1321. return GF_OK;
  1322. } else {
  1323. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in ref transform attribute: %s\n", attribute_content));
  1324. return GF_BAD_PARAM;
  1325. }
  1326. } else {
  1327. read_chars = gf_svg_parse_transformlist(&t->mat, attribute_content);
  1328. if (!read_chars) {
  1329. GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error parsing transform list: %s\n", attribute_content));
  1330. return GF_BAD_PARAM;
  1331. }
  1332. }
  1333. return GF_OK;
  1334. }
  1335. #undef REMOVE_ALLOC
  1336. #if USE_GF_PATH
  1337. //#define PARSE_PATH_ONLY
  1338. static void svg_parse_path(SVG_PathData *path, char *attribute_content)
  1339. {
  1340. char *d = attribute_content;
  1341. /* used to detect end of BNF production:
  1342. "The processing of the BNF must consume as much of a given BNF production as possible,
  1343. stopping at the point when a character is encountered which no longer satisfies the production." */
  1344. u32 read_chars = 0;
  1345. /* Point used to start a new subpath when the previous subpath is closed */
  1346. SVG_Point prev_m_pt;
  1347. /* Point used to convert relative 'lower-case commands' into absolute */
  1348. SVG_Point rel_ref_pt;
  1349. /* Points used to convert S, T commands into C, Q */
  1350. SVG_Point orig, ct_orig, ct_end, end;
  1351. /* Used by elliptical arcs */
  1352. Fixed x_axis_rotation, large_arc_flag, sweep_flag;
  1353. char c, prev_c;
  1354. u32 i;
  1355. if (*d == 0) return;
  1356. i = 0;
  1357. prev_c = 'M';
  1358. orig.x = orig.y = ct_orig.x = ct_orig.y = prev_m_pt.x = prev_m_pt.y = rel_ref_pt.x = rel_ref_pt.y = end.x = end.y = 0;
  1359. while(1) {
  1360. while ( (d[i]==' ') || (d[i] =='\t') || (d[i] =='\r') || (d[i] =='\n') ) i++;
  1361. c = d[i];
  1362. if (! c) break;
  1363. next_command:
  1364. switch (c) {
  1365. case 'm':
  1366. case 'M':
  1367. i++;
  1368. read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
  1369. if (!read_chars) return;
  1370. i += read_chars;
  1371. read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
  1372. if (!read_chars) return;
  1373. i += read_chars;
  1374. if (c == 'm') {
  1375. orig.x += rel_ref_pt.x;
  1376. orig.y += rel_ref_pt.y;
  1377. }
  1378. #ifndef PARSE_PATH_ONLY
  1379. gf_path_add_move_to(path, orig.x, orig.y);
  1380. #endif
  1381. rel_ref_pt = orig;
  1382. prev_m_pt = orig;
  1383. /*provision for nextCurveTo when no curve is specified:
  1384. "If there is no previous command or if the previous command was not an C, c, S or s,
  1385. assume the first control point is coincident with the current point.
  1386. */
  1387. ct_orig = orig;
  1388. prev_c = c;
  1389. break;
  1390. case 'L':
  1391. case 'l':
  1392. i++;
  1393. read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
  1394. if (!read_chars) return;
  1395. i += read_chars;
  1396. read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
  1397. if (!read_chars) return;
  1398. i += read_chars;
  1399. if (c == 'l') {
  1400. orig.x += rel_ref_pt.x;
  1401. orig.y += rel_ref_pt.y;
  1402. }
  1403. #ifndef PARSE_PATH_ONLY
  1404. gf_path_add_line_to(path, orig.x, orig.y);
  1405. #endif
  1406. rel_ref_pt = orig;
  1407. orig = end;
  1408. /*cf above*/
  1409. ct_orig = orig;
  1410. prev_c = c;
  1411. break;
  1412. case 'H':
  1413. case 'h':
  1414. i++;
  1415. read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
  1416. if (!read_chars) return;
  1417. i += read_chars;
  1418. if (c == 'h') {
  1419. orig.x += rel_ref_pt.x;
  1420. }
  1421. orig.y = rel_ref_pt.y;
  1422. #ifndef PARSE_PATH_ONLY
  1423. gf_path_add_line_to(path, orig.x, orig.y);
  1424. #endif
  1425. rel_ref_pt.x = orig.x;
  1426. orig = end;
  1427. /*cf above*/
  1428. ct_orig = orig;
  1429. prev_c = c;
  1430. break;
  1431. case 'V':
  1432. case 'v':
  1433. i++;
  1434. read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
  1435. if (!read_chars) return;
  1436. i += read_chars;
  1437. if (c == 'v') {
  1438. orig.y += rel_ref_pt.y;
  1439. }
  1440. orig.x = rel_ref_pt.x;
  1441. #ifndef PARSE_PATH_ONLY
  1442. gf_path_add_line_to(path, orig.x, orig.y);
  1443. #endif
  1444. rel_ref_pt.y = orig.y;
  1445. orig = end;
  1446. /*cf above*/
  1447. ct_orig

Large files files are truncated, but you can click here to view the full file