PageRenderTime 749ms CodeModel.GetById 7ms app.highlight 669ms RepoModel.GetById 1ms app.codeStats 3ms

/src/scenegraph/svg_attributes.c

https://github.com/svettom/gpac-1
C | 6443 lines | 5960 code | 325 blank | 158 comment | 1469 complexity | a2132d966357a269fa240b28914370a7 MD5 | raw file
   1/*
   2 *					GPAC Multimedia Framework
   3 *
   4 *			Authors: Cyril Concolato - Jean le Feuvre - Jean-Claude Moissinac
   5 *				Copyright (c) 2005-200X ENST
   6 *					All rights reserved
   7 *
   8 *  This file is part of GPAC / SVG Loader module
   9 *
  10 *  GPAC is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU Lesser General Public License as published by
  12 *  the Free Software Foundation; either version 2, or (at your option)
  13 *  any later version.
  14 *
  15 *  GPAC is distributed in the hope that it will be useful,
  16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 *  GNU Lesser General Public License for more details.
  19 *
  20 *  You should have received a copy of the GNU Lesser General Public
  21 *  License along with this library; see the file COPYING.  If not, write to
  22 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23 *
  24 */
  25
  26#include <gpac/base_coding.h>
  27#include <gpac/events.h>
  28#include <gpac/nodes_svg.h>
  29
  30#ifndef GPAC_DISABLE_SVG
  31
  32#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	/*GPAC internals*/
 165	{ GF_EVENT_SCENE_ATTACHED, "gpac_scene_attached", GF_DOM_EVENT_DOM },
 166	{ GF_EVENT_VP_RESIZE, "gpac_vp_changed", GF_DOM_EVENT_DOM },
 167};
 168
 169GF_EXPORT
 170u32 gf_dom_event_type_by_name(const char *name)
 171{
 172	u32 i, count;
 173	count = sizeof(defined_dom_events) / sizeof(struct dom_event_def);
 174	if (!name) return GF_EVENT_UNKNOWN;
 175	if ((name[0]=='o') && (name[1]=='n')) name += 2;
 176	for (i=0;i<count;i++) {
 177		if (!strcmp(name, defined_dom_events[i].name))
 178			return defined_dom_events[i].event;
 179	}
 180	return GF_EVENT_UNKNOWN;
 181}
 182
 183const char *gf_dom_event_get_name(u32 type)
 184{
 185	u32 i, count;
 186	count = sizeof(defined_dom_events) / sizeof(struct dom_event_def);
 187	for (i=0;i<count;i++) {
 188		if (defined_dom_events[i].event == type)
 189			return defined_dom_events[i].name;
 190	}
 191	return "unknown";
 192}
 193
 194u32 gf_dom_event_get_category(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].category;
 201	}
 202	return 0;
 203}
 204
 205
 206static const struct predef_keyid {u32 key_code;  const char *name; } predefined_key_identifiers[] =
 207{
 208	{ GF_KEY_ACCEPT, "Accept" },
 209	{ GF_KEY_AGAIN, "Again" },
 210	{ GF_KEY_ALLCANDIDATES, "AllCandidates" },
 211	{ GF_KEY_ALPHANUM, "Alphanumeric" },
 212	{ GF_KEY_ALT, "Alt" },
 213	{ GF_KEY_ALTGRAPH, "AltGraph" },
 214	{ GF_KEY_APPS, "Apps" },
 215	{ GF_KEY_ATTN, "Attn" },
 216	{ GF_KEY_BROWSERBACK, "BrowserBack" },
 217	{ GF_KEY_BROWSERFAVORITES, "BrowserFavorites" },
 218	{ GF_KEY_BROWSERFORWARD, "BrowserForward" },
 219	{ GF_KEY_BROWSERHOME, "BrowserHome" },
 220	{ GF_KEY_BROWSERREFRESH, "BrowserRefresh" },
 221	{ GF_KEY_BROWSERSEARCH, "BrowserSearch" },
 222	{ GF_KEY_BROWSERSTOP, "BrowserStop" },
 223	{ GF_KEY_CAPSLOCK, "CapsLock" },
 224	{ GF_KEY_CLEAR, "Clear" },
 225	{ GF_KEY_CODEINPUT, "CodeInput" },
 226	{ GF_KEY_COMPOSE, "Compose" },
 227	{ GF_KEY_CONTROL, "Control" },
 228	{ GF_KEY_CRSEL, "Crsel" },
 229	{ GF_KEY_CONVERT, "Convert" },
 230	{ GF_KEY_COPY, "Copy"  },
 231	{ GF_KEY_CUT, "Cut" },
 232	{ GF_KEY_DOWN, "Down" },
 233	{ GF_KEY_END, "End" },
 234	{ GF_KEY_ENTER, "Enter" },
 235	{ GF_KEY_ERASEEOF, "EraseEof" },
 236	{ GF_KEY_EXECUTE, "Execute" },
 237	{ GF_KEY_EXSEL, "Exsel" },
 238	{ GF_KEY_F1, "F1" },
 239	{ GF_KEY_F2, "F2" },
 240	{ GF_KEY_F3, "F3" },
 241	{ GF_KEY_F4, "F4" },
 242	{ GF_KEY_F5, "F5" },
 243	{ GF_KEY_F6, "F6" },
 244	{ GF_KEY_F7, "F7" },
 245	{ GF_KEY_F8, "F8" },
 246	{ GF_KEY_F9, "F9" },
 247	{ GF_KEY_F10, "F10" },
 248	{ GF_KEY_F11, "F11" },
 249	{ GF_KEY_F12, "F12" },
 250	{ GF_KEY_F13, "F13" },
 251	{ GF_KEY_F14, "F14" },
 252	{ GF_KEY_F15, "F15" },
 253	{ GF_KEY_F16, "F16" },
 254	{ GF_KEY_F17, "F17" },
 255	{ GF_KEY_F18, "F18" },
 256	{ GF_KEY_F19, "F19" },
 257	{ GF_KEY_F20, "F20" },
 258	{ GF_KEY_F21, "F21" },
 259	{ GF_KEY_F22, "F22" },
 260	{ GF_KEY_F23, "F23" },
 261	{ GF_KEY_F24, "F24" },
 262	{ GF_KEY_FINALMODE, "FinalMode" },
 263	{ GF_KEY_FIND, "Find" },
 264	{ GF_KEY_FULLWIDTH, "FullWidth" },
 265	{ GF_KEY_HALFWIDTH, "HalfWidth" },
 266	{ GF_KEY_HANGULMODE, "HangulMode" },
 267	{ GF_KEY_HANJAMODE, "HanjaMode"   },
 268	{ GF_KEY_HELP, "Help" },
 269	{ GF_KEY_HIRAGANA, "Hiragana" },
 270	{ GF_KEY_HOME, "Home" },
 271	{ GF_KEY_INSERT, "Insert" },
 272	{ GF_KEY_JAPANESEHIRAGANA, "JapaneseHiragana" },
 273	{ GF_KEY_JAPANESEKATAKANA, "JapaneseKatakana" },
 274	{ GF_KEY_JAPANESEROMAJI, "JapaneseRomaji" },
 275	{ GF_KEY_JUNJAMODE, "JunjaMode" },
 276	{ GF_KEY_KANAMODE, "KanaMode"   },
 277	{ GF_KEY_KANJIMODE, "KanjiMode" },
 278	{ GF_KEY_KATAKANA, "Katakana"   },
 279	{ GF_KEY_LAUNCHAPPLICATION1, "LaunchApplication1" },
 280	{ GF_KEY_LAUNCHAPPLICATION2, "LaunchApplication2" },
 281	{ GF_KEY_LAUNCHMAIL, "LaunchMail" },
 282	{ GF_KEY_LEFT, "Left" },
 283	{ GF_KEY_META, "Meta" },
 284	{ GF_KEY_MEDIANEXTTRACK, "MediaNextTrack" },
 285	{ GF_KEY_MEDIAPLAYPAUSE, "MediaPlayPause" },
 286	{ GF_KEY_MEDIAPREVIOUSTRACK, "MediaPreviousTrack" },
 287	{ GF_KEY_MEDIASTOP, "MediaStop" },
 288	{ GF_KEY_MODECHANGE, "ModeChange" },
 289	{ GF_KEY_NONCONVERT, "Nonconvert" },
 290	{ GF_KEY_NUMLOCK, "NumLock" },
 291	{ GF_KEY_PAGEDOWN, "PageDown" },
 292	{ GF_KEY_PAGEUP, "PageUp" },
 293	{ GF_KEY_PASTE, "Paste" },
 294	{ GF_KEY_PAUSE, "Pause" },
 295	{ GF_KEY_PLAY, "Play" },
 296	{ GF_KEY_PREVIOUSCANDIDATE, "PreviousCandidate" },
 297	{ GF_KEY_PRINTSCREEN, "PrintScreen" },
 298	{ GF_KEY_PROCESS, "Process" },
 299	{ GF_KEY_PROPS, "Props" },
 300	{ GF_KEY_RIGHT, "Right" },
 301	{ GF_KEY_ROMANCHARACTERS, "RomanCharacters" },
 302	{ GF_KEY_SCROLL, "Scroll" },
 303	{ GF_KEY_SELECT, "Select" },
 304	{ GF_KEY_SELECTMEDIA, "SelectMedia" },
 305	{ GF_KEY_SHIFT, "Shift" },
 306	{ GF_KEY_STOP, "Stop" },
 307	{ GF_KEY_UP, "Up" },
 308	{ GF_KEY_UNDO, "Undo" },
 309	{ GF_KEY_VOLUMEDOWN, "VolumeDown" },
 310	{ GF_KEY_VOLUMEMUTE, "VolumeMute" },
 311	{ GF_KEY_VOLUMEUP, "VolumeUp" },
 312	{ GF_KEY_WIN, "Win" },
 313	{ GF_KEY_ZOOM, "Zoom" },
 314	{ GF_KEY_BACKSPACE, "U+0008" },
 315	{ GF_KEY_TAB, "U+0009" },
 316	{ GF_KEY_CANCEL, "U+0018" },
 317	{ GF_KEY_ESCAPE, "U+001B" },
 318	{ GF_KEY_SPACE, "U+0020" },
 319	{ GF_KEY_EXCLAMATION, "U+0021" },
 320	{ GF_KEY_QUOTATION, "U+0022" },
 321	{ GF_KEY_NUMBER, "U+0023" },
 322	{ GF_KEY_DOLLAR, "U+0024" },
 323	{ GF_KEY_AMPERSAND, "U+0026" },
 324	{ GF_KEY_APOSTROPHE, "U+0027" },
 325	{ GF_KEY_LEFTPARENTHESIS, "U+0028" },
 326	{ GF_KEY_RIGHTPARENTHESIS, "U+0029" },
 327	{ GF_KEY_STAR, "U+002A" },
 328	{ GF_KEY_PLUS, "U+002B" },
 329	{ GF_KEY_COMMA, "U+002C" },
 330	{ GF_KEY_HYPHEN, "U+002D" },
 331	{ GF_KEY_FULLSTOP, "U+002E" },
 332	{ GF_KEY_SLASH, "U+002F" },
 333	{ GF_KEY_0, "U+0030" },
 334	{ GF_KEY_1, "U+0031" },
 335	{ GF_KEY_2, "U+0032" },
 336	{ GF_KEY_3, "U+0033" },
 337	{ GF_KEY_4, "U+0034" },
 338	{ GF_KEY_5, "U+0035" },
 339	{ GF_KEY_6, "U+0036" },
 340	{ GF_KEY_7, "U+0037" },
 341	{ GF_KEY_8, "U+0038" },
 342	{ GF_KEY_9, "U+0039" },
 343	{ GF_KEY_COLON, "U+003A" },
 344	{ GF_KEY_SEMICOLON, "U+003B" },
 345	{ GF_KEY_LESSTHAN, "U+003C" },
 346	{ GF_KEY_EQUALS, "U+003D" },
 347	{ GF_KEY_GREATERTHAN, "U+003E" },
 348	{ GF_KEY_QUESTION, "U+003F" },
 349	{ GF_KEY_AT, "U+0040" },
 350	{ GF_KEY_A, "U+0041" },
 351	{ GF_KEY_B, "U+0042" },
 352	{ GF_KEY_C, "U+0043" },
 353	{ GF_KEY_D, "U+0044" },
 354	{ GF_KEY_E, "U+0045" },
 355	{ GF_KEY_F, "U+0046" },
 356	{ GF_KEY_G, "U+0047" },
 357	{ GF_KEY_H, "U+0048" },
 358	{ GF_KEY_I, "U+0049" },
 359	{ GF_KEY_J, "U+004A" },
 360	{ GF_KEY_K, "U+004B" },
 361	{ GF_KEY_L, "U+004C" },
 362	{ GF_KEY_M, "U+004D" },
 363	{ GF_KEY_N, "U+004E" },
 364	{ GF_KEY_O, "U+004F" },
 365	{ GF_KEY_P, "U+0050" },
 366	{ GF_KEY_Q, "U+0051" },
 367	{ GF_KEY_R, "U+0052" },
 368	{ GF_KEY_S, "U+0053" },
 369	{ GF_KEY_T, "U+0054" },
 370	{ GF_KEY_U, "U+0055" },
 371	{ GF_KEY_V, "U+0056" },
 372	{ GF_KEY_W, "U+0057" },
 373	{ GF_KEY_X, "U+0058" },
 374	{ GF_KEY_Y, "U+0059" },
 375	{ GF_KEY_Z, "U+005A" },
 376	{ GF_KEY_LEFTSQUAREBRACKET, "U+005B" },
 377	{ GF_KEY_BACKSLASH, "U+005C" },
 378	{ GF_KEY_RIGHTSQUAREBRACKET, "U+005D" },
 379	{ GF_KEY_CIRCUM, "U+005E" },
 380	{ GF_KEY_UNDERSCORE, "U+005F" },
 381	{ GF_KEY_GRAVEACCENT, "U+0060" },
 382	{ GF_KEY_LEFTCURLYBRACKET, "U+007B" },
 383	{ GF_KEY_PIPE, "U+007C" },
 384	{ GF_KEY_RIGHTCURLYBRACKET, "U+007D" },
 385	{ GF_KEY_DEL, "U+007F" },
 386	{ GF_KEY_INVERTEXCLAMATION, "U+00A1" },
 387	{ GF_KEY_DEADGRAVE, "U+0300" },
 388	{ GF_KEY_DEADEACUTE, "U+0301" },
 389	{ GF_KEY_DEADCIRCUM, "U+0302" },
 390	{ GF_KEY_DEADTILDE, "U+0303" },
 391	{ GF_KEY_DEADMACRON, "U+0304" },
 392	{ GF_KEY_DEADBREVE, "U+0306" },
 393	{ GF_KEY_DEADABOVEDOT, "U+0307" },
 394	{ GF_KEY_DEADDIARESIS, "U+0308" },
 395	{ GF_KEY_DEADRINGABOVE, "U+030A" },
 396	{ GF_KEY_DEADDOUBLEACUTE, "U+030B" },
 397	{ GF_KEY_DEADCARON, "U+030C" },
 398	{ GF_KEY_DEADCEDILLA, "U+0327" },
 399	{ GF_KEY_DEADOGONEK, "U+0328" },
 400	{ GF_KEY_DEADIOTA, "U+0345" },
 401	{ GF_KEY_EURO, "U+20AC" },
 402	{ GF_KEY_DEADVOICESOUND, "U+3099" },
 403	{ GF_KEY_DEADSEMIVOICESOUND, "U+309A" },
 404	{ GF_KEY_CHANNELUP, "ChannelUp" },
 405	{ GF_KEY_CHANNELDOWN, "ChannelDown" },
 406	{ GF_KEY_TEXT, "Text" },
 407	{ GF_KEY_INFO, "Info" },
 408	{ GF_KEY_EPG, "EPG" },
 409	{ GF_KEY_RECORD, "Record" },
 410	{ GF_KEY_BEGINPAGE, "BeginPage" }
 411};
 412
 413
 414GF_EXPORT
 415const char *gf_dom_get_key_name(u32 key_identifier)
 416{
 417	u32 count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid);
 418	if (!key_identifier || count<=key_identifier) return "Unknown";
 419	return predefined_key_identifiers[key_identifier-1].name;
 420}
 421
 422
 423u32 gf_dom_get_key_type(char *key_name)
 424{
 425	if (strlen(key_name) == 1) {
 426		char c[2];
 427		c[0] = key_name[0];
 428		c[1] = 0;
 429		strupr(c);
 430		if (c[0] >= 'A' && c[0] <= 'Z')
 431			return (GF_KEY_A + (c[0] - 'A') );
 432
 433		if (c[0] >= '0' && c[0] <= '9')
 434			return ( GF_KEY_0 + (c[0] - '0') );
 435
 436		switch (c[0]) {
 437		case '@': return GF_KEY_AT;
 438		case '*': return GF_KEY_STAR;
 439		case '#': return GF_KEY_NUMBER;
 440		case ' ': return GF_KEY_SPACE;
 441		case '!': return GF_KEY_EXCLAMATION;
 442		case '"': return GF_KEY_QUOTATION;
 443		case '$': return GF_KEY_DOLLAR;
 444		case '&': return GF_KEY_AMPERSAND;
 445		case '\'': return GF_KEY_APOSTROPHE;
 446		case '(': return GF_KEY_LEFTPARENTHESIS;
 447		case ')': return GF_KEY_RIGHTPARENTHESIS;
 448		case '+': return GF_KEY_PLUS;
 449		case ',': return GF_KEY_COMMA;
 450		case '-': return GF_KEY_HYPHEN;
 451		case '.': return GF_KEY_FULLSTOP;
 452		case '/': return GF_KEY_SLASH;
 453		case ':': return GF_KEY_COLON;
 454		case ';': return GF_KEY_SEMICOLON;
 455		case '<': return GF_KEY_LESSTHAN;
 456		case '=': return GF_KEY_EQUALS;
 457		case '>': return GF_KEY_GREATERTHAN;
 458		case '?': return GF_KEY_QUESTION;
 459		case '[': return GF_KEY_LEFTSQUAREBRACKET;
 460		case '\\': return GF_KEY_BACKSLASH;
 461		case ']': return GF_KEY_RIGHTSQUAREBRACKET;
 462		case '^': return GF_KEY_CIRCUM;
 463		case '_': return GF_KEY_UNDERSCORE;
 464		case '`': return GF_KEY_GRAVEACCENT;
 465		case '{': return GF_KEY_LEFTCURLYBRACKET;
 466		case '|': return GF_KEY_PIPE;
 467		case '}': return GF_KEY_RIGHTCURLYBRACKET;
 468		case 'Ą': return GF_KEY_INVERTEXCLAMATION;
 469		default: return GF_KEY_UNIDENTIFIED;
 470		}
 471	} else {
 472		u32 i, count;
 473		count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid);
 474		for (i=0; i<count; i++) {
 475			if (!stricmp(key_name, predefined_key_identifiers[i].name)) {
 476				return predefined_key_identifiers[i].key_code;
 477			}
 478		}
 479		return GF_KEY_UNIDENTIFIED;
 480	}
 481}
 482
 483/* Basic SVG datatype parsing functions */
 484static const struct predef_col { const char *name; u8 r; u8 g; u8 b; } predefined_colors[] =
 485{
 486	{"aliceblue",240, 248, 255},
 487	{"antiquewhite",250, 235, 215},
 488	{"aqua", 0, 255, 255},
 489	{"aquamarine",127, 255, 212},
 490	{"azure",240, 255, 255},
 491	{"beige",245, 245, 220},
 492	{"bisque",255, 228, 196},
 493	{"black", 0, 0, 0},
 494	{"blanchedalmond",255, 235, 205},
 495	{"blue", 0, 0, 255},
 496	{"blueviolet",138, 43, 226},
 497	{"brown",165, 42, 42},
 498	{"burlywood",222, 184, 135},
 499	{"cadetblue", 95, 158, 160},
 500	{"chartreuse",127, 255, 0},
 501	{"chocolate",210, 105, 30},
 502	{"coral",255, 127, 80},
 503	{"lightpink",255, 182, 193},
 504	{"lightsalmon",255, 160, 122},
 505	{"lightseagreen", 32, 178, 170},
 506	{"lightskyblue",135, 206, 250},
 507	{"lightslategray",119, 136, 153},
 508	{"lightslategrey",119, 136, 153},
 509	{"lightsteelblue",176, 196, 222},
 510	{"lightyellow",255, 255, 224},
 511	{"lime", 0, 255, 0},
 512	{"limegreen", 50, 205, 50},
 513	{"linen",250, 240, 230},
 514	{"magenta",255, 0, 255},
 515	{"maroon",128, 0, 0},
 516	{"mediumaquamarine",102, 205, 170},
 517	{"mediumblue", 0, 0, 205},
 518	{"mediumorchid",186, 85, 211},
 519	{"cornflowerblue",100, 149, 237},
 520	{"cornsilk",255, 248, 220},
 521	{"crimson",220, 20, 60},
 522	{"cyan", 0, 255, 255},
 523	{"darkblue", 0, 0, 139},
 524	{"darkcyan", 0, 139, 139},
 525	{"darkgoldenrod",184, 134, 11},
 526	{"darkgray",169, 169, 169},
 527	{"darkgreen", 0, 100, 0},
 528	{"darkgrey",169, 169, 169},
 529	{"darkkhaki",189, 183, 107},
 530	{"darkmagenta",139, 0, 139},
 531	{"darkolivegreen", 85, 107, 47},
 532	{"darkorange",255, 140, 0},
 533	{"darkorchid",153, 50, 204},
 534	{"darkred",139, 0, 0},
 535	{"darksalmon",233, 150, 122},
 536	{"darkseagreen",143, 188, 143},
 537	{"darkslateblue", 72, 61, 139},
 538	{"darkslategray", 47, 79, 79},
 539	{"darkslategrey", 47, 79, 79},
 540	{"darkturquoise", 0, 206, 209},
 541	{"darkviolet",148, 0, 211},
 542	{"deeppink",255, 20, 147},
 543	{"deepskyblue", 0, 191, 255},
 544	{"dimgray",105, 105, 105},
 545	{"dimgrey",105, 105, 105},
 546	{"dodgerblue", 30, 144, 255},
 547	{"firebrick",178, 34, 34},
 548	{"floralwhite",255, 250, 240},
 549	{"forestgreen", 34, 139, 34},
 550	{"fuchsia",255, 0, 255},
 551	{"gainsboro",220, 220, 220},
 552	{"ghostwhite",248, 248, 255},
 553	{"gold",255, 215, 0},
 554	{"goldenrod",218, 165, 32},
 555	{"gray",128, 128, 128},
 556	{"grey",128, 128, 128},
 557	{"green", 0, 128, 0},
 558	{"greenyellow",173, 255, 47},
 559	{"honeydew",240, 255, 240},
 560	{"hotpink",255, 105, 180},
 561	{"indianred",205, 92, 92},
 562	{"indigo", 75, 0, 130},
 563	{"ivory",255, 255, 240},
 564	{"khaki",240, 230, 140},
 565	{"lavender",230, 230, 25},
 566	{"lavenderblush",255, 240, 245},
 567	{"mediumpurple",147, 112, 219},
 568	{"mediumseagreen", 60, 179, 113},
 569	{"mediumslateblue",123, 104, 238},
 570	{"mediumspringgreen", 0, 250, 154},
 571	{"mediumturquoise", 72, 209, 204},
 572	{"mediumvioletred",199, 21, 133},
 573	{"midnightblue", 25, 25, 112},
 574	{"mintcream",245, 255, 250},
 575	{"mistyrose",255, 228, 225},
 576	{"moccasin",255, 228, 181},
 577	{"navajowhite",255, 222, 173},
 578	{"navy", 0, 0, 128},
 579	{"oldlace",253, 245, 230},
 580	{"olive",128, 128, 0},
 581	{"olivedrab",107, 142, 35},
 582	{"orange",255, 165, 0},
 583	{"orangered",255, 69, 0},
 584	{"orchid",218, 112, 214},
 585	{"palegoldenrod",238, 232, 170},
 586	{"palegreen",152, 251, 152},
 587	{"paleturquoise",175, 238, 238},
 588	{"palevioletred",219, 112, 147},
 589	{"papayawhip",255, 239, 213},
 590	{"peachpuff",255, 218, 185},
 591	{"peru",205, 133, 63},
 592	{"pink",255, 192, 203},
 593	{"plum",221, 160, 221},
 594	{"powderblue",176, 224, 230},
 595	{"purple",128, 0, 128},
 596	{"red",255, 0, 0},
 597	{"rosybrown",188, 143, 143},
 598	{"royalblue", 65, 105, 225},
 599	{"saddlebrown",139, 69, 19},
 600	{"salmon",250, 128, 114},
 601	{"sandybrown",244, 164, 96},
 602	{"seagreen", 46, 139, 87},
 603	{"seashell",255, 245, 238},
 604	{"sienna",160, 82, 45},
 605	{"silver",192, 192, 192},
 606	{"skyblue",135, 206, 235},
 607	{"slateblue",106, 90, 205},
 608	{"slategray",112, 128, 144},
 609	{"slategrey",112, 128, 144},
 610	{"snow",255, 250, 250},
 611	{"springgreen", 0, 255, 127},
 612	{"steelblue", 70, 130, 180},
 613	{"tan",210, 180, 140},
 614	{"teal", 0, 128, 128},
 615	{"lawngreen",124, 252, 0},
 616	{"lemonchiffon",255, 250, 205},
 617	{"lightblue",173, 216, 230},
 618	{"lightcoral",240, 128, 128},
 619	{"lightcyan",224, 255, 255},
 620	{"lightgoldenrodyellow",250, 250, 210},
 621	{"lightgray",211, 211, 211},
 622	{"lightgreen",144, 238, 144},
 623	{"lightgrey",211, 211, 211},
 624	{"thistle",216, 191, 216},
 625	{"tomato",255, 99, 71},
 626	{"turquoise", 64, 224, 208},
 627	{"violet",238, 130, 238},
 628	{"wheat",245, 222, 179},
 629	{"white",255, 255, 255},
 630	{"whitesmoke",245, 245, 245},
 631	{"yellow",255, 255, 0},
 632	{"yellowgreen",154, 205, 50}
 633
 634};
 635
 636
 637/* Basic SVG datatype parsing functions */
 638static const struct sys_col { const char *name; u8 type; } system_colors[] =
 639{
 640	{"ActiveBorder", SVG_COLOR_ACTIVE_BORDER},
 641	{"ActiveCaption", SVG_COLOR_ACTIVE_CAPTION},
 642	{"AppWorkspace", SVG_COLOR_APP_WORKSPACE},
 643	{"Background", SVG_COLOR_BACKGROUND},
 644	{"ButtonFace", SVG_COLOR_BUTTON_FACE},
 645	{"ButtonHighlight", SVG_COLOR_BUTTON_HIGHLIGHT},
 646	{"ButtonShadow", SVG_COLOR_BUTTON_SHADOW},
 647	{"ButtonText", SVG_COLOR_BUTTON_TEXT},
 648	{"CaptionText", SVG_COLOR_CAPTION_TEXT},
 649	{"GrayText", SVG_COLOR_GRAY_TEXT},
 650	{"Highlight", SVG_COLOR_HIGHLIGHT},
 651	{"HighlightText", SVG_COLOR_HIGHLIGHT_TEXT},
 652	{"InactiveBorder", SVG_COLOR_INACTIVE_BORDER},
 653	{"InactiveCaption", SVG_COLOR_INACTIVE_CAPTION},
 654	{"InactiveCaptionText", SVG_COLOR_INACTIVE_CAPTION_TEXT},
 655	{"InfoBackground", SVG_COLOR_INFO_BACKGROUND},
 656	{"InfoText", SVG_COLOR_INFO_TEXT},
 657	{"Menu", SVG_COLOR_MENU},
 658	{"MenuText", SVG_COLOR_MENU_TEXT},
 659	{"Scrollbar", SVG_COLOR_SCROLLBAR},
 660	{"ThreeDDarkShadow", SVG_COLOR_3D_DARK_SHADOW},
 661	{"ThreeDFace", SVG_COLOR_3D_FACE},
 662	{"ThreeDHighlight", SVG_COLOR_3D_HIGHLIGHT},
 663	{"ThreeDLightShadow", SVG_COLOR_3D_LIGHT_SHADOW},
 664	{"ThreeDShadow", SVG_COLOR_3D_SHADOW},
 665	{"Window", SVG_COLOR_WINDOW},
 666	{"WindowFrame", SVG_COLOR_WINDOW_FRAME},
 667	{"WindowText", SVG_COLOR_WINDOW_TEXT},
 668};
 669
 670/* parses an color from a named color HTML or CSS 2 */
 671static void svg_parse_named_color(SVG_Color *col, char *attribute_content)
 672{
 673	u32 i, count;
 674	count = sizeof(predefined_colors) / sizeof(struct predef_col);
 675	for (i=0; i<count; i++) {
 676		if (!strcmp(attribute_content, predefined_colors[i].name)) {
 677			col->red = INT2FIX(predefined_colors[i].r) / 255;
 678			col->green = INT2FIX(predefined_colors[i].g) / 255;
 679			col->blue = INT2FIX(predefined_colors[i].b) / 255;
 680			col->type = SVG_COLOR_RGBCOLOR;
 681			return;
 682		}
 683	}
 684	count = sizeof(system_colors) / sizeof(struct sys_col);
 685	for (i=0; i<count; i++) {
 686		if (!strcmp(attribute_content, system_colors[i].name)) {
 687			col->type = system_colors[i].type;
 688			return;
 689		}
 690	}
 691}
 692
 693const char *gf_svg_get_system_paint_server_name(u32 paint_type)
 694{
 695	u32 i, count;
 696	count = sizeof(system_colors) / sizeof(struct sys_col);
 697	for (i=0; i<count; i++) {
 698		if (paint_type == system_colors[i].type) return system_colors[i].name;
 699	}
 700	return "undefined";
 701}
 702
 703u32 gf_svg_get_system_paint_server_type(const char *name)
 704{
 705	u32 i, count;
 706	count = sizeof(system_colors) / sizeof(struct sys_col);
 707	for (i=0; i<count; i++) {
 708		if (!strcmp(name, system_colors[i].name)) return system_colors[i].type;
 709	}
 710	return 0;
 711}
 712
 713/* Reads an SVG Color
 714   either #RRGGBB, #RGB, rgb(r,g,b) in [0,255] , colorname, or 'r g b' in [0,1]
 715   ignores any space, comma, semi-column before and any space after
 716   TODO:
 717	transform the char into char and duplicate the input, instead of modifying it
 718		be more robust to errors in color description ex rgb(0 0 0)
 719*/
 720static void svg_parse_color(SVG_Color *col, char *attribute_content)
 721{
 722	char *str = attribute_content;
 723	while (str[strlen(attribute_content)-1] == ' ') str[strlen(attribute_content)-1] = 0;
 724	while (*str != 0 && (*str == ' ' || *str == ',' || *str == ';')) str++;
 725
 726	if (!strcmp(str, "currentColor")) {
 727		col->type = SVG_COLOR_CURRENTCOLOR;
 728		return;
 729	} else if (!strcmp(str, "inherit")) {
 730		col->type = SVG_COLOR_INHERIT;
 731		return;
 732	} else if (str[0]=='#') {
 733		u32 val;
 734		sscanf(str+1, "%x", &val);
 735		if (strlen(str) == 7) {
 736			col->red = INT2FIX((val>>16) & 0xFF) / 255;
 737			col->green = INT2FIX((val>>8) & 0xFF) / 255;
 738			col->blue = INT2FIX(val & 0xFF) / 255;
 739		} else {
 740			col->red = INT2FIX((val>>8) & 0xF) / 15;
 741			col->green = INT2FIX((val>>4) & 0xF) / 15;
 742			col->blue = INT2FIX(val & 0xF) / 15;
 743		}
 744		col->type = SVG_COLOR_RGBCOLOR;
 745	} else if (strstr(str, "rgb(") || strstr(str, "RGB(")) {
 746		Float _val;
 747		u8 is_percentage= 0;
 748		if (strstr(str, "%")) is_percentage = 1;
 749		str = strstr(str, "(");
 750		str++;
 751		sscanf(str, "%f", &_val); col->red = FLT2FIX(_val);
 752		str = strstr(str, ",");
 753		if (!str) {
 754			/* space separated colors/percentages are not allowed neither in SVG 1.1 nor in SVG T1.2 */
 755			col->red = col->green = col->blue = 0;
 756			return;
 757		}
 758		str++;
 759		sscanf(str, "%f", &_val); col->green = FLT2FIX(_val);
 760		str = strstr(str, ",");
 761		if (!str) {
 762			/* space separated colors/percentages are not allowed neither in SVG 1.1 nor in SVG T1.2 */
 763			col->red = col->green = col->blue = 0;
 764			return;
 765		}
 766		str++;
 767		sscanf(str, "%f", &_val); col->blue = FLT2FIX(_val);
 768		if (is_percentage) {
 769			col->red /= 100;
 770			col->green /= 100;
 771			col->blue /= 100;
 772		} else {
 773			col->red /= 255;
 774			col->green /= 255;
 775			col->blue /= 255;
 776		}
 777		col->type = SVG_COLOR_RGBCOLOR;
 778	} else if ((str[0] >= 'a' && str[0] <= 'z')
 779		|| (str[0] >= 'A' && str[0] <= 'Z')) {
 780		svg_parse_named_color(col, str);
 781	} else {
 782		Float _r, _g, _b;
 783		sscanf(str, "%f %f %f", &_r, &_g, &_b);
 784		col->red = FLT2FIX(_r);
 785		col->green = FLT2FIX(_g);
 786		col->blue = FLT2FIX(_b);
 787		col->type = SVG_COLOR_RGBCOLOR;
 788	}
 789}
 790
 791/*
 792	Reads a number (i.e. without unit) according to the CSS syntax (same as SVG paths and transforms)
 793		trims any space, comma, semi-column before or after (TODO: fix this)
 794		reads an optional + or -
 795		then reads a digit between 0 and 9
 796		optionally followed by an '.' and digits between 0 and 9
 797		optionally followed by e or E and digits between 0 and 9
 798	Returns the number of chars read in d
 799*/
 800static u32 svg_parse_number(char *d, Fixed *f, Bool is_angle)
 801{
 802    u32 nb_digit_before = 0;
 803	u32 nb_digit_after = 0;
 804    Bool has_fractional = 0;
 805	Bool is_negative = 0;
 806	Float _val = 0;
 807	u32 i = 0;
 808
 809    /* warning the comma and semicolumn should not be there when parsing a number in a path */
 810	while ((d[i] != 0) && strchr(" ,;\r\n\t", d[i])) i++;
 811
 812    if (!d[i]) {
 813        GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Parsing] Parsing number with empty string or only spaces: %s\n", d));
 814        return 0;
 815    }
 816    if (d[i] == '+') {
 817        i++;
 818    } else if (d[i] == '-') {
 819		is_negative = 1;
 820		i++;
 821	}
 822    /* Warning: this is not normal, should be detected somehow by checking the BNF */
 823	/* if ((d[i]=='N') && (d[i+1]=='a') && (d[i+2]=='N')) {
 824		i+= 3;
 825		_val = 0;
 826		goto end;
 827	}*/
 828    /* read the digit-sequence token of the BNF */
 829	while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) {
 830		_val = _val*10 + (d[i]-'0');
 831        nb_digit_before++;
 832		i++;
 833	}
 834	if (d[i] == '.') {
 835        has_fractional = 1;
 836		i++;
 837		while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) {
 838			_val = _val*10 + (d[i]-'0');
 839			nb_digit_after++;
 840			i++;
 841		}
 842        if (nb_digit_after) {
 843            _val /= (Float)pow(10,nb_digit_after);
 844        } else if (nb_digit_before == 0) {
 845            GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error in parsing number (expecting digits before or after a '.': %s\n", d));
 846            return 0;
 847        } else {
 848            /* dangling '.' without digits after. This is allowed by the BNF */
 849        }
 850	}
 851    if ((nb_digit_before == 0) && (has_fractional == 0)) {
 852        GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error in parsing number (expecting digits):%s\n", d));
 853        return 0;
 854    }
 855    /* reading the exponent */
 856	if (d[i] == 'e' || d[i] == 'E') {
 857		Bool neg_exp = 0;
 858		u32 nb_exp_digits = 0;
 859        s32 exp = 0;
 860		i++;
 861		if (d[i] == '+') i++;
 862		else if (d[i] == '-') {
 863			i++;
 864			neg_exp=1;
 865		}
 866		while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) {
 867			exp = exp*10 + (d[i]-'0');
 868            nb_exp_digits++;
 869			i++;
 870		}
 871        if (nb_exp_digits) {
 872            _val *= (Float)pow(10, neg_exp ? -exp : exp);
 873        } else {
 874            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));
 875            return 0;
 876        }
 877	}
 878    /* We can now produce the final number */
 879	if (is_negative) _val *= -1;
 880	if (is_angle) {
 881		_val/=180;
 882		(*f) = gf_mulfix(FLT2FIX(_val), GF_PI);
 883	} else {
 884		(*f) = FLT2FIX(_val);
 885	}
 886
 887    /* warning the comma and semicolumn should not be there when parsing a path number */
 888	while (d[i] != 0 && (d[i] == ' ' || d[i] == ',' || d[i] == ';')) i++;
 889	return i;
 890}
 891
 892/*
 893   Parse an Offset Value, i.e +/- Clock Value
 894*/
 895static GF_Err svg_parse_clock_value(char *d, Double *clock_value)
 896{
 897	char *tmp;
 898	s32 sign = 1;
 899
 900	if (!d) return GF_BAD_PARAM;
 901
 902	if (!d[0]) return GF_BAD_PARAM;
 903
 904	if (d[0] == '+') d++;
 905	else if (d[0] == '-') { sign = -1; d++; }
 906
 907	if (!d[0]) return GF_BAD_PARAM;
 908
 909	/* According to SVG, the following are invalid syntaxes (see animate-elem-225-t.svg)
 910		'+-2s'
 911		'1++s' even though sscanf returns the right values
 912	*/
 913	if (strchr(d, '+') || strchr(d, '-')) return GF_BAD_PARAM;
 914
 915	/* No embedded white space is allowed in clock values,
 916	   although leading and trailing white space characters will be ignored.*/
 917	while (*d == ' ') d++;
 918
 919	if ((tmp = strchr(d, ':'))) {
 920		/* Full or Partial Clock value */
 921		tmp++;
 922		if ((tmp = strchr(tmp, ':'))) {
 923			/* Full Clock value : hh:mm:ss(.frac) */
 924			u32 hours;
 925			u32 minutes;
 926			Float seconds;
 927			if (sscanf(d, "%u:%u:%f", &hours, &minutes, &seconds) < 3) return GF_BAD_PARAM;
 928			*clock_value = hours*3600 + minutes*60 + seconds;
 929		} else {
 930			/* Partial Clock value : mm:ss(.frac) */
 931			s32 minutes;
 932			Float seconds;
 933			if (sscanf(d, "%d:%f", &minutes, &seconds) < 2) return GF_BAD_PARAM;
 934			*clock_value = minutes*60 + seconds;
 935		}
 936	} else if ((tmp = strstr(d, "h"))) {
 937		Float f;
 938		if (sscanf(d, "%fh", &f) == 0) return GF_BAD_PARAM;
 939		*clock_value = 3600*f;
 940	} else if (strstr(d, "min")) {
 941		Float f;
 942		if (sscanf(d, "%fmin", &f) == 0) return GF_BAD_PARAM;
 943		*clock_value = 60*f;
 944	} else if ((tmp = strstr(d, "ms"))) {
 945		Float f;
 946		if (sscanf(d, "%fms", &f) == 0) return GF_BAD_PARAM;
 947		*clock_value = f/1000;
 948	} else if (strchr(d, 's')) {
 949		Float f;
 950		if (sscanf(d, "%fs", &f) == 0) return GF_BAD_PARAM;
 951		*clock_value = f;
 952	} else {
 953		Float f;
 954		if (sscanf(d, "%f", &f) == 0) return GF_BAD_PARAM;
 955		*clock_value = f;
 956	}
 957	*clock_value *= sign;
 958	return GF_OK;
 959}
 960/* Parses one SVG time value:
 961	  indefinite,
 962	  element_id.event_name
 963	  wallclock,
 964	  accessKey,
 965	  events,
 966	  clock value.
 967 */
 968static GF_Err smil_parse_time(GF_Node *elt, SMIL_Time *v, char *d)
 969{
 970	GF_Err e = GF_OK;
 971	char *tmp;
 972
 973	/* Offset Values */
 974	if ((d[0] >= '0' && d[0] <= '9') || d[0] == '+' || d[0] == '-'){
 975		v->type = GF_SMIL_TIME_CLOCK;
 976		return svg_parse_clock_value(d, &(v->clock));
 977	}
 978
 979	/* Indefinite Values */
 980	else if (!strcmp(d, "indefinite")) {
 981		v->type = GF_SMIL_TIME_INDEFINITE;
 982		return GF_OK;
 983	}
 984
 985	/* Wallclock Values */
 986	else if ((tmp = strstr(d, "wallclock("))) {
 987		u32 year, month, day;
 988		u32 hours, minutes;
 989		u32 nhours, nminutes;
 990		Float seconds;
 991		char *tmp1, *tmp2;
 992
 993		v->type = GF_SMIL_TIME_WALLCLOCK;
 994		tmp += 10;
 995		if ((tmp1 = strchr(tmp, 'T')) ) {
 996			/* From tmp to wallStartTime, we parse a date */
 997			sscanf(tmp, "%u-%u-%dT", &year, &month, &day);
 998			tmp1++;
 999			tmp = tmp1;
1000		}
1001		if ((tmp1 = strchr(tmp, ':')) ) {
1002			if ((tmp2 = strchr(tmp1, ':')) ) {
1003				/* HHMMSS */
1004				sscanf(tmp, "%u:%u:%f", &hours, &minutes, &seconds);
1005			} else {
1006				/* HHMM */
1007				sscanf(tmp, "%u:%u", &hours, &minutes);
1008			}
1009		}
1010		if (strchr(tmp, 'Z')) {
1011			return GF_OK;
1012		} else {
1013			if ( (tmp1 = strchr(tmp, '+')) ) {
1014				sscanf(tmp1, "%u:%u", &nhours, &nminutes);
1015			} else if ( (tmp1 = strchr(tmp, '-')) ) {
1016				sscanf(tmp1, "%u:%u", &nhours, &nminutes);
1017			}
1018		}
1019		return GF_OK;
1020	}
1021
1022	/* AccessKey Values */
1023	else if ((tmp = strstr(d, "accessKey("))) {
1024		char *sep;
1025		v->type = GF_SMIL_TIME_EVENT;
1026		v->event.type = GF_EVENT_KEYDOWN;
1027		v->element = elt->sgprivate->scenegraph->RootNode;
1028		tmp+=10;
1029		sep = strchr(d, ')');
1030		sep[0] = 0;
1031		v->event.parameter = gf_dom_get_key_type(tmp);
1032		sep++;
1033		if ((tmp = strchr(sep, '+')) || (tmp = strchr(sep, '-'))) {
1034			char c = *tmp;
1035			tmp++;
1036			e = svg_parse_clock_value(tmp, &(v->clock));
1037			if (c == '-') v->clock *= -1;
1038		}
1039		return e;
1040	}
1041
1042	else {
1043		Bool had_param = 0;
1044		char *tmp2;
1045		v->type = GF_SMIL_TIME_EVENT;
1046		if ((tmp = strchr(d, '.'))) {
1047			tmp[0] = 0;
1048			if (strlen(d) == 0) {
1049				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] expecting an id before '.' in SMIL Time .%s\n", tmp+1));
1050				return GF_BAD_PARAM;
1051			}
1052			v->element_id = gf_strdup(d);
1053			tmp[0] = '.';
1054			tmp++;
1055		} else {
1056			tmp = d;
1057		}
1058		if ((tmp2 = strchr(tmp, '('))) {
1059			tmp2[0] = 0;
1060			v->event.type = gf_dom_event_type_by_name(tmp);
1061			tmp2[0] = '(';
1062			tmp2++;
1063			had_param = 1;
1064			v->event.parameter = atoi(tmp2);
1065			tmp = strchr(tmp2, ')');
1066			if (!tmp) {
1067				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] expecting ')' in SMIL Time %s\n", d));
1068				return GF_BAD_PARAM;
1069			}
1070			tmp++;
1071		}
1072		if ((tmp2 = strchr(tmp, '+')) || (tmp2 = strchr(tmp, '-'))) {
1073			char c = *tmp2;
1074			char *tmp3 = tmp2;
1075			tmp2[0] = 0;
1076			tmp3--;
1077			while (*tmp3==' ') { *tmp3=0; tmp3--; }
1078			if (v->event.type == 0) v->event.type = gf_dom_event_type_by_name(tmp);
1079			if (!had_param && (v->event.type == GF_EVENT_REPEAT || v->event.type == GF_EVENT_REPEAT_EVENT))
1080				v->event.parameter = 1;
1081			tmp2[0] = c;
1082			tmp2++;
1083			e = svg_parse_clock_value(tmp2, &(v->clock));
1084			if (c == '-') v->clock *= -1;
1085			return e;
1086		} else {
1087			if (v->event.type == 0) v->event.type = gf_dom_event_type_by_name(tmp);
1088			if (!had_param && (v->event.type == GF_EVENT_REPEAT || v->event.type == GF_EVENT_REPEAT_EVENT))
1089				v->event.parameter = 1;
1090		}
1091	}
1092	return GF_OK;
1093}
1094
1095/* Parses a list of SVG transformations and collapses them in the given matrix */
1096Bool gf_svg_parse_transformlist(GF_Matrix2D *mat, char *attribute_content)
1097{
1098	GF_Matrix2D tmp;
1099
1100	char *str;
1101    u32 read_chars;
1102	u32 i;
1103
1104	gf_mx2d_init(*mat);
1105
1106	str = attribute_content;
1107	i = 0;
1108	while (str[i] != 0) {
1109		while (str[i] == ' ') i++;
1110		if (str[i] == ',') i++;
1111		while (str[i] == ' ') i++;
1112		if (strstr(str+i, "scale")==str+i) {
1113			i += 5;
1114			while(str[i] == ' ') i++;
1115			if (str[i] == '(') {
1116				Fixed sx, sy;
1117				i++;
1118				read_chars = svg_parse_number(&(str[i]), &sx, 0);
1119                if (!read_chars) {
1120                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading sx component in scale: %s\n", attribute_content));
1121                    return 0;
1122                }
1123                i += read_chars;
1124
1125				if (str[i] == ')') {
1126					sy = sx;
1127				} else {
1128					read_chars = svg_parse_number(&(str[i]), &sy, 0);
1129                    if (!read_chars) {
1130                        GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading sy component in scale: %s\n", attribute_content));
1131                        return 0;
1132                    }
1133                    i += read_chars;
1134				}
1135				gf_mx2d_init(tmp);
1136				gf_mx2d_add_scale(&tmp, sx, sy);
1137				gf_mx2d_add_matrix(&tmp, mat);
1138				gf_mx2d_copy(*mat, tmp);
1139
1140				while(str[i] == ' ') i++;
1141				if (str[i] == ')') i++;
1142				else {
1143                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1144                    return 0;
1145				}
1146			} else {
1147				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
1148                return 0;
1149			}
1150		} else if (strstr(str+i, "translate")==str+i) {
1151			i += 9;
1152			while(str[i] == ' ') i++;
1153			if (str[i] == '(') {
1154				Fixed tx, ty;
1155				i++;
1156				read_chars = svg_parse_number(&(str[i]), &tx, 0);
1157                if (!read_chars) {
1158                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading tx component in translate: %s\n", attribute_content));
1159                    return 0;
1160                }
1161                i += read_chars;
1162				if (str[i] == ')') {
1163					ty = 0;
1164				} else {
1165					read_chars = svg_parse_number(&(str[i]), &ty, 0);
1166                    if (!read_chars) {
1167                        GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading ty component in translate: %s\n", attribute_content));
1168                        return 0;
1169                    }
1170                    i += read_chars;
1171				}
1172				gf_mx2d_init(tmp);
1173				gf_mx2d_add_translation(&tmp, tx, ty);
1174				gf_mx2d_add_matrix(&tmp, mat);
1175				gf_mx2d_copy(*mat, tmp);
1176				while(str[i] == ' ') i++;
1177				if (str[i] == ')') i++;
1178				else {
1179                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1180                    return 0;
1181				}
1182			} else {
1183				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
1184                return 0;
1185			}
1186		} else if (strstr(str+i, "rotate")==str+i) {
1187			i += 6;
1188			while(str[i] == ' ') i++;
1189			if (str[i] == '(') {
1190				Fixed angle, cx, cy;
1191				i++;
1192				read_chars = svg_parse_number(&(str[i]), &angle, 1);
1193                if (!read_chars) {
1194                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading angle component in rotate: %s\n", attribute_content));
1195                    return 0;
1196                }
1197                i += read_chars;
1198				if (str[i] == ')') {
1199					cx = cy = 0;
1200				} else {
1201					read_chars = svg_parse_number(&(str[i]), &cx, 0);
1202                    if (!read_chars) {
1203                        GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading cx component in rotate: %s\n", attribute_content));
1204                        return 0;
1205                    }
1206                    i += read_chars;
1207					read_chars = svg_parse_number(&(str[i]), &cy, 0);
1208                    if (!read_chars) {
1209                        GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading cy component in rotate: %s\n", attribute_content));
1210                        return 0;
1211                    }
1212                    i += read_chars;
1213				}
1214				gf_mx2d_init(tmp);
1215				gf_mx2d_add_rotation(&tmp, cx, cy, angle);
1216				gf_mx2d_add_matrix(&tmp, mat);
1217				gf_mx2d_copy(*mat, tmp);
1218				while(str[i] == ' ') i++;
1219				if (str[i] == ')') i++;
1220				else {
1221                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1222                    return 0;
1223				}
1224			} else {
1225				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
1226                return 0;
1227			}
1228		} else if (strstr(str+i, "skewX")==str+i) {
1229			i += 5;
1230			while(str[i] == ' ') i++;
1231			if (str[i] == '(') {
1232				Fixed angle;
1233				i++;
1234				read_chars = svg_parse_number(&(str[i]), &angle, 1);
1235                if (!read_chars) {
1236                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading angle in skewX: %s\n", attribute_content));
1237                    return 0;
1238                }
1239                i += read_chars;
1240				gf_mx2d_init(tmp);
1241				gf_mx2d_add_skew_x(&tmp, angle);
1242				gf_mx2d_add_matrix(&tmp, mat);
1243				gf_mx2d_copy(*mat, tmp);
1244				while(str[i] == ' ') i++;
1245				if (str[i] == ')') i++;
1246				else {
1247                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1248                    return 0;
1249				}
1250			} else {
1251				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
1252                return 0;
1253			}
1254		} else if (strstr(str+i, "skewY")==str+i) {
1255			i += 5;
1256			while(str[i] == ' ') i++;
1257			if (str[i] == '(') {
1258				Fixed angle;
1259				i++;
1260				read_chars = svg_parse_number(&(str[i]), &angle, 1);
1261                if (!read_chars) {
1262                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading angle component in skewY: %s\n", attribute_content));
1263                    return 0;
1264                }
1265                i += read_chars;
1266				gf_mx2d_init(tmp);
1267				gf_mx2d_add_skew_y(&tmp, angle);
1268				gf_mx2d_add_matrix(&tmp, mat);
1269				gf_mx2d_copy(*mat, tmp);
1270				while(str[i] == ' ') i++;
1271				if (str[i] == ')') i++;
1272				else {
1273                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1274                    return 0;
1275				}
1276			} else {
1277				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
1278                return 0;
1279			}
1280		} else if (strstr(str+i, "matrix")==str+i) {
1281			i+=6;
1282			while(str[i] == ' ') i++;
1283			if (str[i] == '(') {
1284				i++;
1285				read_chars = svg_parse_number(&(str[i]), &(tmp.m[0]), 0);
1286                if (!read_chars) {
1287                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient a in matrix: %s\n", attribute_content));
1288                    return 0;
1289                }
1290                i += read_chars;
1291				read_chars = svg_parse_number(&(str[i]), &(tmp.m[3]), 0);
1292                if (!read_chars) {
1293                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient b in matrix: %s\n", attribute_content));
1294                    return 0;
1295                }
1296                i += read_chars;
1297				read_chars = svg_parse_number(&(str[i]), &(tmp.m[1]), 0);
1298                if (!read_chars) {
1299                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient c in matrix: %s\n", attribute_content));
1300                    return 0;
1301                }
1302                i += read_chars;
1303				read_chars = svg_parse_number(&(str[i]), &(tmp.m[4]), 0);
1304                if (!read_chars) {
1305                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient d in matrix: %s\n", attribute_content));
1306                    return 0;
1307                }
1308                i += read_chars;
1309				read_chars = svg_parse_number(&(str[i]), &(tmp.m[2]), 0);
1310                if (!read_chars) {
1311                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient e in matrix: %s\n", attribute_content));
1312                    return 0;
1313                }
1314                i += read_chars;
1315				read_chars = svg_parse_number(&(str[i]), &(tmp.m[5]), 0);
1316                if (!read_chars) {
1317                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient f in matrix: %s\n", attribute_content));
1318                    return 0;
1319                }
1320                i += read_chars;
1321				gf_mx2d_add_matrix(&tmp, mat);
1322				gf_mx2d_copy(*mat, tmp);
1323				while(str[i] == ' ') i++;
1324				if (str[i] == ')') i++;
1325				else {
1326                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1327                    return 0;
1328				}
1329			} else {
1330				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content));
1331                return 0;
1332			}
1333		} else {
1334			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Unrecognized transofrm type in attribute %s\n", attribute_content));
1335            return 0;
1336		}
1337		/*for svgView parsing*/
1338		if (str[i] == ')') i++;
1339	}
1340    return 1;
1341}
1342
1343/* Parses an SVG transform attribute and collapses all in the given matrix */
1344static Bool svg_parse_transform(SVG_Transform *t, char *attribute_content)
1345{
1346	char *str;
1347	u32 i;
1348    u32 read_chars;
1349	str = attribute_content;
1350	i = 0;
1351
1352	if ((str = strstr(attribute_content, "ref"))) {
1353		t->is_ref = 1;
1354		gf_mx2d_init(t->mat);
1355		str+=2;
1356		while (str[i] == ' ') i++;
1357		if (str[i] == '(') {
1358			i++;
1359			while (str[i] == ' ') i++;
1360			if (str[i] == 's' && str[i+1] == 'v' && str[i+2] == 'g') {
1361				i+=3;
1362				while (str[i] == ' ') i++;
1363                if (str[i] == ',') {
1364                    i++;
1365                } else if (str[i] == ')') {
1366					i++;
1367					return GF_OK;
1368				}
1369				read_chars = svg_parse_number(&(str[i]), &(t->mat.m[2]), 0);
1370                if (!read_chars) {
1371                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient tx in ref transform: %s\n", attribute_content));
1372                    return GF_BAD_PARAM;
1373                }
1374                i += read_chars;
1375				read_chars = svg_parse_number(&(str[i]), &(t->mat.m[5]), 0);
1376                if (!read_chars) {
1377                    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error reading coefficient ty in ref transform: %s\n", attribute_content));
1378                    return GF_BAD_PARAM;
1379                }
1380                i += read_chars;
1381            } else {
1382				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Unsupported syntax for ref transform attribute"));
1383            }
1384			while (str[i] == ' ') i++;
1385			if (str[i] == ')') i++;
1386			else {
1387                GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing closing parenthesis in transform attribute: %s\n", attribute_content));
1388			}
1389			return GF_OK;
1390		} else {
1391            GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in ref transform attribute: %s\n", attribute_content));
1392			return GF_BAD_PARAM;
1393		}
1394	} else {
1395		read_chars = gf_svg_parse_transformlist(&t->mat, attribute_content);
1396        if (!read_chars) {
1397            GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error parsing transform list: %s\n", attribute_content));
1398            return GF_BAD_PARAM;
1399        }
1400	}
1401	return GF_OK;
1402}
1403
1404#undef REMOVE_ALLOC
1405
1406#if USE_GF_PATH
1407
1408//#define PARSE_PATH_ONLY
1409
1410static void svg_parse_path(SVG_PathData *path, char *attribute_content)
1411{
1412	char *d = attribute_content;
1413
1414    /* used to detect end of BNF production:
1415    "The processing of the BNF must consume as much of a given BNF production as possible,
1416    stopping at the point when a character is encountered which no longer satisfies the production." */
1417    u32 read_chars = 0;
1418
1419	/* Point used to start a new subpath when the previous subpath is closed */
1420	SVG_Point prev_m_pt;
1421	/* Point used to convert relative 'lower-case commands' into absolute */
1422	SVG_Point rel_ref_pt;
1423	/* Points used to convert S, T commands into C, Q */
1424	SVG_Point orig, ct_orig, ct_end, end;
1425	/* Used by elliptical arcs */
1426	Fixed x_axis_rotation, large_arc_flag, sweep_flag;
1427
1428	char c, prev_c;
1429	u32 i;
1430
1431	if (*d == 0) return;
1432
1433	i = 0;
1434	prev_c = 'M';
1435	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;
1436	while(1) {
1437		while ( (d[i]==' ') || (d[i] =='\t') || (d[i] =='\r') || (d[i] =='\n') ) i++;
1438		c = d[i];
1439		if (! c) break;
1440next_command:
1441		switch (c) {
1442		case 'm':
1443		case 'M':
1444			i++;
1445			read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
1446            if (!read_chars) return;
1447            i += read_chars;
1448			read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
1449            if (!read_chars) return;
1450            i += read_chars;
1451			if (c == 'm') {
1452				orig.x += rel_ref_pt.x;
1453				orig.y += rel_ref_pt.y;
1454			}
1455#ifndef PARSE_PATH_ONLY
1456			gf_path_add_move_to(path, orig.x, orig.y);
1457#endif
1458			rel_ref_pt = orig;
1459			prev_m_pt = orig;
1460			/*provision for nextCurveTo when no curve is specified:
1461				"If there is no previous command or if the previous command was not an C, c, S or s,
1462				assume the first control point is coincident with the current point.
1463			*/
1464			ct_orig = orig;
1465			prev_c = c;
1466			break;
1467		case 'L':
1468		case 'l':
1469			i++;
1470			read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
1471            if (!read_chars) return;
1472            i += read_chars;
1473			read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
1474            if (!read_chars) return;
1475            i += read_chars;
1476			if (c == 'l') {
1477				orig.x += rel_ref_pt.x;
1478				orig.y += rel_ref_pt.y;
1479			}
1480#ifndef PARSE_PATH_ONLY
1481			gf_path_add_line_to(path, orig.x, orig.y);
1482#endif
1483			rel_ref_pt = orig;
1484			orig = end;
1485			/*cf above*/
1486			ct_orig = orig;
1487			prev_c = c;
1488			break;
1489		case 'H':
1490		case 'h':
1491			i++;
1492			read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
1493            if (!read_chars) return;
1494            i += read_chars;
1495			if (c == 'h') {
1496				orig.x += rel_ref_pt.x;
1497			}
1498			orig.y = rel_ref_pt.y;
1499#ifndef PARSE_PATH_ONLY
1500			gf_path_add_line_to(path, orig.x, orig.y);
1501#endif
1502			rel_ref_pt.x = orig.x;
1503			orig = end;
1504			/*cf above*/
1505			ct_orig = orig;
1506			prev_c = c;
1507			break;
1508		case 'V':
1509		case 'v':
1510			i++;
1511			read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
1512            if (!read_chars) return;
1513            i += read_chars;
1514			if (c == 'v') {
1515				orig.y += rel_ref_pt.y;
1516			}
1517			orig.x = rel_ref_pt.x;
1518#ifndef PARSE_PATH_ONLY
1519			gf_path_add_line_to(path, orig.x, orig.y);
1520#endif
1521			rel_ref_pt.y = orig.y;
1522			orig = end;
1523			/*cf above*/
1524			ct_orig = orig;
1525			prev_c = c;
1526			break;
1527		case 'C':
1528		case 'c':
1529			i++;
1530			read_chars = svg_parse_number(&(d[i]), &(ct_orig.x), 0);
1531            if (!read_chars) return;
1532            i += read_chars;
1533			read_chars = svg_parse_number(&(d[i]), &(ct_orig.y), 0);
1534            if (!read_chars) return;
1535            i += read_chars;
1536			if (c == 'c') {
1537				ct_orig.x += rel_ref_pt.x;
1538				ct_orig.y += rel_ref_pt.y;
1539			}
1540			read_chars = svg_parse_number(&(d[i]), &(ct_end.x), 0);
1541            if (!read_chars) return;
1542            i += read_chars;
1543			read_chars = svg_parse_number(&(d[i]), &(ct_end.y), 0);
1544            if (!read_chars) return;
1545            i += read_chars;
1546			if (c == 'c') {
1547				ct_end.x += rel_ref_pt.x;
1548				ct_end.y += rel_ref_pt.y;
1549			}
1550			read_chars = svg_parse_number(&(d[i]), &(end.x), 0);
1551            if (!read_chars) return;
1552            i += read_chars;
1553			read_chars = svg_parse_number(&(d[i]), &(end.y), 0);
1554            if (!read_chars) return;
1555            i += read_chars;
1556			if (c == 'c') {
1557				end.x += rel_ref_pt.x;
1558				end.y += rel_ref_pt.y;
1559			}
1560#ifndef PARSE_PATH_ONLY
1561			gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y);
1562#endif
1563			rel_ref_pt = end;
1564			ct_orig = ct_end;
1565			orig = end;
1566			prev_c = c;
1567			break;
1568		case 'S':
1569		case 's':
1570			i++;
1571			ct_orig.x = 2*orig.x - ct_orig.x;
1572			ct_orig.y = 2*orig.y - ct_orig.y;
1573			read_chars = svg_parse_number(&(d[i]), &(ct_end.x), 0);
1574            if (!read_chars) return;
1575            i += read_chars;
1576			read_chars = svg_parse_number(&(d[i]), &(ct_end.y), 0);
1577            if (!read_chars) return;
1578            i += read_chars;
1579			if (c == 's') {
1580				ct_end.x += rel_ref_pt.x;
1581				ct_end.y += rel_ref_pt.y;
1582			}
1583			read_chars = svg_parse_number(&(d[i]), &(end.x), 0);
1584            if (!read_chars) return;
1585            i += read_chars;
1586			read_chars = svg_parse_number(&(d[i]), &(end.y), 0);
1587            if (!read_chars) return;
1588            i += read_chars;
1589			if (c == 's') {
1590				end.x += rel_ref_pt.x;
1591				end.y += rel_ref_pt.y;
1592			}
1593#ifndef PARSE_PATH_ONLY
1594			gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y);
1595#endif
1596			rel_ref_pt = end;
1597			ct_orig = ct_end;
1598			orig = end;
1599			prev_c = c;
1600			break;
1601		case 'Q':
1602		case 'q':
1603			i++;
1604			read_chars = svg_parse_number(&(d[i]), &(ct_orig.x), 0);
1605            if (!read_chars) return;
1606            i += read_chars;
1607			read_chars = svg_parse_number(&(d[i]), &(ct_orig.y), 0);
1608            if (!read_chars) return;
1609            i += read_chars;
1610			if (c == 'q') {
1611				ct_orig.x += rel_ref_pt.x;
1612				ct_orig.y += rel_ref_pt.y;
1613			}
1614			read_chars = svg_parse_number(&(d[i]), &(end.x), 0);
1615            if (!read_chars) return;
1616            i += read_chars;
1617			read_chars = svg_parse_number(&(d[i]), &(end.y), 0);
1618            if (!read_chars) return;
1619            i += read_chars;
1620			if (c == 'q') {
1621				end.x += rel_ref_pt.x;
1622				end.y += rel_ref_pt.y;
1623			}
1624#ifndef PARSE_PATH_ONLY
1625			gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y);
1626#endif
1627			rel_ref_pt = end;
1628			orig = end;
1629			prev_c = c;
1630			break;
1631		case 'T':
1632		case 't':
1633			i++;
1634			ct_orig.x = 2*orig.x - ct_orig.x;
1635			ct_orig.y = 2*orig.y - ct_orig.y;
1636			read_chars = svg_parse_number(&(d[i]), &(end.x), 0);
1637            if (!read_chars) return;
1638            i += read_chars;
1639			read_chars = svg_parse_number(&(d[i]), &(end.y), 0);
1640            if (!read_chars) return;
1641            i += read_chars;
1642			if (c == 't') {
1643				end.x += rel_ref_pt.x;
1644				end.y += rel_ref_pt.y;
1645			}
1646#ifndef PARSE_PATH_ONLY
1647			gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y);
1648#endif
1649			rel_ref_pt = end;
1650			orig = end;
1651			prev_c = c;
1652			break;
1653		case 'A':
1654		case 'a':
1655			i++;
1656
1657			read_chars = svg_parse_number(&(d[i]), &(orig.x), 0);
1658            if (!read_chars) return;
1659            i += read_chars;
1660			read_chars = svg_parse_number(&(d[i]), &(orig.y), 0);
1661            if (!read_chars) return;
1662            i += read_chars;
1663
1664			read_chars = svg_parse_number(&(d[i]), &(x_axis_rotation), 0);
1665            if (!read_chars) return;
1666            i += read_chars;
1667			read_chars = svg_parse_number(&(d[i]), &(large_arc_flag), 0);
1668            if (!read_chars) return;
1669            i += read_chars;
1670			read_chars = svg_parse_number(&(d[i]), &(sweep_flag), 0);
1671            if (!read_chars) return;
1672            i += read_chars;
1673
1674			read_chars = svg_parse_number(&(d[i]), &(end.x), 0);
1675            if (!read_chars) return;
1676            i += read_chars;
1677			read_chars = svg_parse_number(&(d[i]), &(end.y), 0);
1678            if (!read_chars) return;
1679            i += read_chars;
1680			if (c == 'a') {
1681				end.x += rel_ref_pt.x;
1682				end.y += rel_ref_pt.y;
1683			}
1684#ifndef PARSE_PATH_ONLY
1685			gf_path_add_svg_arc_to(path, end.x, end.y, orig.x, orig.y, x_axis_rotation , (large_arc_flag == FIX_ONE ? 1 : 0), (sweep_flag == FIX_ONE ? 1 : 0));
1686#endif
1687			rel_ref_pt = end;
1688			ct_orig = end;
1689			prev_c = c;
1690			break;
1691		case 'Z':
1692		case 'z':
1693			i++;
1694#ifndef PARSE_PATH_ONLY
1695			gf_path_close(path);
1696#endif
1697			prev_c = c;
1698			rel_ref_pt = prev_m_pt;
1699			break;
1700		default:
1701			i--;
1702			switch (prev_c) {
1703			case 'M':
1704				c = 'L';
1705				break;
1706			case 'm':
1707				c = 'l';
1708				break;
1709			default:
1710				c = prev_c;
1711			}
1712			goto next_command;
1713		}
1714	}
1715}
1716#else
1717/* TODO: Change the function to handle elliptical arcs, requires changing data structure */
1718static void svg_parse_path(SVG_PathData *d_attribute, char *attribute_content)
1719{
1720	GF_List *d_commands = d_attribute->commands;
1721	GF_List *d_points = d_attribute->points;
1722	char *d = attribute_content;
1723
1724	if (strlen(d)) {
1725		SVG_Point *pt, cur_pt, prev_m_pt;
1726		u8 *command;
1727		u32 i, k;
1728		char c, prev_c = 'M';
1729#ifdef REMOVE_ALLOC
1730		GF_SAFEALLOC(pt, SVG_Point)
1731#endif
1732		i = 0;
1733		cur_pt.x = cur_pt.y = 0;
1734		prev_m_pt.x = prev_m_pt.y = 0;
1735		while(1) {
1736			while ( (d[i]==' ') || (d[i] =='\t') ) i++;
1737			c = d[i];
1738			if (! c) break;
1739next_command:
1740			switch (c) {
1741			case 'm':
1742			case 'M':
1743				i++;
1744#ifndef REMOVE_ALLOC
1745				GF_SAFEALLOC(command, u8)
1746				gf_list_add(d_commands, command);
1747				*command = SVG_PATHCOMMAND_M;
1748
1749				GF_SAFEALLOC(pt, SVG_Point)
1750				gf_list_add(d_points, pt);
1751#endif
1752				i += svg_parse_number(&(d[i]), &(pt->x), 0);
1753				i += svg_parse_number(&(d[i]), &(pt->y), 0);
1754				if (c == 'm') {
1755					pt->x += cur_pt.x;
1756					pt->y += cur_pt.y;
1757				}
1758				cur_pt.x = pt->x;
1759				cur_pt.y = pt->y;
1760				prev_m_pt = cur_pt;
1761				prev_c = c;
1762				break;
1763			case 'L':
1764			case 'l':
1765				i++;
1766#ifndef REMOVE_ALLOC
1767				GF_SAFEALLOC(command, u8)
1768				gf_list_add(d_commands, command);
1769				*command = SVG_PATHCOMMAND_L;
1770
1771				GF_SAFEALLOC(pt, SVG_Point)
1772				gf_list_add(d_points, pt);
1773#endif
1774				i += svg_parse_number(&(d[i]), &(pt->x), 0);
1775				i += svg_parse_number(&(d[i]), &(pt->y), 0);
1776				if (c == 'l') {
1777					pt->x += cur_pt.x;
1778					pt->y += cur_pt.y;
1779				}
1780				cur_pt.x = pt->x;
1781				cur_pt.y = pt->y;
1782				prev_c = c;
1783				break;
1784			case 'H':
1785			case 'h':
1786				i++;
1787#ifndef REMOVE_ALLOC
1788				GF_SAFEALLOC(command, u8)
1789				gf_list_add(d_commands, command);
1790				*command = SVG_PATHCOMMAND_L;
1791
1792				GF_SAFEALLOC(pt, SVG_Point)
1793				gf_list_add(d_points, pt);
1794#endif
1795				i += svg_parse_number(&(d[i]), &(pt->x), 0);
1796				if (c == 'h') {
1797					pt->x += cur_pt.x;
1798				}
1799				pt->y = cur_pt.y;
1800				cur_pt.x = pt->x;
1801				prev_c = c;
1802				break;
1803			case 'V':
1804			case 'v':
1805				i++;
1806#ifndef REMOVE_ALLOC
1807				GF_SAFEALLOC(command, u8)
1808				gf_list_add(d_commands, command);
1809				*command = SVG_PATHCOMMAND_L;
1810
1811				GF_SAFEALLOC(pt, SVG_Point)
1812				gf_list_add(d_points, pt);
1813#endif
1814				i += svg_parse_number(&(d[i]), &(pt->y), 0);
1815				if (c == 'v') {
1816					pt->y += cur_pt.y;
1817				}
1818				pt->x = cur_pt.x;
1819				cur_pt.y = pt->y;
1820				prev_c = c;
1821				break;
1822			case 'C':
1823			case 'c':
1824				i++;
1825#ifndef REMOVE_ALLOC
1826				GF_SAFEALLOC(command, u8)
1827				gf_list_add(d_commands, command);
1828				*command = SVG_PATHCOMMAND_C;
1829#endif
1830
1831				for (k=0; k<3; k++) {
1832#ifndef REMOVE_ALLOC
1833					GF_SAFEALLOC(pt, SVG_Point)
1834					gf_list_add(d_points, pt);
1835#endif
1836					i += svg_parse_number(&(d[i]), &(pt->x), 0);
1837					i += svg_parse_number(&(d[i]), &(pt->y), 0);
1838					if (c == 'c') {
1839						pt->x += cur_pt.x;
1840						pt->y += cur_pt.y;
1841					}
1842				}
1843				cur_pt.x = pt->x;
1844				cur_pt.y = pt->y;
1845				prev_c = c;
1846				break;
1847			case 'S':
1848			case 's':
1849				i++;
1850#ifndef REMOVE_ALLOC
1851				GF_SAFEALLOC(command, u8)
1852				gf_list_add(d_commands, command);
1853				*command = SVG_PATHCOMMAND_S;
1854#endif
1855
1856				for (k=0; k<2; k++) {
1857#ifndef REMOVE_ALLOC
1858					GF_SAFEALLOC(pt, SVG_Point)
1859					gf_list_add(d_points, pt);
1860#endif
1861					i += svg_parse_number(&(d[i]), &(pt->x), 0);
1862					i += svg_parse_number(&(d[i]), &(pt->y), 0);
1863					if (c == 's') {
1864						pt->x += cur_pt.x;
1865						pt->y += cur_pt.y;
1866					}
1867				}
1868				cur_pt.x = pt->x;
1869				cur_pt.y = pt->y;
1870				prev_c = c;
1871				break;
1872			case 'Q':
1873			case 'q':
1874				i++;
1875#ifndef REMOVE_ALLOC
1876				GF_SAFEALLOC(command, u8)
1877				gf_list_add(d_commands, command);
1878				*command = SVG_PATHCOMMAND_Q;
1879#endif
1880
1881				for (k=0; k<2; k++) {
1882#ifndef REMOVE_ALLOC
1883					GF_SAFEALLOC(pt, SVG_Point)
1884					gf_list_add(d_points, pt);
1885#endif
1886					i += svg_parse_number(&(d[i]), &(pt->x), 0);
1887					i += svg_parse_number(&(d[i]), &(pt->y), 0);
1888					if (c == 'q') {
1889						pt->x += cur_pt.x;
1890						pt->y += cur_pt.y;
1891					}
1892				}
1893				cur_pt.x = pt->x;
1894				cur_pt.y = pt->y;
1895				prev_c = c;
1896				break;
1897			case 'T':
1898			case 't':
1899				i++;
1900#ifndef REMOVE_ALLOC
1901				GF_SAFEALLOC(command, u8)
1902				gf_list_add(d_commands, command);
1903				*command = SVG_PATHCOMMAND_T;
1904
1905				GF_SAFEALLOC(pt, SVG_Point)
1906				gf_list_add(d_points, pt);
1907#endif
1908				i += svg_parse_number(&(d[i]), &(pt->x), 0);
1909				i += svg_parse_number(&(d[i]), &(pt->y), 0);
1910				if (c == 't') {
1911					pt->x += cur_pt.x;
1912					pt->y += cur_pt.y;
1913				}
1914				cur_pt.x = pt->x;
1915				cur_pt.y = pt->y;
1916				prev_c = c;
1917				break;
1918			case 'A':
1919			case 'a':
1920				{
1921					Fixed tmp;
1922					i++;
1923#ifndef REMOVE_ALLOC
1924					GF_SAFEALLOC(command, u8)
1925					gf_list_add(d_commands, command);
1926					*command = SVG_PATHCOMMAND_A;
1927
1928					GF_SAFEALLOC(pt, SVG_Point)
1929					gf_list_add(d_points, pt);
1930#endif
1931					i += svg_parse_number(&(d[i]), &(pt->x), 0);
1932					i += svg_parse_number(&(d[i]), &(pt->y), 0);
1933
1934					i += svg_parse_number(&(d[i]), &(tmp), 0);
1935					i += svg_parse_number(&(d[i]), &(tmp), 0);
1936					i += svg_parse_number(&(d[i]), &(tmp), 0);
1937
1938#ifndef REMOVE_ALLOC
1939					GF_SAFEALLOC(pt, SVG_Point)
1940					gf_list_add(d_points, pt);
1941#endif
1942					i += svg_parse_number(&(d[i]), &(pt->x), 0);
1943					i += svg_parse_number(&(d[i]), &(pt->y), 0);
1944					if (c == 'a') {
1945						pt->x += cur_pt.x;
1946						pt->y += cur_pt.y;
1947					}
1948					cur_pt.x = pt->x;
1949					cur_pt.y = pt->y;
1950				}
1951				prev_c = c;
1952				break;
1953			case 'Z':
1954			case 'z':
1955				i++;
1956#ifndef REMOVE_ALLOC
1957				GF_SAFEALLOC(command, u8)
1958				gf_list_add(d_commands, command);
1959				*command = SVG_PATHCOMMAND_Z;
1960#endif
1961				prev_c = c;
1962				cur_pt = prev_m_pt;
1963				break;
1964			default:
1965				i--;
1966				switch (prev_c) {
1967				case 'M':
1968					c = 'L';
1969					break;
1970				case 'm':
1971					c = 'l';
1972					break;
1973				default:
1974					c = prev_c;
1975				}
1976				goto next_command;
1977			}
1978		}
1979	}
1980}
1981#endif
1982
1983static void svg_parse_iri(GF_Node *elt, XMLRI *iri, char *attribute_content)
1984{
1985	if (iri->string) {
1986		gf_free(iri->string);
1987		iri->string = NULL;
1988	}
1989	/* TODO: Handle xpointer(id()) syntax */
1990	if (attribute_content[0] == '#') {
1991		iri->string = gf_strdup(attribute_content);
1992		iri->target = gf_sg_find_node_by_name(elt->sgprivate->scenegraph, attribute_content + 1);
1993		if (!iri->target) {
1994			iri->type = XMLRI_STRING;
1995		} else {
1996			iri->type = XMLRI_ELEMENTID;
1997			gf_node_register_iri(elt->sgprivate->scenegraph, iri);
1998		}
1999	} else {
2000		iri->type = XMLRI_STRING;
2001		iri->string = gf_strdup(attribute_content);
2002	}
2003}
2004
2005static void svg_parse_idref(GF_Node *elt, XML_IDREF *iri, char *attribute_content)
2006{
2007	iri->type = XMLRI_ELEMENTID;
2008	iri->target = gf_sg_find_node_by_name(elt->sgprivate->scenegraph, attribute_content);
2009	if (!iri->target) {
2010		iri->string = gf_strdup(attribute_content);
2011	} else {
2012		gf_node_register_iri(elt->sgprivate->scenegraph, iri);
2013	}
2014}
2015
2016/* Parses a paint attribute: none, inherit or color */
2017static void svg_parse_paint(GF_Node *n, SVG_Paint *paint, char *attribute_content)
2018{
2019	if (!strcmp(attribute_content, "none")) {
2020		paint->type = SVG_PAINT_NONE;
2021	} else if (!strcmp(attribute_content, "inherit")) {
2022		paint->type = SVG_PAINT_INHERIT;
2023	} else if (!strncmp(attribute_content, "url(", 4) ) {
2024		char *ext = strrchr(attribute_content, ')');
2025		paint->type = SVG_PAINT_URI;
2026		if (ext) ext[0] = 0;
2027		svg_parse_iri(n, &paint->iri, attribute_content+4);
2028		if (ext) ext[0] = ')';
2029	} else {
2030		paint->type = SVG_PAINT_COLOR;
2031		svg_parse_color(&paint->color, attribute_content);
2032	}
2033}
2034
2035/* Parses a length which is a number with a unit */
2036static u32 svg_parse_length(SVG_Number *number, char *value_string, Bool clamp0to1)
2037{
2038    char c = '\0';
2039    char *unit = NULL;
2040    u32 len = 0;
2041    u32 unit_pos = 0;
2042    u32 unit_len = 0;
2043    u32 read_chars;
2044
2045	if (!strcmp(value_string, "inherit")) {
2046		number->type = SVG_NUMBER_INHERIT;
2047		return 7;
2048	} else if (!strcmp(value_string, "auto")) {
2049		number->type = SVG_NUMBER_AUTO;
2050		return 4;
2051	} else if (!strcmp(value_string, "auto-reverse")) {
2052		number->type = SVG_NUMBER_AUTO_REVERSE;
2053		return 12;
2054	} else if ((unit = strstr(value_string, "%")) ) {
2055		number->type = SVG_NUMBER_PERCENTAGE;
2056        unit_len = 1;
2057	} else if ((unit = strstr(value_string, "em"))) {
2058		number->type = SVG_NUMBER_EMS;
2059	} else if ((unit = strstr(value_string, "ex"))) {
2060		number->type = SVG_NUMBER_EXS;
2061	} else if ((unit = strstr(value_string, "px"))) {
2062		number->type = SVG_NUMBER_PX;
2063	} else if ((unit = strstr(value_string, "cm"))) {
2064		number->type = SVG_NUMBER_CM;
2065	} else if ((unit = strstr(value_string, "mm"))) {
2066		number->type = SVG_NUMBER_MM;
2067	} else if ((unit = strstr(value_string, "in"))) {
2068		number->type = SVG_NUMBER_IN;
2069	} else if ((unit = strstr(value_string, "pt"))) {
2070		number->type = SVG_NUMBER_PT;
2071	} else if ((unit = strstr(value_string, "pc"))) {
2072		number->type = SVG_NUMBER_PC;
2073	} else {
2074		number->type = SVG_NUMBER_VALUE;
2075	}
2076    if (unit) {
2077        if (!unit_len) unit_len = 2;
2078        unit_pos = unit - value_string;
2079        /* setting the first unit character to 0 for the svg_parse_number method to finish */
2080        c = value_string[unit_pos];
2081        value_string[unit_pos] = 0;
2082    }
2083	read_chars = svg_parse_number(value_string, &(number->value), 0);
2084    if (unit) {
2085        value_string[unit_pos] = c;
2086    }
2087    if (!read_chars) {
2088        GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error in parsing: %s\n", value_string));
2089        len = 0;
2090    } else {
2091        len = unit_len + read_chars;
2092    }
2093
2094	if (clamp0to1) number->value = MAX(0, MIN(1, number->value));
2095	return len;
2096}
2097
2098static void svg_parse_visibility(SVG_Visibility *value, char *value_string)
2099{
2100	if (!strcmp(value_string, "inherit")) {
2101		*value = SVG_VISIBILITY_INHERIT;
2102	} else if (!strcmp(value_string, "visible")) {
2103		*value = SVG_VISIBILITY_VISIBLE;
2104	} else if (!strcmp(value_string, "hidden")) {
2105		*value = SVG_VISIBILITY_HIDDEN;
2106	} else if (!strcmp(value_string, "collapse")) {
2107		*value = SVG_VISIBILITY_COLLAPSE;
2108	}
2109}
2110
2111static void svg_parse_display(SVG_Display *value, char *value_string)
2112{
2113	if (!strcmp(value_string, "inherit")) {
2114		*value = SVG_DISPLAY_INHERIT;
2115	} else if (!strcmp(value_string, "none")) {
2116		*value = SVG_DISPLAY_NONE;
2117	} else if (!strcmp(value_string, "inline")) {
2118		*value = SVG_DISPLAY_INLINE;
2119	} else if (!strcmp(value_string, "block")) {
2120		*value = SVG_DISPLAY_BLOCK;
2121	} else if (!strcmp(value_string, "list-item")) {
2122		*value = SVG_DISPLAY_LIST_ITEM;
2123	} else if (!strcmp(value_string, "run-in")) {
2124		*value = SVG_DISPLAY_RUN_IN;
2125	} else if (!strcmp(value_string, "compact")) {
2126		*value = SVG_DISPLAY_COMPACT;
2127	} else if (!strcmp(value_string, "marker")) {
2128		*value = SVG_DISPLAY_MARKER;
2129	} else if (!strcmp(value_string, "table")) {
2130		*value = SVG_DISPLAY_TABLE;
2131	} else if (!strcmp(value_string, "inline-table")) {
2132		*value = SVG_DISPLAY_INLINE_TABLE;
2133	} else if (!strcmp(value_string, "table-row-group")) {
2134		*value = SVG_DISPLAY_TABLE_ROW_GROUP;
2135	} else if (!strcmp(value_string, "table-header-group")) {
2136		*value = SVG_DISPLAY_TABLE_HEADER_GROUP;
2137	} else if (!strcmp(value_string, "table-footer-group")) {
2138		*value = SVG_DISPLAY_TABLE_FOOTER_GROUP;
2139	} else if (!strcmp(value_string, "table-row")) {
2140		*value = SVG_DISPLAY_TABLE_ROW;
2141	} else if (!strcmp(value_string, "table-column-group")) {
2142		*value = SVG_DISPLAY_TABLE_COLUMN_GROUP;
2143	} else if (!strcmp(value_string, "table-column")) {
2144		*value = SVG_DISPLAY_TABLE_COLUMN;
2145	} else if (!strcmp(value_string, "table-cell")) {
2146		*value = SVG_DISPLAY_TABLE_CELL;
2147	} else if (!strcmp(value_string, "table-caption")) {
2148		*value = SVG_DISPLAY_TABLE_CAPTION;
2149	}
2150}
2151
2152static void svg_parse_displayalign(SVG_DisplayAlign *value, char *value_string)
2153{
2154	if (!strcmp(value_string, "inherit")) {
2155		*value = SVG_DISPLAYALIGN_INHERIT;
2156	} else if (!strcmp(value_string, "auto")) {
2157		*value = SVG_DISPLAYALIGN_AUTO;
2158	} else if (!strcmp(value_string, "before")) {
2159		*value = SVG_DISPLAYALIGN_BEFORE;
2160	} else if (!strcmp(value_string, "center")) {
2161		*value = SVG_DISPLAYALIGN_CENTER;
2162	} else if (!strcmp(value_string, "after")) {
2163		*value = SVG_DISPLAYALIGN_AFTER;
2164	}
2165}
2166
2167static void svg_parse_textalign(SVG_TextAlign *value, char *value_string)
2168{
2169	if (!strcmp(value_string, "inherit")) {
2170		*value = SVG_TEXTALIGN_INHERIT;
2171	} else if (!strcmp(value_string, "start")) {
2172		*value = SVG_TEXTALIGN_START;
2173	} else if (!strcmp(value_string, "center")) {
2174		*value = SVG_TEXTALIGN_CENTER;
2175	} else if (!strcmp(value_string, "end")) {
2176		*value = SVG_TEXTALIGN_END;
2177	}
2178}
2179
2180static void svg_parse_pointerevents(SVG_PointerEvents *value, char *value_string)
2181{
2182	if (!strcmp(value_string, "inherit")) {
2183		*value = SVG_POINTEREVENTS_INHERIT;
2184	} else if (!strcmp(value_string, "visiblePainted")) {
2185		*value = SVG_POINTEREVENTS_VISIBLEPAINTED;
2186	} else if (!strcmp(value_string, "visibleFill")) {
2187		*value = SVG_POINTEREVENTS_VISIBLEFILL;
2188	} else if (!strcmp(value_string, "visibleStroke")) {
2189		*value = SVG_POINTEREVENTS_VISIBLESTROKE;
2190	} else if (!strcmp(value_string, "visible")) {
2191		*value = SVG_POINTEREVENTS_VISIBLE;
2192	} else if (!strcmp(value_string, "painted")) {
2193		*value = SVG_POINTEREVENTS_PAINTED;
2194	} else if (!strcmp(value_string, "fill")) {
2195		*value = SVG_POINTEREVENTS_FILL;
2196	} else if (!strcmp(value_string, "stroke")) {
2197		*value = SVG_POINTEREVENTS_STROKE;
2198	} else if (!strcmp(value_string, "all")) {
2199		*value = SVG_POINTEREVENTS_ALL;
2200	} else if (!strcmp(value_string, "boundingBox")) {
2201		*value = SVG_POINTEREVENTS_BOUNDINGBOX;
2202	} else if (!strcmp(value_string, "none")) {
2203		*value = SVG_POINTEREVENTS_NONE;
2204	}
2205}
2206
2207static void svg_parse_renderinghint(SVG_RenderingHint *value, char *value_string)
2208{
2209	if (!strcmp(value_string, "inherit")) {
2210		*value = SVG_RENDERINGHINT_INHERIT;
2211	} else if (!strcmp(value_string, "auto")) {
2212		*value = SVG_RENDERINGHINT_AUTO;
2213	} else if (!strcmp(value_string, "optimizeQuality")) {
2214		*value = SVG_RENDERINGHINT_OPTIMIZEQUALITY;
2215	} else if (!strcmp(value_string, "optimizeSpeed")) {
2216		*value = SVG_RENDERINGHINT_OPTIMIZESPEED;
2217	} else if (!strcmp(value_string, "optimizeLegibility")) {
2218		*value = SVG_RENDERINGHINT_OPTIMIZELEGIBILITY;
2219	} else if (!strcmp(value_string, "crispEdges")) {
2220		*value = SVG_RENDERINGHINT_CRISPEDGES;
2221	} else if (!strcmp(value_string, "geometricPrecision")) {
2222		*value = SVG_RENDERINGHINT_GEOMETRICPRECISION;
2223	}
2224}
2225
2226static void svg_parse_vectoreffect(SVG_VectorEffect *value, char *value_string)
2227{
2228	if (!strcmp(value_string, "inherit")) {
2229		*value = SVG_VECTOREFFECT_INHERIT;
2230	} else if (!strcmp(value_string, "none")) {
2231		*value = SVG_VECTOREFFECT_NONE;
2232	} else if (!strcmp(value_string, "non-scaling-stroke")) {
2233		*value = SVG_VECTOREFFECT_NONSCALINGSTROKE;
2234	}
2235}
2236
2237static void svg_parse_playbackorder(SVG_VectorEffect *value, char *value_string)
2238{
2239	if (!strcmp(value_string, "forwardOnly")) {
2240		*value = SVG_PLAYBACKORDER_FORWARDONLY;
2241	} else if (!strcmp(value_string, "all")) {
2242		*value = SVG_PLAYBACKORDER_ALL;
2243	}
2244}
2245
2246static void svg_parse_timelinebegin(SVG_TimelineBegin *value, char *value_string)
2247{
2248	if (!strcmp(value_string, "onStart")) {
2249		*value = SVG_TIMELINEBEGIN_ONSTART;
2250	} else if (!strcmp(value_string, "onLoad")) {
2251		*value = SVG_TIMELINEBEGIN_ONLOAD;
2252	}
2253}
2254
2255static void svg_parse_xmlspace(XML_Space *value, char *value_string)
2256{
2257	if (!strcmp(value_string, "default")) {
2258		*value = XML_SPACE_DEFAULT;
2259	} else if (!strcmp(value_string, "preserve")) {
2260		*value = XML_SPACE_PRESERVE;
2261	}
2262}
2263
2264static void svg_parse_xmlev_propagate(XMLEV_Propagate *value, char *value_string)
2265{
2266	if (!strcmp(value_string, "continue")) {
2267		*value = XMLEVENT_PROPAGATE_CONTINUE;
2268	} else if (!strcmp(value_string, "stop")) {
2269		*value = XMLEVENT_PROPAGATE_STOP;
2270	}
2271}
2272
2273static void svg_parse_xmlev_defaultAction(XMLEV_DefaultAction *value, char *value_string)
2274{
2275	if (!strcmp(value_string, "cancel")) {
2276		*value = XMLEVENT_DEFAULTACTION_CANCEL;
2277	} else if (!strcmp(value_string, "perform")) {
2278		*value = XMLEVENT_DEFAULTACTION_PERFORM;
2279	}
2280}
2281
2282static void svg_parse_xmlev_phase(XMLEV_Phase *value, char *value_string)
2283{
2284	if (!strcmp(value_string, "default")) {
2285		*value = XMLEVENT_PHASE_DEFAULT;
2286	} else if (!strcmp(value_string, "capture")) {
2287		*value = XMLEVENT_PHASE_CAPTURE;
2288	}
2289}
2290
2291static void svg_parse_overflow(SVG_Overflow *value, char *value_string)
2292{
2293	if (!strcmp(value_string, "inherit")) {
2294		*value = SVG_OVERFLOW_INHERIT;
2295	} else if (!strcmp(value_string, "visible")) {
2296		*value = SVG_OVERFLOW_VISIBLE;
2297	} else if (!strcmp(value_string, "hidden")) {
2298		*value = SVG_OVERFLOW_HIDDEN;
2299	} else if (!strcmp(value_string, "scroll")) {
2300		*value = SVG_OVERFLOW_SCROLL;
2301	} else if (!strcmp(value_string, "auto")) {
2302		*value = SVG_OVERFLOW_AUTO;
2303	}
2304}
2305
2306static void svg_parse_textanchor(SVG_TextAnchor *value, char *value_string)
2307{
2308	if (!strcmp(value_string, "inherit")) {
2309		*value = SVG_TEXTANCHOR_INHERIT;
2310	} else if (!strcmp(value_string, "start")) {
2311		*value = SVG_TEXTANCHOR_START;
2312	} else if (!strcmp(value_string, "middle")) {
2313		*value = SVG_TEXTANCHOR_MIDDLE;
2314	} else if (!strcmp(value_string, "end")) {
2315		*value = SVG_TEXTANCHOR_END;
2316	}
2317}
2318
2319static void svg_parse_clipfillrule(SVG_FillRule *value, char *value_string)
2320{
2321	if (!strcmp(value_string, "inherit")) {
2322		*value = SVG_FILLRULE_INHERIT;
2323	} else if (!strcmp(value_string, "nonzero")) {
2324		*value = SVG_FILLRULE_NONZERO;
2325	} else if (!strcmp(value_string, "evenodd")) {
2326		*value = SVG_FILLRULE_EVENODD;
2327	}
2328}
2329
2330static void svg_parse_strokelinejoin(SVG_StrokeLineJoin *value, char *value_string)
2331{
2332	if (!strcmp(value_string, "inherit")) {
2333		*value = SVG_STROKELINEJOIN_INHERIT;
2334	} else if (!strcmp(value_string, "miter")) {
2335		*value = SVG_STROKELINEJOIN_MITER;
2336	} else if (!strcmp(value_string, "round")) {
2337		*value = SVG_STROKELINEJOIN_ROUND;
2338	} else if (!strcmp(value_string, "bevel")) {
2339		*value = SVG_STROKELINEJOIN_BEVEL;
2340	}
2341}
2342
2343static void svg_parse_strokelinecap(SVG_StrokeLineCap *value, char *value_string)
2344{
2345	if (!strcmp(value_string, "inherit")) {
2346		*value = SVG_STROKELINECAP_INHERIT;
2347	} else if (!strcmp(value_string, "butt")) {
2348		*value = SVG_STROKELINECAP_BUTT;
2349	} else if (!strcmp(value_string, "round")) {
2350		*value = SVG_STROKELINECAP_ROUND;
2351	} else if (!strcmp(value_string, "square")) {
2352		*value = SVG_STROKELINECAP_SQUARE;
2353	}
2354}
2355
2356static void svg_parse_fontfamily(SVG_FontFamily *value, char *value_string)
2357{
2358	if (!strcmp(value_string, "inherit")) {
2359		value->type = SVG_FONTFAMILY_INHERIT;
2360	} else {
2361		value->type = SVG_FONTFAMILY_VALUE;
2362		value->value = gf_strdup(value_string);
2363	}
2364}
2365
2366static void svg_parse_fontstyle(SVG_FontStyle *value, char *value_string)
2367{
2368	if (!strcmp(value_string, "inherit")) {
2369		*value = SVG_FONTSTYLE_INHERIT;
2370	} else if (!strcmp(value_string, "normal")) {
2371		*value = SVG_FONTSTYLE_NORMAL;
2372	} else if (!strcmp(value_string, "italic")) {
2373		*value = SVG_FONTSTYLE_ITALIC;
2374	} else if (!strcmp(value_string, "oblique")) {
2375		*value = SVG_FONTSTYLE_OBLIQUE;
2376	}
2377}
2378
2379static void svg_parse_fontweight(SVG_FontWeight *value, char *value_string)
2380{
2381	if (!strcmp(value_string, "inherit")) {
2382		*value = SVG_FONTWEIGHT_INHERIT;
2383	} else if (!strcmp(value_string, "normal")) {
2384		*value = SVG_FONTWEIGHT_NORMAL;
2385	} else if (!strcmp(value_string, "bold")) {
2386		*value = SVG_FONTWEIGHT_BOLD;
2387	} else if (!strcmp(value_string, "bolder")) {
2388		*value = SVG_FONTWEIGHT_BOLDER;
2389	} else if (!strcmp(value_string, "lighter")) {
2390		*value = SVG_FONTWEIGHT_LIGHTER;
2391	} else if (!strcmp(value_string, "100")) {
2392		*value = SVG_FONTWEIGHT_100;
2393	} else if (!strcmp(value_string, "200")) {
2394		*value = SVG_FONTWEIGHT_200;
2395	} else if (!strcmp(value_string, "300")) {
2396		*value = SVG_FONTWEIGHT_300;
2397	} else if (!strcmp(value_string, "400")) {
2398		*value = SVG_FONTWEIGHT_400;
2399	} else if (!strcmp(value_string, "500")) {
2400		*value = SVG_FONTWEIGHT_500;
2401	} else if (!strcmp(value_string, "600")) {
2402		*value = SVG_FONTWEIGHT_600;
2403	} else if (!strcmp(value_string, "700")) {
2404		*value = SVG_FONTWEIGHT_700;
2405	} else if (!strcmp(value_string, "800")) {
2406		*value = SVG_FONTWEIGHT_800;
2407	} else if (!strcmp(value_string, "900")) {
2408		*value = SVG_FONTWEIGHT_900;
2409	}
2410}
2411
2412static void svg_parse_fontvariant(SVG_FontVariant *value, char *value_string)
2413{
2414	if (!strcmp(value_string, "inherit")) {
2415		*value = SVG_FONTVARIANT_INHERIT;
2416	} else if (!strcmp(value_string, "normal")) {
2417		*value = SVG_FONTVARIANT_NORMAL;
2418	} else if (!strcmp(value_string, "small-caps")) {
2419		*value = SVG_FONTVARIANT_SMALLCAPS;
2420	}
2421}
2422
2423static void svg_parse_boolean(SVG_Boolean *value, char *value_string)
2424{
2425	/*simple for text editable*/
2426	if (!strcmp(value_string, "1") || !strcmp(value_string, "true") || !strcmp(value_string, "simple"))
2427		*value = 1;
2428	else
2429		*value = 0;
2430}
2431
2432
2433static void smil_parse_time_list(GF_Node *e, GF_List *values, char *begin_or_end_list)
2434{
2435	SMIL_Time *value;
2436	char value_string[500];
2437	char *str = begin_or_end_list, *tmp;
2438	u32 len;
2439
2440	/* get rid of leading spaces */
2441	while (*str == ' ') str++;
2442
2443	while (1) {
2444		tmp = strchr(str, ';');
2445		if (tmp) len = tmp-str;
2446		else len = strlen(str);
2447		memcpy(value_string, str, len);
2448		while (value_string[len - 1] == ' ' && len > 0) len--;
2449		value_string[len] = 0;
2450
2451		GF_SAFEALLOC(value, SMIL_Time)
2452 		gf_list_add(values, value);
2453
2454		if (smil_parse_time(e, value, value_string) != GF_OK) goto err;
2455
2456		if (!tmp) break;
2457
2458		str = tmp + 1;
2459		while (*str == ' ') str++;
2460	}
2461
2462	/* sorting timing values */
2463	if (gf_list_count(values) > 1) {
2464		SMIL_Time *v, *sv;
2465		GF_List *sorted = gf_list_new();
2466		u32 i, count;
2467		u8 added = 0;
2468		do {
2469			v = (SMIL_Time*)gf_list_get(values, 0);
2470			gf_list_rem(values, 0);
2471			added = 0;
2472			count = gf_list_count(sorted);
2473			for (i=0; i<count; i++) {
2474				sv = (SMIL_Time*)gf_list_get(sorted, i);
2475				if (v->type >= GF_SMIL_TIME_EVENT) {
2476					/* unresolved or indefinite so add at the end of the sorted list */
2477					gf_list_add(sorted, v);
2478					added = 1;
2479					break;
2480				} else {
2481					if (sv->type >= GF_SMIL_TIME_EVENT) {
2482						gf_list_insert(sorted, v, i);
2483						added = 1;
2484						break;
2485					} else {
2486						if (v->clock <= sv->clock) {
2487							gf_list_insert(sorted, v, i);
2488							added = 1;
2489							break;
2490						}
2491					}
2492				}
2493			}
2494			if (!added) gf_list_add(sorted, v);
2495		} while (gf_list_count(values) > 0);
2496
2497		count = gf_list_count(sorted);
2498		for (i = 0; i < count; i++) {
2499			gf_list_add(values, gf_list_get(sorted, i));
2500		}
2501		gf_list_del(sorted);
2502	}
2503	return;
2504
2505err:
2506	/* See SVG spec:
2507	"If the 'begin' attribute is
2508	syntactically invalid, in the list itself or in any of the individual
2509	list values, it is equivalent to a single 'begin' value of 'indefinite'."*/
2510	len = gf_list_count(values);
2511	while (len) {
2512		SMIL_Time *v = (SMIL_Time*)gf_list_get(values, 0);
2513		if (v->element_id) gf_free(v->element_id);
2514		gf_list_rem(values, 0);
2515		gf_free(v);
2516		len--;
2517	}
2518
2519	GF_SAFEALLOC(value, SMIL_Time)
2520	gf_list_add(values, value);
2521
2522	switch (e->sgprivate->tag) {
2523	case TAG_SVG_discard:
2524		value->type = GF_SMIL_TIME_CLOCK;
2525		value->clock = 0;
2526		break;
2527	default:
2528		value->type = GF_SMIL_TIME_INDEFINITE;
2529		break;
2530	}
2531	return;
2532}
2533
2534static void smil_parse_attributeType(SMIL_AttributeType *value, char *value_string)
2535{
2536	if (!strcmp(value_string, "auto")) {
2537		*value = SMIL_ATTRIBUTETYPE_AUTO;
2538	} else if (!strcmp(value_string, "XML")) {
2539		*value = SMIL_ATTRIBUTETYPE_XML;
2540	} else if (!strcmp(value_string, "CSS")) {
2541		*value = SMIL_ATTRIBUTETYPE_CSS;
2542	}
2543}
2544
2545static void smil_parse_min_max_dur_repeatdur(SMIL_Duration *value, char *value_string)
2546{
2547	if (!strcmp(value_string, "indefinite")) {
2548		value->type = SMIL_DURATION_INDEFINITE;
2549	} else if (!strcmp(value_string, "media")) {
2550		value->type = SMIL_DURATION_MEDIA;
2551	} else {
2552		Double ftime;
2553		if ((svg_parse_clock_value(value_string, &ftime) == GF_OK) && (ftime >= 0)) {
2554			value->clock_value = ftime;
2555			value->type = SMIL_DURATION_DEFINED;
2556		} else {
2557			/* WARNING: Should this attribute in error be removed ? */
2558			value->type = SMIL_DURATION_INDEFINITE;
2559		}
2560	}
2561}
2562
2563static void smil_parse_repeatcount(SMIL_RepeatCount *value, char *value_string)
2564{
2565	if (!strcmp(value_string, "indefinite")) {
2566		value->type = SMIL_REPEATCOUNT_INDEFINITE;
2567	} else {
2568		Float _val;
2569		sscanf(value_string, "%f", &_val);
2570		value->type = SMIL_REPEATCOUNT_DEFINED;
2571		value->count = FLT2FIX(_val);
2572	}
2573}
2574
2575static void smil_parse_fill(SMIL_Fill *value, char *value_string)
2576{
2577	if (!strcmp(value_string, "freeze")) {
2578		*value = SMIL_FILL_FREEZE;
2579	} else if (!strcmp(value_string, "remove")) {
2580		*value = SMIL_FILL_REMOVE;
2581	}
2582}
2583
2584static void smil_parse_restart(SMIL_Restart *value, char *value_string)
2585{
2586	if (!strcmp(value_string, "always")) {
2587		*value = SMIL_RESTART_ALWAYS;
2588	} else if (!strcmp(value_string, "whenNotActive")) {
2589		*value = SMIL_RESTART_WHENNOTACTIVE;
2590	} else if (!strcmp(value_string, "never")) {
2591		*value = SMIL_RESTART_NEVER;
2592	}
2593}
2594
2595static void smil_parse_calcmode(SMIL_CalcMode *value, char *value_string)
2596{
2597	if (!strcmp(value_string, "discrete")) {
2598		*value = SMIL_CALCMODE_DISCRETE;
2599	} else if (!strcmp(value_string, "linear")) {
2600		*value = SMIL_CALCMODE_LINEAR;
2601	} else if (!strcmp(value_string, "paced")) {
2602		*value = SMIL_CALCMODE_PACED;
2603	} else if (!strcmp(value_string, "spline")) {
2604		*value = SMIL_CALCMODE_SPLINE;
2605	}
2606}
2607
2608static void smil_parse_additive(SMIL_Additive *value, char *value_string)
2609{
2610	if (!strcmp(value_string, "replace")) {
2611		*value = SMIL_ADDITIVE_REPLACE;
2612	} else if (!strcmp(value_string, "sum")) {
2613		*value = SMIL_ADDITIVE_SUM;
2614	}
2615}
2616
2617static void smil_parse_accumulate(SMIL_Accumulate *value, char *value_string)
2618{
2619	if (!strcmp(value_string, "none")) {
2620		*value = SMIL_ACCUMULATE_NONE;
2621	} else if (!strcmp(value_string, "sum")) {
2622		*value = SMIL_ACCUMULATE_SUM;
2623	}
2624}
2625
2626static void smil_parse_syncBehaviorOrDefault(SMIL_SyncBehavior *value, char *value_string)
2627{
2628	if (!strcmp(value_string, "inherit")) {
2629		*value = SMIL_SYNCBEHAVIOR_INHERIT;
2630	} else if (!strcmp(value_string, "default")) {
2631		*value = SMIL_SYNCBEHAVIOR_DEFAULT;
2632	} else if (!strcmp(value_string, "locked")) {
2633		*value = SMIL_SYNCBEHAVIOR_LOCKED;
2634	} else if (!strcmp(value_string, "canSlip")) {
2635		*value = SMIL_SYNCBEHAVIOR_CANSLIP;
2636	} else if (!strcmp(value_string, "independent")) {
2637		*value = SMIL_SYNCBEHAVIOR_INDEPENDENT;
2638	}
2639}
2640
2641static void smil_parse_syncToleranceOrDefault(SMIL_SyncTolerance *value, char *value_string)
2642{
2643	if (!strcmp(value_string, "inherit")) {
2644		value->type = SMIL_SYNCTOLERANCE_INHERIT;
2645	} else if (!strcmp(value_string, "default")) {
2646		value->type = SMIL_SYNCTOLERANCE_DEFAULT;
2647	} else {
2648		value->type = SMIL_SYNCBEHAVIOR_LOCKED;
2649		svg_parse_clock_value(value_string, &(value->value));
2650	}
2651}
2652
2653static void svg_parse_viewbox(SVG_ViewBox *value, char *value_string)
2654{
2655    u32 read_chars;
2656	char *str = value_string;
2657	if (!strcmp(str, "none")) {
2658		value->is_set = 0;
2659	} else {
2660		u32 i = 0;
2661		value->is_set = 1;
2662		read_chars = svg_parse_number(&(str[i]), &(value->x), 0);
2663        if (!read_chars) return;
2664        i += read_chars;
2665		read_chars = svg_parse_number(&(str[i]), &(value->y), 0);
2666        if (!read_chars) return;
2667        i += read_chars;
2668		read_chars = svg_parse_number(&(str[i]), &(value->width), 0);
2669        if (!read_chars) return;
2670        i += read_chars;
2671		read_chars = svg_parse_number(&(str[i]), &(value->height), 0);
2672        if (!read_chars) return;
2673        i += read_chars;
2674	}
2675}
2676
2677/* Parses a list of coordinates or a list of lengths (in SVG, length and coordinate is the same type )*/
2678static void svg_parse_coordinates(GF_List *values, char *value_string)
2679{
2680	SVG_Coordinate *c;
2681	u32 i = 0;
2682	char *str = value_string;
2683	u32 len = strlen(str);
2684
2685	while (gf_list_count(values)) {
2686		c = (SVG_Coordinate*)gf_list_get(values, 0);
2687		gf_list_rem(values, 0);
2688		gf_free(c);
2689	}
2690	while (i < len) {
2691		u32 sub;
2692		GF_SAFEALLOC(c, SVG_Coordinate)
2693		sub = svg_parse_length(c, &(str[i]), 0);
2694		if (!sub) {
2695			gf_free(c);
2696			return;
2697		}
2698		i+=sub;
2699		gf_list_add(values, c);
2700	}
2701}
2702
2703/* Parse a point as a pair of number without units */
2704u32 svg_parse_point(SVG_Point *p, char *value_string)
2705{
2706	u32 i = 0, j = 0;
2707	i = svg_parse_number(&(value_string[i]), &(p->x), 0);
2708    /* TODO: handle cases where a point has an invalid syntax */
2709	j = svg_parse_number(&(value_string[i]), &(p->y), 0);
2710	/* we need to detect an odd number of coordinates in polygon points list
2711	   cf. http://www.w3.org/TR/SVGMobile12/shapes.html#PolygonElement
2712	   see svg_parse_points */
2713	if (j == 0) return 0;
2714	else return i+j;
2715}
2716
2717static u32 svg_parse_point_into_matrix(GF_Matrix2D *p, char *value_string)
2718{
2719	u32 i = 0, j = 0;
2720	gf_mx2d_init(*p);
2721	i = svg_parse_number(&(value_string[i]), &(p->m[2]), 0);
2722    if (i == 0) return 0;
2723	j = svg_parse_number(&(value_string[i]), &(p->m[5]), 0);
2724    if (j == 0) return 0;
2725	return i+j;
2726}
2727
2728/* Parses the points attribute of a polygon or polyline element */
2729static void svg_parse_points(GF_List *values, char *value_string)
2730{
2731	u32 i = 0, j;
2732	char *str = value_string;
2733	u32 len = strlen(str);
2734	while (i < len) {
2735		SVG_Point *p;
2736		GF_SAFEALLOC(p, SVG_Point)
2737		j = svg_parse_point(p, &str[i]);
2738		if (j == 0) {
2739			/* cf. http://www.w3.org/TR/SVGMobile12/shapes.html#PolygonElement
2740			   If an odd number of coordinates is provided, then the element
2741			   is treated as if the attribute had not been specified.*/
2742			while (gf_list_count(values)) {
2743				p = (SVG_Point *)gf_list_get(values, 0);
2744				gf_free(p);
2745				gf_list_rem(values, 0);
2746			}
2747			return;
2748		}
2749		i += j;
2750		gf_list_add(values, p);
2751	}
2752}
2753
2754/* Parses a list of numbers */
2755static void svg_parse_numbers(GF_List *values, char *value_string, Bool is_angle)
2756{
2757    u32 read_chars;
2758	u32 i = 0;
2759	char *str = value_string;
2760	u32 len = strlen(str);
2761	while (i < len) {
2762		Fixed *f;
2763		GF_SAFEALLOC(f, Fixed)
2764		read_chars = svg_parse_number(&(str[i]), f, is_angle);
2765		if (!read_chars) {
2766			gf_free(f);
2767			return;
2768		}
2769        i += read_chars;
2770		gf_list_add(values, f);
2771	}
2772}
2773
2774static void svg_string_list_add(GF_List *values, char *string, u32 string_type)
2775{
2776	XMLRI *iri;
2777	switch (string_type) {
2778	case 1:
2779		iri = (XMLRI*)gf_malloc(sizeof(XMLRI));
2780		iri->type = XMLRI_STRING;
2781		iri->string = gf_strdup(string);
2782		gf_list_add(values, iri);
2783		break;
2784	default:
2785		gf_list_add(values, gf_strdup(string));
2786		break;
2787	}
2788}
2789
2790static void svg_parse_strings(GF_List *values, char *value_string, u32 string_type)
2791{
2792	char *next, *sep = value_string;
2793
2794	while (gf_list_count(values)) {
2795		next = (char*)gf_list_last(values);
2796		gf_list_rem_last(values);
2797		gf_free(next);
2798	}
2799
2800	while (1) {
2801		while (sep && sep[0]==' ') sep++;
2802		if (!sep) break;
2803		next = sep+1;
2804		while (next[0]) {
2805			if (strchr(" ;,", next[0])) break;
2806			next++;
2807		}
2808		if (!next[0]) {
2809			svg_string_list_add(values, sep, string_type);
2810			break;
2811		}
2812		next[0]=0;
2813		svg_string_list_add(values, sep, string_type);
2814		next[0]=';';
2815		sep = next+1;
2816		while (strchr(" ,;", sep[0])) sep++;
2817	}
2818}
2819
2820static void svg_parse_strokedasharray(SVG_StrokeDashArray *value, char *value_string)
2821{
2822    u32 read_chars;
2823	if (!strcmp(value_string, "none")) {
2824		value->type = SVG_STROKEDASHARRAY_NONE;
2825	} else if (!strcmp(value_string, "inherit")) {
2826		value->type = SVG_STROKEDASHARRAY_INHERIT;
2827	} else {
2828		UnitArray *vals = &(value->array);
2829		GF_List *values = gf_list_new();
2830		u32 i = 0;
2831		u32 len = strlen(value_string);
2832		char *str = value_string;
2833		while (i < len) {
2834			SVG_Length *f;
2835			GF_SAFEALLOC(f, SVG_Length)
2836			read_chars = svg_parse_length(f, &(str[i]), 0);
2837            if (!read_chars) {
2838                gf_free(f);
2839                return;
2840            }
2841            i += read_chars;
2842			gf_list_add(values, f);
2843		}
2844		vals->count = gf_list_count(values);
2845		vals->units = (u8 *) gf_malloc(sizeof(u8)*vals->count);
2846		vals->vals = (Fixed *) gf_malloc(sizeof(Fixed)*vals->count);
2847		for (i = 0; i < vals->count; i++) {
2848			SVG_Length *f = (SVG_Length *)gf_list_get(values, i);
2849			vals->vals[i] = f->value;
2850            vals->units[i] = f->type;
2851			gf_free(f);
2852		}
2853		gf_list_del(values);
2854		value->type = SVG_STROKEDASHARRAY_ARRAY;
2855	}
2856}
2857
2858static void svg_parse_zoomandpan(SVG_ZoomAndPan *value, char *value_string)
2859{
2860	if (!strcmp(value_string, "disable")) {
2861		*value = SVG_ZOOMANDPAN_DISABLE;
2862	} else if (!strcmp(value_string, "magnify")) {
2863		*value = SVG_ZOOMANDPAN_MAGNIFY;
2864	}
2865}
2866
2867static void svg_parse_preserveaspectratio(SVG_PreserveAspectRatio *par, char *attribute_content)
2868{
2869	char *content = attribute_content;
2870	while (*content == ' ') content++;
2871	if (strstr(content, "defer")) {
2872		par->defer = 1;
2873		content += 4;
2874	} else {
2875		content = attribute_content;
2876	}
2877	while (*content == ' ') content++;
2878	if (strstr(content, "none")) {
2879		par->align = SVG_PRESERVEASPECTRATIO_NONE;
2880		content+=4;
2881	} else if (strstr(content, "xMinYMin")) {
2882		par->align = SVG_PRESERVEASPECTRATIO_XMINYMIN;
2883		content+=8;
2884	} else if (strstr(content, "xMidYMin")) {
2885		par->align = SVG_PRESERVEASPECTRATIO_XMIDYMIN;
2886		content+=8;
2887	} else if (strstr(content, "xMaxYMin")) {
2888		par->align = SVG_PRESERVEASPECTRATIO_XMAXYMIN;
2889		content+=8;
2890	} else if (strstr(content, "xMinYMid")) {
2891		par->align = SVG_PRESERVEASPECTRATIO_XMINYMID;
2892		content+=8;
2893	} else if (strstr(content, "xMidYMid")) {
2894		par->align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
2895		content+=8;
2896	} else if (strstr(content, "xMaxYMid")) {
2897		par->align = SVG_PRESERVEASPECTRATIO_XMAXYMID;
2898		content+=8;
2899	} else if (strstr(content, "xMinYMax")) {
2900		par->align = SVG_PRESERVEASPECTRATIO_XMINYMAX;
2901		content+=8;
2902	} else if (strstr(content, "xMidYMax")) {
2903		par->align = SVG_PRESERVEASPECTRATIO_XMIDYMAX;
2904		content+=8;
2905	} else if (strstr(content, "xMaxYMax")) {
2906		par->align = SVG_PRESERVEASPECTRATIO_XMAXYMAX;
2907		content+=8;
2908	}
2909	while (*content == ' ') content++;
2910	if (*content == 0) return;
2911	if (strstr(content, "meet")) {
2912		par->meetOrSlice = SVG_MEETORSLICE_MEET;
2913	} else if (strstr(content, "slice")) {
2914		par->meetOrSlice = SVG_MEETORSLICE_SLICE;
2915	}
2916}
2917
2918static void svg_parse_animatetransform_type(SVG_TransformType *anim_transform_type, char *attribute_content)
2919{
2920	*anim_transform_type = SVG_TRANSFORM_MATRIX;
2921	if (!strcmp(attribute_content, "scale")) {
2922		*anim_transform_type = SVG_TRANSFORM_SCALE;
2923	} else if (!strcmp(attribute_content, "rotate")) {
2924		*anim_transform_type = SVG_TRANSFORM_ROTATE;
2925	} else if (!strcmp(attribute_content, "translate")) {
2926		*anim_transform_type = SVG_TRANSFORM_TRANSLATE;
2927	} else if (!strcmp(attribute_content, "skewX")) {
2928		*anim_transform_type = SVG_TRANSFORM_SKEWX;
2929	} else if (!strcmp(attribute_content, "skewY")) {
2930		*anim_transform_type = SVG_TRANSFORM_SKEWY;
2931	}
2932}
2933
2934static void svg_parse_focushighlight(SVG_FocusHighlight *fh, char *attribute_content)
2935{
2936	if (!strcmp(attribute_content, "auto")) {
2937		*fh = SVG_FOCUSHIGHLIGHT_AUTO;
2938	} else if (!strcmp(attribute_content, "none")) {
2939		*fh = SVG_FOCUSHIGHLIGHT_NONE;
2940	}
2941}
2942
2943static void svg_parse_focusable(SVG_Focusable *f, char *attribute_content)
2944{
2945	if (!strcmp(attribute_content, "true")) {
2946		*f = SVG_FOCUSABLE_TRUE;
2947	} else if (!strcmp(attribute_content, "false")) {
2948		*f = SVG_FOCUSABLE_FALSE;
2949	} else {
2950		*f = SVG_FOCUSABLE_AUTO;
2951	}
2952}
2953
2954static void svg_parse_initialvisibility(SVG_InitialVisibility *iv, char *attribute_content)
2955{
2956	if (!strcmp(attribute_content, "whenStarted")) {
2957		*iv = SVG_INITIALVISIBILTY_WHENSTARTED;
2958	} else if (!strcmp(attribute_content, "always")) {
2959		*iv = SVG_INITIALVISIBILTY_ALWAYS;
2960	}
2961}
2962
2963static void svg_parse_overlay(SVG_Overlay *o, char *attribute_content)
2964{
2965	if (!strcmp(attribute_content, "none")) {
2966		*o = SVG_OVERLAY_NONE;
2967	} else if (!strcmp(attribute_content, "top")) {
2968		*o = SVG_OVERLAY_TOP;
2969	}
2970}
2971
2972static void svg_parse_transformbehavior(SVG_TransformBehavior *tb, char *attribute_content)
2973{
2974	if (!strcmp(attribute_content, "geometric")) {
2975		*tb = SVG_TRANSFORMBEHAVIOR_GEOMETRIC;
2976	} else if (!strcmp(attribute_content, "pinned")) {
2977		*tb = SVG_TRANSFORMBEHAVIOR_PINNED;
2978	} else if (!strcmp(attribute_content, "pinned90")) {
2979		*tb = SVG_TRANSFORMBEHAVIOR_PINNED90;
2980	} else if (!strcmp(attribute_content, "pinned180")) {
2981		*tb = SVG_TRANSFORMBEHAVIOR_PINNED180;
2982	} else if (!strcmp(attribute_content, "pinned270")) {
2983		*tb = SVG_TRANSFORMBEHAVIOR_PINNED270;
2984	}
2985}
2986
2987static void svg_parse_focus(GF_Node *e,  SVG_Focus *o, char *attribute_content)
2988{
2989	if (o->target.string) gf_free(o->target.string);
2990	o->target.string = NULL;
2991	o->target.target = NULL;
2992
2993	if (!strcmp(attribute_content, "self")) o->type = SVG_FOCUS_SELF;
2994	else if (!strcmp(attribute_content, "auto")) o->type = SVG_FOCUS_AUTO;
2995	else if (!strnicmp(attribute_content, "url(", 4)) {
2996		char *sep = strrchr(attribute_content, ')');
2997		if (sep) sep[0] = 0;
2998		o->type = SVG_FOCUS_IRI;
2999		svg_parse_iri(e, &o->target, attribute_content+4);
3000		if (sep) sep[0] = ')';
3001	}
3002}
3003
3004/* end of Basic SVG datatype parsing functions */
3005
3006void svg_parse_one_anim_value(GF_Node *n, SMIL_AnimateValue *anim_value, char *attribute_content, u8 anim_value_type)
3007{
3008	GF_FieldInfo info;
3009	info.fieldType = anim_value_type;
3010	info.far_ptr = gf_svg_create_attribute_value(anim_value_type);
3011	if (info.far_ptr) gf_svg_parse_attribute(n, &info, attribute_content, 0);
3012
3013	anim_value->value = info.far_ptr;
3014	anim_value->type = anim_value_type;
3015}
3016
3017void svg_parse_anim_values(GF_Node *n, SMIL_AnimateValues *anim_values, char *anim_values_string, u8 anim_value_type)
3018{
3019	u32 i = 0;
3020	char *str;
3021	s32 psemi = -1;
3022	GF_FieldInfo info;
3023	info.fieldType = anim_value_type;
3024	anim_values->type = anim_value_type;
3025
3026	str = anim_values_string;
3027	while (1) {
3028		if (str[i] == ';' || str[i] == 0) {
3029			u32 single_value_len = 0;
3030			char c;
3031			single_value_len = i - (psemi+1);
3032			c = str [ (psemi+1) + single_value_len];
3033			str [ (psemi+1) + single_value_len] = 0;
3034			info.far_ptr = gf_svg_create_attribute_value(anim_value_type);
3035			if (info.far_ptr) {
3036				gf_svg_parse_attribute(n, &info, str + (psemi+1), anim_value_type);
3037				gf_list_add(anim_values->values, info.far_ptr);
3038			}
3039			str [ (psemi+1) + single_value_len] = c;
3040			psemi = i;
3041			if (!str[i]) return;
3042		}
3043		i++;
3044	}
3045}
3046
3047GF_Err laser_parse_choice(LASeR_Choice *choice, char *attribute_content)
3048{
3049	if (!strcmp(attribute_content, "none")) {
3050		choice->type = LASeR_CHOICE_NONE;
3051	} else if (!strcmp(attribute_content, "all")) {
3052		choice->type = LASeR_CHOICE_ALL;
3053	} else {
3054		choice->type = LASeR_CHOICE_N;
3055		choice->choice_index = atoi(attribute_content);
3056	}
3057	return GF_OK;
3058}
3059
3060GF_Err laser_parse_size(LASeR_Size *size, char *attribute_content)
3061{
3062	char *str = attribute_content;
3063	u32 i = 0;
3064	i+=svg_parse_number(&(str[i]), &(size->width), 0);
3065	i+=svg_parse_number(&(str[i]), &(size->height), 0);
3066	return GF_OK;
3067}
3068
3069GF_Err gf_svg_parse_element_id(GF_Node *n, const char *nodename, Bool warning_if_defined)
3070{
3071	GF_SceneGraph *sg = gf_node_get_graph((GF_Node *)n);
3072	u32 id = gf_sg_get_max_node_id(sg) + 1;
3073	gf_node_set_id(n, id, nodename);
3074	return GF_OK;
3075}
3076
3077/* Parse an SVG attribute */
3078GF_EXPORT
3079GF_Err gf_svg_parse_attribute(GF_Node *n, GF_FieldInfo *info, char *attribute_content, u8 anim_value_type)
3080{
3081    /* for all attributes, except strings, apply some sort of white space normalization*/
3082	if (info->fieldType != DOM_String_datatype && strlen(attribute_content)) {
3083		u32 i, len;
3084		/*remove spaces at the begining*/
3085		while (attribute_content[0] && (strchr("\r\n\t ", attribute_content[0])))
3086			attribute_content++;
3087
3088		/*change all special chars in spaces*/
3089		i=0;
3090		len = strlen(attribute_content);
3091		while (i<len) {
3092			if (strchr("\r\n\t", attribute_content[i]))
3093				attribute_content[i] = ' ';
3094			i++;
3095		}
3096		/*remove spaces in the end*/
3097		while (len && attribute_content[len-1]==' ') {
3098			attribute_content[len-1] = 0;
3099			len--;
3100		}
3101	}
3102
3103	switch (info->fieldType) {
3104	case SVG_Boolean_datatype:
3105		svg_parse_boolean((SVG_Boolean *)info->far_ptr, attribute_content);
3106	    break;
3107	case SVG_Color_datatype:
3108		svg_parse_color((SVG_Color *)info->far_ptr, attribute_content);
3109	    break;
3110	case SVG_Paint_datatype:
3111		svg_parse_paint(n, (SVG_Paint *)info->far_ptr, attribute_content);
3112		break;
3113
3114/* beginning of keyword type parsing */
3115	case SVG_FillRule_datatype:
3116		svg_parse_clipfillrule((SVG_FillRule *)info->far_ptr, attribute_content);
3117		break;
3118	case SVG_StrokeLineJoin_datatype:
3119		svg_parse_strokelinejoin((SVG_StrokeLineJoin *)info->far_ptr, attribute_content);
3120		break;
3121	case SVG_StrokeLineCap_datatype:
3122		svg_parse_strokelinecap((SVG_StrokeLineCap *)info->far_ptr, attribute_content);
3123		break;
3124	case SVG_FontStyle_datatype:
3125		svg_parse_fontstyle((SVG_FontStyle *)info->far_ptr, attribute_content);
3126		break;
3127	case SVG_FontWeight_datatype:
3128		svg_parse_fontweight((SVG_FontWeight *)info->far_ptr, attribute_content);
3129		break;
3130	case SVG_FontVariant_datatype:
3131		svg_parse_fontvariant((SVG_FontVariant *)info->far_ptr, attribute_content);
3132		break;
3133	case SVG_TextAnchor_datatype:
3134		svg_parse_textanchor((SVG_TextAnchor *)info->far_ptr, attribute_content);
3135		break;
3136	case SVG_Display_datatype:
3137		svg_parse_display((SVG_Display *)info->far_ptr, attribute_content);
3138		break;
3139	case SVG_Visibility_datatype:
3140		svg_parse_visibility((SVG_Visibility *)info->far_ptr, attribute_content);
3141		break;
3142	case SVG_Overflow_datatype:
3143		svg_parse_overflow((SVG_Overflow *)info->far_ptr, attribute_content);
3144		break;
3145	case SVG_ZoomAndPan_datatype:
3146		svg_parse_zoomandpan((SVG_ZoomAndPan *)info->far_ptr, attribute_content);
3147		break;
3148	case SVG_DisplayAlign_datatype:
3149		svg_parse_displayalign((SVG_DisplayAlign *)info->far_ptr, attribute_content);
3150		break;
3151	case SVG_TextAlign_datatype:
3152		svg_parse_textalign((SVG_TextAlign *)info->far_ptr, attribute_content);
3153		break;
3154	case SVG_PointerEvents_datatype:
3155		svg_parse_pointerevents((SVG_PointerEvents *)info->far_ptr, attribute_content);
3156		break;
3157	case SVG_RenderingHint_datatype:
3158		svg_parse_renderinghint((SVG_RenderingHint *)info->far_ptr, attribute_content);
3159		break;
3160	case SVG_VectorEffect_datatype:
3161		svg_parse_vectoreffect((SVG_VectorEffect *)info->far_ptr, attribute_content);
3162		break;
3163	case SVG_PlaybackOrder_datatype:
3164		svg_parse_playbackorder((SVG_PlaybackOrder *)info->far_ptr, attribute_content);
3165		break;
3166	case SVG_TimelineBegin_datatype:
3167		svg_parse_timelinebegin((SVG_TimelineBegin *)info->far_ptr, attribute_content);
3168		break;
3169	case XML_Space_datatype:
3170		svg_parse_xmlspace((XML_Space *)info->far_ptr, attribute_content);
3171		break;
3172	case XMLEV_Propagate_datatype:
3173		svg_parse_xmlev_propagate((XMLEV_Propagate *)info->far_ptr, attribute_content);
3174		break;
3175	case XMLEV_DefaultAction_datatype:
3176		svg_parse_xmlev_defaultAction((XMLEV_DefaultAction *)info->far_ptr, attribute_content);
3177		break;
3178	case XMLEV_Phase_datatype:
3179		svg_parse_xmlev_phase((XMLEV_Phase *)info->far_ptr, attribute_content);
3180		break;
3181	case SMIL_SyncBehavior_datatype:
3182		smil_parse_syncBehaviorOrDefault((SMIL_SyncBehavior *)info->far_ptr, attribute_content);
3183		break;
3184	case SMIL_SyncTolerance_datatype:
3185		smil_parse_syncToleranceOrDefault((SMIL_SyncTolerance *)info->far_ptr, attribute_content);
3186		break;
3187	case SMIL_AttributeType_datatype:
3188		smil_parse_attributeType((SMIL_AttributeType *)info->far_ptr, attribute_content);
3189		break;
3190	case SMIL_CalcMode_datatype:
3191		smil_parse_calcmode((SMIL_CalcMode *)info->far_ptr, attribute_content);
3192		break;
3193	case SMIL_Additive_datatype:
3194		smil_parse_additive((SMIL_CalcMode *)info->far_ptr, attribute_content);
3195		break;
3196	case SMIL_Accumulate_datatype:
3197		smil_parse_accumulate((SMIL_Accumulate *)info->far_ptr, attribute_content);
3198		break;
3199	case SMIL_Restart_datatype:
3200		smil_parse_restart((SMIL_Restart *)info->far_ptr, attribute_content);
3201		break;
3202	case SMIL_Fill_datatype:
3203		smil_parse_fill((SMIL_Fill *)info->far_ptr, attribute_content);
3204		break;
3205	case SVG_GradientUnit_datatype:
3206		*((SVG_GradientUnit *)info->far_ptr) = !strcmp(attribute_content, "userSpaceOnUse") ? SVG_GRADIENTUNITS_USER : SVG_GRADIENTUNITS_OBJECT;
3207		break;
3208	case SVG_FocusHighlight_datatype:
3209		svg_parse_focushighlight((SVG_FocusHighlight*)info->far_ptr, attribute_content);
3210		break;
3211	case SVG_Focusable_datatype:
3212		svg_parse_focusable((SVG_Focusable*)info->far_ptr, attribute_content);
3213		break;
3214
3215	case SVG_InitialVisibility_datatype:
3216		svg_parse_initialvisibility((SVG_InitialVisibility*)info->far_ptr, attribute_content);
3217		break;
3218	case SVG_Overlay_datatype:
3219		svg_parse_overlay((SVG_Overlay*)info->far_ptr, attribute_content);
3220		break;
3221	case SVG_TransformBehavior_datatype:
3222		svg_parse_transformbehavior((SVG_TransformBehavior*)info->far_ptr, attribute_content);
3223		break;
3224	case SVG_SpreadMethod_datatype:
3225		if (!strcmp(attribute_content, "reflect")) *(u8*)info->far_ptr = SVG_SPREAD_REFLECT;
3226		else if (!strcmp(attribute_content, "repeat")) *(u8*)info->far_ptr = SVG_SPREAD_REPEAT;
3227		else *(u8*)info->far_ptr = SVG_SPREAD_PAD;
3228		break;
3229	case SVG_Filter_TransferType_datatype:
3230		if (!strcmp(attribute_content, "table")) *(u8*)info->far_ptr = SVG_FILTER_TRANSFER_TABLE;
3231		else if (!strcmp(attribute_content, "discrete")) *(u8*)info->far_ptr = SVG_FILTER_TRANSFER_DISCRETE;
3232		else if (!strcmp(attribute_content, "linear")) *(u8*)info->far_ptr = SVG_FILTER_TRANSFER_LINEAR;
3233		else if (!strcmp(attribute_content, "gamma")) *(u8*)info->far_ptr = SVG_FILTER_TRANSFER_GAMMA;
3234		else *(u8*)info->far_ptr = SVG_FILTER_TRANSFER_IDENTITY;
3235		break;
3236
3237/* end of keyword type parsing */
3238
3239	/* keyword | numbers (with possibly units) */
3240	case SVG_Length_datatype:
3241	case SVG_Coordinate_datatype:
3242	case SVG_FontSize_datatype:
3243	case SVG_Rotate_datatype:
3244    case SVG_Number_datatype:
3245		svg_parse_length((SVG_Number*)info->far_ptr, attribute_content, 0);
3246		break;
3247
3248	case SMIL_AnimateValue_datatype:
3249		svg_parse_one_anim_value(n, (SMIL_AnimateValue*)info->far_ptr, attribute_content, anim_value_type);
3250		break;
3251	case SMIL_AnimateValues_datatype:
3252		svg_parse_anim_values(n, (SMIL_AnimateValues*)info->far_ptr, attribute_content, anim_value_type);
3253		break;
3254
3255	case XMLRI_datatype:
3256		svg_parse_iri(n, (XMLRI*)info->far_ptr, attribute_content);
3257		break;
3258	case XML_IDREF_datatype:
3259		svg_parse_idref(n, (XMLRI*)info->far_ptr, attribute_content);
3260		break;
3261	case SMIL_AttributeName_datatype:
3262		((SMIL_AttributeName *)info->far_ptr)->name = gf_strdup(attribute_content);
3263		break;
3264	case SMIL_Times_datatype:
3265		smil_parse_time_list(n, *(GF_List **)info->far_ptr, attribute_content);
3266		break;
3267	case SMIL_Duration_datatype:
3268		smil_parse_min_max_dur_repeatdur((SMIL_Duration*)info->far_ptr, attribute_content);
3269		break;
3270	case SMIL_RepeatCount_datatype:
3271		smil_parse_repeatcount((SMIL_RepeatCount*)info->far_ptr, attribute_content);
3272		break;
3273	case SVG_PathData_datatype:
3274		svg_parse_path((SVG_PathData*)info->far_ptr, attribute_content);
3275		break;
3276	case SVG_Points_datatype:
3277		svg_parse_points(*(GF_List **)(info->far_ptr), attribute_content);
3278		break;
3279	case SMIL_KeyTimes_datatype:
3280	case SMIL_KeyPoints_datatype:
3281	case SMIL_KeySplines_datatype:
3282	case SVG_Numbers_datatype:
3283		svg_parse_numbers(*(GF_List **)(info->far_ptr), attribute_content, 0);
3284		break;
3285	case SVG_Coordinates_datatype:
3286		svg_parse_coordinates(*(GF_List **)(info->far_ptr), attribute_content);
3287		break;
3288	case SVG_ViewBox_datatype:
3289		svg_parse_viewbox((SVG_ViewBox*)info->far_ptr, attribute_content);
3290		break;
3291	case SVG_StrokeDashArray_datatype:
3292		svg_parse_strokedasharray((SVG_StrokeDashArray*)info->far_ptr, attribute_content);
3293		break;
3294	case SVG_FontFamily_datatype:
3295		svg_parse_fontfamily((SVG_FontFamily*)info->far_ptr, attribute_content);
3296		break;
3297	case SVG_Motion_datatype:
3298		svg_parse_point_into_matrix((GF_Matrix2D*)info->far_ptr, attribute_content);
3299		break;
3300	case SVG_Transform_datatype:
3301		svg_parse_transform((SVG_Transform*)info->far_ptr, attribute_content);
3302		break;
3303	case SVG_Transform_Translate_datatype:
3304		{
3305			u32 i = 0;
3306			SVG_Point *p = (SVG_Point *)info->far_ptr;;
3307			i+=svg_parse_number(&(attribute_content[i]), &(p->x), 0);
3308			if (attribute_content[i] == 0) {
3309				p->y = 0;
3310			} else {
3311				i+=svg_parse_number(&(attribute_content[i]), &(p->y), 0);
3312			}
3313		}
3314		break;
3315	case SVG_Transform_Scale_datatype:
3316		{
3317			u32 i = 0;
3318			SVG_Point *p = (SVG_Point *)info->far_ptr;;
3319			i+=svg_parse_number(&(attribute_content[i]), &(p->x), 0);
3320			if (attribute_content[i] == 0) {
3321				p->y = p->x;
3322			} else {
3323				i+=svg_parse_number(&(attribute_content[i]), &(p->y), 0);
3324			}
3325		}
3326		break;
3327	case SVG_Transform_SkewX_datatype:
3328	case SVG_Transform_SkewY_datatype:
3329		{
3330			Fixed *p = (Fixed *)info->far_ptr;
3331			svg_parse_number(attribute_content, p, 1);
3332		}
3333		break;
3334	case SVG_Transform_Rotate_datatype:
3335		{
3336			u32 i = 0;
3337			SVG_Point_Angle *p = (SVG_Point_Angle *)info->far_ptr;;
3338			i+=svg_parse_number(&(attribute_content[i]), &(p->angle), 1);
3339			if (attribute_content[i] == 0) {
3340				p->y = p->x = 0;
3341			} else {
3342				i+=svg_parse_number(&(attribute_content[i]), &(p->x), 0);
3343				i+=svg_parse_number(&(attribute_content[i]), &(p->y), 0);
3344			}
3345		}
3346		break;
3347	case SVG_PreserveAspectRatio_datatype:
3348		svg_parse_preserveaspectratio((SVG_PreserveAspectRatio*)info->far_ptr, attribute_content);
3349		break;
3350	case SVG_TransformType_datatype:
3351		svg_parse_animatetransform_type((SVG_TransformType*)info->far_ptr, attribute_content);
3352		break;
3353
3354	case SVG_ID_datatype:
3355	case DOM_String_datatype:
3356	case SVG_ContentType_datatype:
3357	case SVG_LanguageID_datatype:
3358		if (*(SVG_String *)info->far_ptr) gf_free(*(SVG_String *)info->far_ptr);
3359
3360		*(SVG_String *)info->far_ptr = gf_strdup(attribute_content);
3361		break;
3362
3363	case DOM_StringList_datatype:
3364		svg_parse_strings(*(GF_List **)info->far_ptr, attribute_content, 0);
3365		break;
3366	case XMLRI_List_datatype:
3367		svg_parse_strings(*(GF_List **)info->far_ptr, attribute_content, 1);
3368		break;
3369
3370	case XMLEV_Event_datatype:
3371	{
3372		XMLEV_Event *xml_ev = (XMLEV_Event *)info->far_ptr;
3373		char *sep = strchr(attribute_content, '(');
3374		if (sep) {
3375			sep[0] = 0;
3376			xml_ev->type = gf_dom_event_type_by_name(attribute_content);
3377			sep[0] = '(';
3378			if ((xml_ev->type == GF_EVENT_REPEAT) || (xml_ev->type == GF_EVENT_REPEAT_EVENT)) {
3379				char _v;
3380				sscanf(sep, "(%c)", &_v);
3381				xml_ev->parameter = _v;
3382			} else { /* key events ... */
3383				char *sep2 = strchr(attribute_content, ')');
3384				sep2[0] = 0;
3385				xml_ev->parameter = gf_dom_get_key_type(sep+1);
3386				sep2[0] = ')';
3387			}
3388		} else {
3389			xml_ev->parameter = 0;
3390			xml_ev->type = gf_dom_event_type_by_name(attribute_content);
3391		}
3392	}
3393		break;
3394
3395	case SVG_Focus_datatype:
3396		svg_parse_focus(n, (SVG_Focus*)info->far_ptr, attribute_content);
3397		break;
3398	case LASeR_Choice_datatype:
3399		laser_parse_choice((LASeR_Choice*)info->far_ptr, attribute_content);
3400		break;
3401	case LASeR_Size_datatype:
3402		laser_parse_size((LASeR_Size*)info->far_ptr, attribute_content);
3403		break;
3404	case SVG_Clock_datatype:
3405		svg_parse_clock_value(attribute_content, (SVG_Clock*)info->far_ptr);
3406		break;
3407	case SVG_Unknown_datatype:
3408		if (*(SVG_String *)info->far_ptr) gf_free(*(SVG_String *)info->far_ptr);
3409		*(SVG_String *)info->far_ptr = gf_strdup(attribute_content);
3410		break;
3411	default:
3412		GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Parsing] Cannot parse attribute %s\n", info->name, gf_svg_attribute_type_to_string(info->fieldType)));
3413		break;
3414	}
3415	return GF_OK;
3416}
3417
3418void svg_parse_one_style(GF_Node *n, char *one_style)
3419{
3420	GF_FieldInfo info;
3421	char *c, sep;
3422	u32 attributeNameLen;
3423
3424	while (*one_style == ' ') one_style++;
3425	c = strchr(one_style, ':');
3426	if (!c) return;
3427	attributeNameLen = (c - one_style);
3428	sep = one_style[attributeNameLen];
3429	one_style[attributeNameLen] = 0;
3430	while (strchr("\r\n\t ", one_style[0]))
3431		one_style++;
3432	if (!gf_node_get_field_by_name(n, one_style, &info)) {
3433		c++;
3434		gf_svg_parse_attribute(n, &info, c, 0);
3435	} else {
3436		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Attribute %s does not belong to element %s.\n", one_style, gf_node_get_class_name(n)));
3437	}
3438	one_style[attributeNameLen] = sep;
3439}
3440
3441void gf_svg_parse_style(GF_Node *n, char *style)
3442{
3443	u32 i = 0;
3444	char *str = style;
3445	s32 psemi = -1;
3446
3447	while (1) {
3448		if (str[i] == ';' || str[i] == 0) {
3449			u32 single_value_len = 0;
3450			single_value_len = i - (psemi+1);
3451			if (single_value_len) {
3452				char c = str[psemi+1 + single_value_len];
3453				str[psemi+1 + single_value_len] = 0;
3454				svg_parse_one_style(n, str + psemi+1);
3455				str[psemi+1 + single_value_len] = c;
3456				psemi = i;
3457			}
3458			if (!str[i]) return;
3459		}
3460		i++;
3461	}
3462
3463}
3464
3465GF_EXPORT
3466void *gf_svg_create_attribute_value(u32 attribute_type)
3467{
3468	switch (attribute_type) {
3469	case SVG_Boolean_datatype:
3470		{
3471			SVG_Boolean *b;
3472			GF_SAFEALLOC(b, SVG_Boolean)
3473			return b;
3474		}
3475		break;
3476	case SVG_Color_datatype:
3477		{
3478			SVG_Color *color;
3479			GF_SAFEALLOC(color, SVG_Color)
3480			return color;
3481		}
3482		break;
3483	case SVG_Paint_datatype:
3484		{
3485			SVG_Paint *paint;
3486			GF_SAFEALLOC(paint, SVG_Paint)
3487			return paint;
3488		}
3489		break;
3490
3491	/* keyword types */
3492	case SVG_FillRule_datatype:
3493	case SVG_StrokeLineJoin_datatype:
3494	case SVG_StrokeLineCap_datatype:
3495	case SVG_FontStyle_datatype:
3496	case SVG_FontWeight_datatype:
3497	case SVG_FontVariant_datatype:
3498	case SVG_TextAnchor_datatype:
3499	case SVG_Display_datatype:
3500	case SVG_Visibility_datatype:
3501	case SVG_Overflow_datatype:
3502	case SVG_ZoomAndPan_datatype:
3503	case SVG_DisplayAlign_datatype:
3504	case SVG_TextAlign_datatype:
3505	case SVG_PointerEvents_datatype:
3506	case SVG_RenderingHint_datatype:
3507	case SVG_VectorEffect_datatype:
3508	case SVG_PlaybackOrder_datatype:
3509	case SVG_TimelineBegin_datatype:
3510	case XML_Space_datatype:
3511	case XMLEV_Propagate_datatype:
3512	case XMLEV_DefaultAction_datatype:
3513	case XMLEV_Phase_datatype:
3514	case SMIL_SyncBehavior_datatype:
3515	case SMIL_AttributeType_datatype:
3516	case SMIL_CalcMode_datatype:
3517	case SMIL_Additive_datatype:
3518	case SMIL_Accumulate_datatype:
3519	case SMIL_Restart_datatype:
3520	case SMIL_Fill_datatype:
3521	case SVG_TransformType_datatype:
3522	case SVG_FocusHighlight_datatype:
3523	case SVG_InitialVisibility_datatype:
3524	case SVG_GradientUnit_datatype:
3525	case SVG_Overlay_datatype:
3526	case SVG_TransformBehavior_datatype:
3527	case SVG_SpreadMethod_datatype:
3528	case SVG_Focusable_datatype:
3529	case SVG_Filter_TransferType_datatype:
3530		{
3531			u8 *keyword;
3532			GF_SAFEALLOC(keyword, u8)
3533			return keyword;
3534		}
3535		break;
3536	case SMIL_SyncTolerance_datatype:
3537		{
3538			SMIL_SyncTolerance *st;
3539			GF_SAFEALLOC(st, SMIL_SyncTolerance)
3540			return st;
3541		}
3542		break;
3543
3544	/* inheritable floats */
3545	case SVG_FontSize_datatype:
3546	case SVG_Length_datatype:
3547	case SVG_Coordinate_datatype:
3548	case SVG_Rotate_datatype:
3549	case SVG_Number_datatype:
3550		{
3551			SVG_Number *number;
3552			GF_SAFEALLOC(number, SVG_Number)
3553			return number;
3554		}
3555		break;
3556
3557	case SVG_StrokeDashArray_datatype:
3558		{
3559			SVG_StrokeDashArray *array;
3560			GF_SAFEALLOC(array, SVG_StrokeDashArray)
3561			return array;
3562		}
3563		break;
3564
3565	case SVG_Motion_datatype:
3566		{
3567			GF_Matrix2D *p;
3568			GF_SAFEALLOC(p, GF_Matrix2D)
3569			gf_mx2d_init(*p);
3570			return p;
3571		}
3572		break;
3573
3574	case SVG_Transform_datatype:
3575		{
3576			SVG_Transform *p;
3577			GF_SAFEALLOC(p, SVG_Transform)
3578			gf_mx2d_init(p->mat);
3579			return p;
3580		}
3581		break;
3582
3583	case SVG_Transform_Translate_datatype:
3584	case SVG_Transform_Scale_datatype:
3585		{
3586			SVG_Point *p;
3587			GF_SAFEALLOC(p, SVG_Point)
3588			return p;
3589		}
3590		break;
3591
3592	case SVG_Transform_SkewX_datatype:
3593	case SVG_Transform_SkewY_datatype:
3594		{
3595			Fixed *p;
3596			GF_SAFEALLOC(p, Fixed)
3597			return p;
3598		}
3599		break;
3600
3601	case SVG_Transform_Rotate_datatype:
3602		{
3603			SVG_Point_Angle *p;
3604			GF_SAFEALLOC(p, SVG_Point_Angle)
3605			return p;
3606		}
3607		break;
3608
3609	case SVG_ViewBox_datatype:
3610		{
3611			SVG_ViewBox *viewbox;
3612			GF_SAFEALLOC(viewbox, SVG_ViewBox)
3613			return viewbox;
3614		}
3615		break;
3616	case XMLRI_datatype:
3617	case XML_IDREF_datatype:
3618		{
3619			XMLRI *iri;
3620			GF_SAFEALLOC(iri, XMLRI)
3621			return iri;
3622		}
3623		break;
3624	case SVG_FontFamily_datatype:
3625		{
3626			SVG_FontFamily *fontfamily;
3627			GF_SAFEALLOC(fontfamily, SVG_FontFamily)
3628			return fontfamily;
3629		}
3630		break;
3631	case DOM_String_datatype:
3632	case SVG_ContentType_datatype:
3633	case SVG_LanguageID_datatype:
3634	case SVG_ID_datatype:
3635		{
3636			SVG_String *string;
3637			GF_SAFEALLOC(string, SVG_String)
3638			return string;
3639		}
3640		break;
3641	case DOM_StringList_datatype:
3642	case XMLRI_List_datatype:
3643	case SVG_Points_datatype:
3644	case SVG_Coordinates_datatype:
3645	case SMIL_Times_datatype:
3646	case SMIL_KeySplines_datatype:
3647	case SMIL_KeyTimes_datatype:
3648	case SMIL_KeyPoints_datatype:
3649	case SVG_Numbers_datatype:
3650		{
3651			ListOfXXX *list;
3652			GF_SAFEALLOC(list, ListOfXXX)
3653			*list = gf_list_new();
3654			return list;
3655		}
3656		break;
3657	case SVG_PreserveAspectRatio_datatype:
3658		{
3659			SVG_PreserveAspectRatio *par;
3660			GF_SAFEALLOC(par, SVG_PreserveAspectRatio)
3661			return par;
3662		}
3663		break;
3664	case SVG_PathData_datatype:
3665		{
3666			SVG_PathData *path;
3667			GF_SAFEALLOC(path, SVG_PathData);
3668#if USE_GF_PATH
3669			gf_path_reset(path);
3670			path->fineness = FIX_ONE;
3671#else
3672			path->commands = gf_list_new();
3673			path->points = gf_list_new();
3674#endif
3675			return path;
3676		}
3677		break;
3678	case LASeR_Choice_datatype:
3679		{
3680			LASeR_Choice *ch;
3681			GF_SAFEALLOC(ch, LASeR_Choice)
3682			return ch;
3683		}
3684	case SVG_Focus_datatype:
3685		{
3686			SVG_Focus *foc;
3687			GF_SAFEALLOC(foc, SVG_Focus)
3688			return foc;
3689		}
3690	case SMIL_AttributeName_datatype:
3691		{
3692			SMIL_AttributeName *an;
3693			GF_SAFEALLOC(an, SMIL_AttributeName)
3694			return an;
3695		}
3696	case SMIL_RepeatCount_datatype:
3697		{
3698			SMIL_RepeatCount *rc;
3699			GF_SAFEALLOC(rc, SMIL_RepeatCount)
3700			return rc;
3701		}
3702	case SMIL_Duration_datatype:
3703		{
3704			SMIL_Duration *sd;
3705			GF_SAFEALLOC(sd, SMIL_Duration)
3706			return sd;
3707		}
3708	case SMIL_AnimateValue_datatype:
3709		{
3710			SMIL_AnimateValue *av;
3711			GF_SAFEALLOC(av, SMIL_AnimateValue)
3712			return av;
3713		}
3714		break;
3715	case SMIL_AnimateValues_datatype:
3716		{
3717			SMIL_AnimateValues *av;
3718			GF_SAFEALLOC(av, SMIL_AnimateValues)
3719			av->values = gf_list_new();
3720			return av;
3721		}
3722		break;
3723	case SVG_Clock_datatype:
3724		{
3725			SVG_Clock *ck;
3726			GF_SAFEALLOC(ck, SVG_Clock)
3727			return ck;
3728		}
3729		break;
3730
3731	case XMLEV_Event_datatype:
3732		{
3733			XMLEV_Event *e;
3734			GF_SAFEALLOC(e, XMLEV_Event);
3735			return e;
3736		}
3737		break;
3738	case LASeR_Size_datatype:
3739		{
3740			LASeR_Size *s;
3741			GF_SAFEALLOC(s, LASeR_Size);
3742			return s;
3743		}
3744		break;
3745
3746	case 0:
3747		{
3748			SVG_String *string;
3749			GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Attributes] Unspecified attribute type - defaulting to string.\n"));
3750			GF_SAFEALLOC(string, SVG_String);
3751			return string;
3752		}
3753
3754
3755	default:
3756		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Attributes] Cannot create attribute value: Type %s not supported.\n", gf_svg_attribute_type_to_string(attribute_type)));
3757		break;
3758	}
3759	return NULL;
3760}
3761
3762static char *svg_dump_color(SVG_Color *col)
3763{
3764	char *res;
3765	if (col->type == SVG_COLOR_CURRENTCOLOR) return gf_strdup("currentColor");
3766	else if (col->type == SVG_COLOR_INHERIT) return gf_strdup("inherit");
3767	else if (col->type !=SVG_COLOR_RGBCOLOR) {
3768		u32 i, count;
3769		count = sizeof(system_colors) / sizeof(struct sys_col);
3770		for (i=0; i<count; i++) {
3771			if (col->type == system_colors[i].type) {
3772				return gf_strdup(system_colors[i].name);
3773			}
3774		}
3775	} else {
3776		u32 i, count = sizeof(predefined_colors) / sizeof(struct predef_col);
3777		u32 r, g, b;
3778		r = FIX2INT(255*col->red);
3779		g = FIX2INT(255*col->green);
3780		b = FIX2INT(255*col->blue);
3781		for (i=0; i<count; i++) {
3782			if (
3783				(r == predefined_colors[i].r)
3784				&& (g == predefined_colors[i].g)
3785				&& (b == predefined_colors[i].b)
3786			) {
3787				return gf_strdup(predefined_colors[i].name);
3788			}
3789		}
3790		res = gf_malloc(sizeof(char)*8);
3791		sprintf(res, "#%02X%02X%02X", r, g, b);
3792		/*compress it...*/
3793		if ( (res[1]==res[2]) && (res[3]==res[4]) && (res[5]==res[6]) )
3794			sprintf(res, "#%c%c%c", res[1], res[3], res[5]);
3795		return res;
3796	}
3797	return NULL;
3798}
3799
3800static char *svg_dump_number(SVG_Number *l)
3801{
3802	char tmp[100];
3803	if (l->type==SVG_NUMBER_INHERIT) return gf_strdup("inherit");
3804	else if (l->type == SVG_NUMBER_AUTO) return gf_strdup("auto");
3805	else if (l->type == SVG_NUMBER_AUTO_REVERSE) return gf_strdup("auto-reverse");
3806	else {
3807		sprintf(tmp, "%g", FIX2FLT(l->value) );
3808		if (l->type == SVG_NUMBER_PERCENTAGE) strcat(tmp, "%");
3809		else if (l->type == SVG_NUMBER_EMS) strcat(tmp, "em");
3810		else if (l->type == SVG_NUMBER_EXS) strcat(tmp, "ex");
3811		else if (l->type == SVG_NUMBER_PX) strcat(tmp, "px");
3812		else if (l->type == SVG_NUMBER_CM) strcat(tmp, "cm");
3813		else if (l->type == SVG_NUMBER_MM) strcat(tmp, "mm");
3814		else if (l->type == SVG_NUMBER_IN) strcat(tmp, "in");
3815		else if (l->type == SVG_NUMBER_PT) strcat(tmp, "pt");
3816		else if (l->type == SVG_NUMBER_PC) strcat(tmp, "pc");
3817
3818		return gf_strdup(tmp);
3819	}
3820}
3821
3822static char *svg_dump_iri(XMLRI*iri)
3823{
3824	if (iri->type == XMLRI_ELEMENTID) {
3825		const char *name;
3826		char *res;
3827		name = gf_node_get_name((GF_Node *)iri->target);
3828
3829		if (name) {
3830			res = gf_malloc(sizeof(char)*(strlen(name)+2));
3831			sprintf(res, "#%s", name);
3832		} else if (iri->target) {
3833			res = gf_malloc(sizeof(char)*32);
3834			sprintf(res, "#N%d", gf_node_get_id((GF_Node *)iri->target) - 1);
3835		} else {
3836			res = gf_strdup("");
3837		}
3838		return res;
3839	}
3840	else if ((iri->type == XMLRI_STRING) && iri->string)
3841		return gf_strdup(iri->string);
3842	else
3843		return gf_strdup("");
3844}
3845
3846static char *svg_dump_idref(XMLRI*iri)
3847{
3848	const char *name;
3849	if (iri->target) {
3850		name = gf_node_get_name((GF_Node *)iri->target);
3851		if (name) return gf_strdup(name);
3852		else {
3853			char tmp[50];
3854			sprintf(tmp, "N%d", gf_node_get_id((GF_Node *)iri->target) - 1);
3855			return gf_strdup(tmp);
3856		}
3857	}
3858	if (iri->string) return gf_strdup(iri->string);
3859	return gf_strdup("");
3860}
3861
3862#if USE_GF_PATH
3863static char *svg_dump_path(SVG_PathData *path)
3864{
3865	char szT[1000];
3866	GF_Point2D *pt, last_pt, *ct1, *ct2, *end;
3867	u32 i, *contour;
3868	char *res = gf_malloc(sizeof(char));
3869	res[0] = 0;
3870
3871	contour = path->contours;
3872	last_pt.x = last_pt.y = 0;
3873
3874	for (i=0; i<path->n_points; ) {
3875		szT[0] = 0;
3876
3877		switch (path->tags[i]) {
3878		case GF_PATH_CURVE_ON:
3879		case GF_PATH_CLOSE:
3880			pt = &path->points[i];
3881			if (!i || (*contour == i-1) ) {
3882				sprintf(szT, "M%g %g", FIX2FLT(pt->x), FIX2FLT(pt->y));
3883				if (i) contour++;
3884			} else if (path->tags[i]==GF_PATH_CLOSE) {
3885				sprintf(szT, "z");
3886			} else {
3887				if (i && (last_pt.x==pt->x)) sprintf(szT, "V%g", FIX2FLT(pt->y));
3888				else if (i && (last_pt.y==pt->y)) sprintf(szT, "H%g", FIX2FLT(pt->x));
3889				else sprintf(szT, "L%g %g", FIX2FLT(pt->x), FIX2FLT(pt->y));
3890			}
3891			last_pt = *pt;
3892			i++;
3893			break;
3894		case GF_PATH_CURVE_CONIC:
3895			ct1 = &path->points[i];
3896			end = &path->points[i+1];
3897			sprintf(szT, "Q%g %g %g %g", FIX2FLT(ct1->x), FIX2FLT(ct1->y), FIX2FLT(end->x), FIX2FLT(end->y));
3898
3899			last_pt = *end;
3900			if (path->tags[i+2]==GF_PATH_CLOSE)  {
3901				strcat(szT, "z");
3902			}
3903			i+=2;
3904			break;
3905		case GF_PATH_CURVE_CUBIC:
3906			ct1 = &path->points[i];
3907			ct2 = &path->points[i+1];
3908			end = &path->points[i+2];
3909			sprintf(szT, "C%g %g %g %g %g %g", FIX2FLT(ct1->x), FIX2FLT(ct1->y), FIX2FLT(ct2->x), FIX2FLT(ct2->y), FIX2FLT(end->x), FIX2FLT(end->y));
3910			last_pt = *end;
3911			if (path->tags[i+2]==GF_PATH_CLOSE) {
3912				strcat(szT, "z");
3913			}
3914			i+=3;
3915			break;
3916		}
3917		if (szT[0]) {
3918			res = gf_realloc(res, sizeof(char)*(strlen(szT)+strlen(res)+1));
3919			strcat(res, szT);
3920		}
3921	}
3922	return res;
3923}
3924#else
3925static void svg_dump_point(SVG_Point *pt, char *attValue)
3926{
3927	if (pt) sprintf(attValue, "%g %g ", FIX2FLT(pt->x), FIX2FLT(pt->y) );
3928}
3929static void svg_dump_path(SVG_PathData *path, char *attValue)
3930{
3931	char szT[1000];
3932	u32 i, pt_i, count;
3933	count = gf_list_count(path->commands);
3934	pt_i = 0;
3935	strcpy(attValue, "");
3936	for (i = 0; i < count; i++) {
3937		u8 command = *(u8 *)gf_list_get(path->commands, i);
3938		switch(command) {
3939		case SVG_PATHCOMMAND_M:
3940			strcat(attValue, "M");
3941			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3942			strcat(attValue, szT);
3943			pt_i++;
3944			break;
3945		case SVG_PATHCOMMAND_L:
3946			strcat(attValue, "L");
3947			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3948			strcat(attValue, szT);
3949			pt_i++;
3950			break;
3951		case SVG_PATHCOMMAND_C:
3952			strcat(attValue, "C");
3953			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3954			strcat(attValue, szT);
3955			pt_i++;
3956			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3957			strcat(attValue, szT);
3958			pt_i++;
3959			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3960			strcat(attValue, szT);
3961			pt_i++;
3962			break;
3963		case SVG_PATHCOMMAND_S:
3964			strcat(attValue, "S");
3965			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3966			strcat(attValue, szT);
3967			pt_i++;
3968			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3969			strcat(attValue, szT);
3970			pt_i++;
3971			break;
3972		case SVG_PATHCOMMAND_Q:
3973			strcat(attValue, "Q");
3974			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3975			strcat(attValue, szT);
3976			pt_i++;
3977			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3978			strcat(attValue, szT);
3979			pt_i++;
3980			break;
3981		case SVG_PATHCOMMAND_T:
3982			strcat(attValue, "T");
3983			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3984			strcat(attValue, szT);
3985			pt_i++;
3986			break;
3987		case SVG_PATHCOMMAND_A:
3988			strcat(attValue, "A");
3989			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3990			strcat(attValue, szT);
3991			pt_i++;
3992			strcat(attValue, "0 0 0 ");
3993			svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT);
3994			strcat(attValue, szT);
3995			pt_i++;
3996			break;
3997		case SVG_PATHCOMMAND_Z:
3998			strcat(attValue, "Z");
3999			break;
4000		default:
4001			GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Dumping] unknown path command %d\n", command));
4002			break;
4003		}
4004	}
4005}
4006#endif
4007
4008static void svg_dump_access_key(XMLEV_Event *evt, char *attValue)
4009{
4010	u32 i, count;
4011	strcpy(attValue, "accessKey(");
4012	count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid);
4013	for (i=0; i<count; i++) {
4014		if (evt->parameter == predefined_key_identifiers[i].key_code) {
4015			strcat(attValue, predefined_key_identifiers[i].name);
4016			break;
4017		}
4018	}
4019	/* OLD LASeR CODE
4020	switch (evt->parameter) {
4021	case 0: strcat(attValue, "UP"); break;
4022	case 1: strcat(attValue, "DOWN"); break;
4023	case 2: strcat(attValue, "LEFT"); break;
4024	case 3: strcat(attValue, "RIGHT"); break;
4025	case 4: strcat(attValue, "FIRE"); break;
4026	case 5: strcat(attValue, "NO_KEY"); break;
4027	case 6: strcat(attValue, "ANY_KEY"); break;
4028	case 7: strcat(attValue, "SOFT_KEY_1"); break;
4029	case 8: strcat(attValue, "SOFT_KEY_2"); break;
4030	case 35: strcat(attValue, "#"); break;
4031	case 42: strcat(attValue, "*"); break;
4032	case 48: strcat(attValue, "0"); break;
4033	case 49: strcat(attValue, "1"); break;
4034	case 50: strcat(attValue, "2"); break;
4035	case 51: strcat(attValue, "3"); break;
4036	case 52: strcat(attValue, "4"); break;
4037	case 53: strcat(attValue, "5"); break;
4038	case 54: strcat(attValue, "6"); break;
4039	case 55: strcat(attValue, "7"); break;
4040	case 56: strcat(attValue, "8"); break;
4041	case 57: strcat(attValue, "9"); break;
4042	*/
4043	strcat(attValue, ")");
4044}
4045
4046static char *gf_svg_dump_matrix(GF_Matrix2D *matrix)
4047{
4048	char attValue[1024];
4049	attValue[0]=0;
4050	/*try to do a simple decomposition...*/
4051	if (!matrix->m[1] && !matrix->m[3]) {
4052		if (matrix->m[2] != 0 || matrix->m[5] != 0) sprintf(attValue, "translate(%g,%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]) );
4053		if ((matrix->m[0]!=FIX_ONE) || (matrix->m[4]!=FIX_ONE)) {
4054			char szT[1024];
4055			if ((matrix->m[0]==-FIX_ONE) && (matrix->m[4]==-FIX_ONE)) {
4056				strcpy(szT, " rotate(180)");
4057			} else {
4058				sprintf(szT, " scale(%g,%g)", FIX2FLT(matrix->m[0]), FIX2FLT(matrix->m[4]) );
4059			}
4060			strcat(attValue, szT);
4061		}
4062	} else if (matrix->m[1] == - matrix->m[3]) {
4063		Fixed angle = gf_asin(matrix->m[3]);
4064		Fixed cos_a = gf_cos(angle);
4065		if (ABS(cos_a)>FIX_EPSILON) {
4066			Fixed sx, sy;
4067			sx = gf_divfix(matrix->m[0], cos_a);
4068			sy = gf_divfix(matrix->m[4], cos_a);
4069			angle = gf_divfix(180*angle, GF_PI);
4070			if ((sx==sy) && ( ABS(FIX_ONE - ABS(sx) ) < FIX_ONE/100)) {
4071				if (matrix->m[2] != 0 || matrix->m[5] != 0)
4072					sprintf(attValue, "translate(%g,%g) rotate(%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]), FIX2FLT(gf_divfix(angle*180, GF_PI) ) );
4073				else
4074					sprintf(attValue, "rotate(%g)", FIX2FLT(gf_divfix(angle*180, GF_PI) ) );
4075			} else {
4076				if (matrix->m[2] != 0 || matrix->m[5] != 0)
4077					sprintf(attValue, "translate(%g,%g) scale(%g,%g) rotate(%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]), FIX2FLT(sx), FIX2FLT(sy), FIX2FLT(gf_divfix(angle*180, GF_PI) ) );
4078				else
4079					sprintf(attValue, "scale(%g,%g) rotate(%g)", FIX2FLT(sx), FIX2FLT(sy), FIX2FLT(gf_divfix(angle*180, GF_PI) ) );
4080			}
4081		} else {
4082			Fixed a = angle;
4083			if (a<0) a += GF_2PI;
4084			if (matrix->m[2] != 0 || matrix->m[5] != 0)
4085				sprintf(attValue, "translate(%g,%g) rotate(%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]), FIX2FLT(gf_divfix(a*180, GF_PI) ) );
4086			else
4087				sprintf(attValue, "rotate(%g)", FIX2FLT(gf_divfix(a*180, GF_PI) ) );
4088		}
4089	}
4090	/*default*/
4091	if (!strlen(attValue))
4092		sprintf(attValue, "matrix(%g %g %g %g %g %g)", FIX2FLT(matrix->m[0]), FIX2FLT(matrix->m[3]), FIX2FLT(matrix->m[1]), FIX2FLT(matrix->m[4]), FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]) );
4093
4094	return gf_strdup(attValue);
4095}
4096
4097char *gf_svg_dump_attribute(GF_Node *elt, GF_FieldInfo *info)
4098{
4099	char tmp[1024];
4100	u8 intVal;
4101
4102	if (!info->far_ptr) return gf_strdup("");
4103	intVal = *(u8 *)info->far_ptr;
4104
4105	switch (info->fieldType) {
4106	case SVG_Boolean_datatype:
4107		return gf_strdup( *(SVG_Boolean *)info->far_ptr ? "true" : "false");
4108
4109	case SVG_Color_datatype:
4110		return svg_dump_color((SVG_Color *)info->far_ptr);
4111
4112	case SVG_Paint_datatype:
4113	{
4114		SVG_Paint *paint = (SVG_Paint *)info->far_ptr;
4115		if (paint->type == SVG_PAINT_NONE) return gf_strdup("none");
4116		else if (paint->type == SVG_PAINT_INHERIT) return gf_strdup("inherit");
4117		else if (paint->type == SVG_PAINT_URI) {
4118			char *tmp = svg_dump_iri(&paint->iri);
4119			char *res = gf_malloc(sizeof(char)*(strlen(tmp)+6));
4120			sprintf(res, "url(%s)", tmp);
4121			gf_free(tmp);
4122			return res;
4123		} else {
4124			return svg_dump_color(&paint->color);
4125		}
4126	}
4127		break;
4128
4129/* beginning of keyword type parsing */
4130	case SVG_FillRule_datatype:
4131		if (intVal == SVG_FILLRULE_INHERIT) return gf_strdup("inherit");
4132		else if (intVal == SVG_FILLRULE_NONZERO) return gf_strdup("nonzero");
4133		else return gf_strdup("evenodd");
4134		break;
4135
4136	case SVG_StrokeLineJoin_datatype:
4137		if (intVal==SVG_STROKELINEJOIN_INHERIT) return gf_strdup("inherit");
4138		else if (intVal==SVG_STROKELINEJOIN_MITER) return gf_strdup("miter");
4139		else if (intVal==SVG_STROKELINEJOIN_ROUND) return gf_strdup("round");
4140		else if (intVal==SVG_STROKELINEJOIN_BEVEL) return gf_strdup("bevel");
4141		break;
4142	case SVG_StrokeLineCap_datatype:
4143		if (intVal==SVG_STROKELINECAP_INHERIT) return gf_strdup("inherit");
4144		else if (intVal==SVG_STROKELINECAP_BUTT) return gf_strdup("butt");
4145		else if (intVal==SVG_STROKELINECAP_ROUND) return gf_strdup("round");
4146		else if (intVal==SVG_STROKELINECAP_SQUARE) return gf_strdup("square");
4147		break;
4148	case SVG_FontStyle_datatype:
4149		if (intVal==SVG_FONTSTYLE_INHERIT) return gf_strdup("inherit");
4150		else if (intVal==SVG_FONTSTYLE_NORMAL) return gf_strdup("normal");
4151		else if (intVal==SVG_FONTSTYLE_ITALIC) return gf_strdup("italic");
4152		else if (intVal==SVG_FONTSTYLE_OBLIQUE) return gf_strdup("oblique");
4153		break;
4154	case SVG_FontWeight_datatype:
4155		if (intVal==SVG_FONTWEIGHT_INHERIT) return gf_strdup("inherit");
4156		else if (intVal==SVG_FONTWEIGHT_NORMAL) return gf_strdup("normal");
4157		else if (intVal==SVG_FONTWEIGHT_BOLD) return gf_strdup("bold");
4158		else if (intVal==SVG_FONTWEIGHT_BOLDER) return gf_strdup("bolder");
4159		else if (intVal==SVG_FONTWEIGHT_LIGHTER) return gf_strdup("lighter");
4160		else if (intVal==SVG_FONTWEIGHT_100) return gf_strdup("100");
4161		else if (intVal==SVG_FONTWEIGHT_200) return gf_strdup("200");
4162		else if (intVal==SVG_FONTWEIGHT_300) return gf_strdup("300");
4163		else if (intVal==SVG_FONTWEIGHT_400) return gf_strdup("400");
4164		else if (intVal==SVG_FONTWEIGHT_500) return gf_strdup("500");
4165		else if (intVal==SVG_FONTWEIGHT_600) return gf_strdup("600");
4166		else if (intVal==SVG_FONTWEIGHT_700) return gf_strdup("700");
4167		else if (intVal==SVG_FONTWEIGHT_800) return gf_strdup("800");
4168		else if (intVal==SVG_FONTWEIGHT_900) return gf_strdup("900");
4169		break;
4170	case SVG_FontVariant_datatype:
4171		if (intVal==SVG_FONTVARIANT_INHERIT) return gf_strdup("inherit");
4172		else if (intVal==SVG_FONTVARIANT_NORMAL) return gf_strdup("normal");
4173		else if (intVal==SVG_FONTVARIANT_SMALLCAPS) return gf_strdup("small-caps");
4174		break;
4175	case SVG_TextAnchor_datatype:
4176		if (intVal==SVG_TEXTANCHOR_INHERIT) return gf_strdup("inherit");
4177		else if (intVal==SVG_TEXTANCHOR_START) return gf_strdup("start");
4178		else if (intVal==SVG_TEXTANCHOR_MIDDLE) return gf_strdup("middle");
4179		else if (intVal==SVG_TEXTANCHOR_END) return gf_strdup("end");
4180		break;
4181	case SVG_Display_datatype:
4182		if (intVal==SVG_DISPLAY_INHERIT) return gf_strdup("inherit");
4183		else if (intVal==SVG_DISPLAY_NONE) return gf_strdup("none");
4184		else if (intVal==SVG_DISPLAY_INLINE) return gf_strdup("inline");
4185		else if (intVal==SVG_DISPLAY_BLOCK) return gf_strdup("block");
4186		else if (intVal==SVG_DISPLAY_LIST_ITEM) return gf_strdup("list-item");
4187		else if (intVal==SVG_DISPLAY_RUN_IN) return gf_strdup("run-in");
4188		else if (intVal==SVG_DISPLAY_COMPACT) return gf_strdup("compact");
4189		else if (intVal==SVG_DISPLAY_MARKER) return gf_strdup("marker");
4190		else if (intVal==SVG_DISPLAY_TABLE) return gf_strdup("table");
4191		else if (intVal==SVG_DISPLAY_INLINE_TABLE) return gf_strdup("inline-table");
4192		else if (intVal==SVG_DISPLAY_TABLE_ROW_GROUP) return gf_strdup("table-row-group");
4193		else if (intVal==SVG_DISPLAY_TABLE_HEADER_GROUP) return gf_strdup("table-header-group");
4194		else if (intVal==SVG_DISPLAY_TABLE_FOOTER_GROUP) return gf_strdup("table-footer-group");
4195		else if (intVal==SVG_DISPLAY_TABLE_ROW) return gf_strdup("table-row");
4196		else if (intVal==SVG_DISPLAY_TABLE_COLUMN_GROUP) return gf_strdup("table-column-group");
4197		else if (intVal==SVG_DISPLAY_TABLE_COLUMN) return gf_strdup("table-column");
4198		else if (intVal==SVG_DISPLAY_TABLE_CELL) return gf_strdup("table-cell");
4199		else if (intVal==SVG_DISPLAY_TABLE_CAPTION) return gf_strdup("table-caption");
4200		break;
4201	case SVG_Visibility_datatype:
4202		if (intVal==SVG_VISIBILITY_INHERIT) return gf_strdup("inherit");
4203		else if (intVal==SVG_VISIBILITY_VISIBLE) return gf_strdup("visible");
4204		else if (intVal==SVG_VISIBILITY_HIDDEN) return gf_strdup("hidden");
4205		else if (intVal==SVG_VISIBILITY_COLLAPSE) return gf_strdup("collapse");
4206		break;
4207	case SVG_Overflow_datatype:
4208		if (intVal==SVG_OVERFLOW_INHERIT) return gf_strdup("inherit");
4209		else if (intVal==SVG_OVERFLOW_VISIBLE) return gf_strdup("visible");
4210		else if (intVal==SVG_OVERFLOW_HIDDEN) return gf_strdup("hidden");
4211		else if (intVal==SVG_OVERFLOW_SCROLL) return gf_strdup("scroll");
4212		else if (intVal==SVG_OVERFLOW_AUTO) return gf_strdup("auto");
4213		break;
4214	case SVG_ZoomAndPan_datatype:
4215		if (intVal==SVG_ZOOMANDPAN_DISABLE) return gf_strdup("disable");
4216		else return gf_strdup("magnify");
4217		break;
4218	case SVG_DisplayAlign_datatype:
4219		if (intVal==SVG_DISPLAYALIGN_INHERIT) return gf_strdup("inherit");
4220		else if (intVal==SVG_DISPLAYALIGN_AUTO) return gf_strdup("auto");
4221		else if (intVal==SVG_DISPLAYALIGN_BEFORE) return gf_strdup("before");
4222		else if (intVal==SVG_DISPLAYALIGN_CENTER) return gf_strdup("center");
4223		else if (intVal==SVG_DISPLAYALIGN_AFTER) return gf_strdup("after");
4224		break;
4225	case SVG_TextAlign_datatype:
4226		if (intVal==SVG_TEXTALIGN_INHERIT) return gf_strdup("inherit");
4227		else if (intVal==SVG_TEXTALIGN_START) return gf_strdup("start");
4228		else if (intVal==SVG_TEXTALIGN_CENTER) return gf_strdup("center");
4229		else if (intVal==SVG_TEXTALIGN_END) return gf_strdup("end");
4230		break;
4231	case SVG_PointerEvents_datatype:
4232		if (intVal==SVG_POINTEREVENTS_INHERIT) return gf_strdup("inherit");
4233		else if (intVal==SVG_POINTEREVENTS_VISIBLEPAINTED) return gf_strdup("visiblePainted");
4234		else if (intVal==SVG_POINTEREVENTS_VISIBLEFILL) return gf_strdup("visibleFill");
4235		else if (intVal==SVG_POINTEREVENTS_VISIBLESTROKE) return gf_strdup("visibleStroke");
4236		else if (intVal==SVG_POINTEREVENTS_VISIBLE) return gf_strdup("visible");
4237		else if (intVal==SVG_POINTEREVENTS_PAINTED) return gf_strdup("painted");
4238		else if (intVal==SVG_POINTEREVENTS_FILL) return gf_strdup("fill");
4239		else if (intVal==SVG_POINTEREVENTS_STROKE) return gf_strdup("stroke");
4240		else if (intVal==SVG_POINTEREVENTS_ALL) return gf_strdup("all");
4241		else if (intVal==SVG_POINTEREVENTS_NONE) return gf_strdup("none");
4242		else if (intVal==SVG_POINTEREVENTS_BOUNDINGBOX) return gf_strdup("boundingBox");
4243		break;
4244	case SVG_RenderingHint_datatype:
4245		if (intVal==SVG_RENDERINGHINT_INHERIT) return gf_strdup("inherit");
4246		else if (intVal==SVG_RENDERINGHINT_AUTO) return gf_strdup("auto");
4247		else if (intVal==SVG_RENDERINGHINT_OPTIMIZEQUALITY) return gf_strdup("optimizeQuality");
4248		else if (intVal==SVG_RENDERINGHINT_OPTIMIZESPEED) return gf_strdup("optimizeSpeed");
4249		else if (intVal==SVG_RENDERINGHINT_OPTIMIZELEGIBILITY) return gf_strdup("optimizeLegibility");
4250		else if (intVal==SVG_RENDERINGHINT_CRISPEDGES) return gf_strdup("crispEdges");
4251		else if (intVal==SVG_RENDERINGHINT_GEOMETRICPRECISION) return gf_strdup("geometricPrecision");
4252		break;
4253	case SVG_VectorEffect_datatype:
4254		if (intVal==SVG_VECTOREFFECT_INHERIT) return gf_strdup("inherit");
4255		else if (intVal==SVG_VECTOREFFECT_NONE) return gf_strdup("none");
4256		else if (intVal==SVG_VECTOREFFECT_NONSCALINGSTROKE) return gf_strdup("non-scaling-stroke");
4257		break;
4258	case SVG_PlaybackOrder_datatype:
4259		if (intVal== SVG_PLAYBACKORDER_FORWARDONLY) return gf_strdup("forwardOnly");
4260		else if (intVal== SVG_PLAYBACKORDER_ALL) return gf_strdup("all");
4261		break;
4262	case SVG_TimelineBegin_datatype:
4263		if (intVal== SVG_TIMELINEBEGIN_ONSTART) return gf_strdup("onStart");
4264		else if (intVal== SVG_TIMELINEBEGIN_ONLOAD) return gf_strdup("onLoad");
4265		break;
4266	case XML_Space_datatype:
4267		if (intVal==XML_SPACE_DEFAULT) return gf_strdup("default");
4268		else if (intVal==XML_SPACE_PRESERVE) return gf_strdup("preserve");
4269		break;
4270	case XMLEV_Propagate_datatype:
4271		if (intVal==XMLEVENT_PROPAGATE_CONTINUE) return gf_strdup("continue");
4272		else if (intVal==XMLEVENT_PROPAGATE_STOP) return gf_strdup("stop");
4273		break;
4274	case XMLEV_DefaultAction_datatype:
4275		if (intVal==XMLEVENT_DEFAULTACTION_CANCEL) return gf_strdup("cancel");
4276		else if (intVal==XMLEVENT_DEFAULTACTION_PERFORM) return gf_strdup("perform");
4277		break;
4278	case XMLEV_Phase_datatype:
4279		if (intVal==XMLEVENT_PHASE_DEFAULT) return gf_strdup("default");
4280		else if (intVal==XMLEVENT_PHASE_CAPTURE) return gf_strdup("capture");
4281		break;
4282	case SMIL_SyncBehavior_datatype:
4283		if (intVal==SMIL_SYNCBEHAVIOR_INHERIT) return gf_strdup("inherit");
4284		else if (intVal==SMIL_SYNCBEHAVIOR_DEFAULT) return gf_strdup("default");
4285		else if (intVal==SMIL_SYNCBEHAVIOR_LOCKED) return gf_strdup("locked");
4286		else if (intVal==SMIL_SYNCBEHAVIOR_CANSLIP) return gf_strdup("canSlip");
4287		else if (intVal==SMIL_SYNCBEHAVIOR_INDEPENDENT) return gf_strdup("independent");
4288		break;
4289	case SMIL_SyncTolerance_datatype:
4290		if (((SMIL_SyncTolerance*)info->far_ptr)->type==SMIL_SYNCTOLERANCE_INHERIT) return gf_strdup("inherit");
4291		else if (((SMIL_SyncTolerance*)info->far_ptr)->type==SMIL_SYNCTOLERANCE_DEFAULT) return gf_strdup("default");
4292		else if (((SMIL_SyncTolerance*)info->far_ptr)->type==SMIL_SYNCBEHAVIOR_LOCKED) {
4293			sprintf(tmp, "%g", ((SMIL_SyncTolerance*)info->far_ptr)->value);
4294			return gf_strdup(tmp);
4295		}
4296		break;
4297	case SMIL_AttributeType_datatype:
4298		if (intVal==SMIL_ATTRIBUTETYPE_AUTO) return gf_strdup("auto");
4299		else if (intVal==SMIL_ATTRIBUTETYPE_XML) return gf_strdup("XML");
4300		else if (intVal==SMIL_ATTRIBUTETYPE_CSS) return gf_strdup("CSS");
4301		break;
4302	case SMIL_CalcMode_datatype:
4303		if (intVal==SMIL_CALCMODE_DISCRETE) return gf_strdup("discrete");
4304		else if (intVal==SMIL_CALCMODE_LINEAR) return gf_strdup("linear");
4305		else if (intVal==SMIL_CALCMODE_PACED) return gf_strdup("paced");
4306		else if (intVal==SMIL_CALCMODE_SPLINE) return gf_strdup("spline");
4307		break;
4308	case SMIL_Additive_datatype:
4309		if (intVal==SMIL_ADDITIVE_REPLACE) return gf_strdup("replace");
4310		else if (intVal==SMIL_ADDITIVE_SUM) return gf_strdup("sum");
4311		break;
4312	case SMIL_Accumulate_datatype:
4313		if (intVal==SMIL_ACCUMULATE_NONE) return gf_strdup("none");
4314		else if (intVal==SMIL_ACCUMULATE_SUM) return gf_strdup("sum");
4315		break;
4316	case SMIL_Restart_datatype:
4317		if (intVal==SMIL_RESTART_ALWAYS) return gf_strdup("always");
4318		else if (intVal==SMIL_RESTART_WHENNOTACTIVE) return gf_strdup("whenNotActive");
4319		else if (intVal==SMIL_RESTART_NEVER) return gf_strdup("never");
4320		break;
4321	case SMIL_Fill_datatype:
4322		if (intVal==SMIL_FILL_FREEZE) return gf_strdup("freeze");
4323		else if (intVal==SMIL_FILL_REMOVE) return gf_strdup("remove");
4324		break;
4325
4326	case SVG_GradientUnit_datatype:
4327		if (intVal==SVG_GRADIENTUNITS_USER) return gf_strdup("userSpaceOnUse");
4328		else if (intVal==SVG_GRADIENTUNITS_OBJECT) return gf_strdup("objectBoundingBox");
4329		break;
4330	case SVG_InitialVisibility_datatype:
4331		if (intVal==SVG_INITIALVISIBILTY_WHENSTARTED) return gf_strdup("whenStarted");
4332		else if (intVal==SVG_INITIALVISIBILTY_ALWAYS) return gf_strdup("always");
4333		break;
4334	case SVG_FocusHighlight_datatype:
4335		if (intVal==SVG_FOCUSHIGHLIGHT_AUTO) return gf_strdup("auto");
4336		else if (intVal==SVG_FOCUSHIGHLIGHT_NONE) return gf_strdup("none");
4337		break;
4338	case SVG_Overlay_datatype:
4339		if (intVal==SVG_OVERLAY_NONE) return gf_strdup("none");
4340		else if (intVal==SVG_OVERLAY_TOP) return gf_strdup("top");
4341		break;
4342	case SVG_TransformBehavior_datatype:
4343		if (intVal==SVG_TRANSFORMBEHAVIOR_GEOMETRIC) return gf_strdup("geometric");
4344		else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED) return gf_strdup("pinned");
4345		else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED90) return gf_strdup("pinned90");
4346		else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED180) return gf_strdup("pinned180");
4347		else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED270) return gf_strdup("pinned270");
4348		break;
4349	case SVG_SpreadMethod_datatype:
4350		if (intVal==SVG_SPREAD_REFLECT) return gf_strdup("reflect");
4351		else if (intVal==SVG_SPREAD_REFLECT) return gf_strdup("repeat");
4352		else return gf_strdup("pad");
4353		break;
4354
4355	case SVG_Filter_TransferType_datatype:
4356		if (intVal==SVG_FILTER_TRANSFER_TABLE) return gf_strdup("table");
4357		else if (intVal==SVG_FILTER_TRANSFER_DISCRETE) return gf_strdup("discrete");
4358		else if (intVal==SVG_FILTER_TRANSFER_LINEAR) return gf_strdup("linear");
4359		else if (intVal==SVG_FILTER_TRANSFER_GAMMA) return gf_strdup("gamma");
4360		else return gf_strdup("identity");
4361		break;
4362
4363	case LASeR_Choice_datatype:
4364		if (intVal==LASeR_CHOICE_ALL) return gf_strdup(