PageRenderTime 19ms CodeModel.GetById 64ms app.highlight 146ms RepoModel.GetById 1ms app.codeStats 4ms

/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

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

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