PageRenderTime 50ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/src/scenegraph/svg_attributes.c

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

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