Mercurial > pidgin
comparison libpurple/protocols/myspace/markup.c @ 25237:401f548e3544
propagate from branch 'im.pidgin.pidgin' (head df6eba32e5b6b34d7483cbfb7e9f2e4c836ac35f)
to branch 'org.darkrain42.pidgin.buddy-add' (head 6831808999a270f8c1a128c7430a73d3dc0bfae2)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sun, 21 Dec 2008 18:32:37 +0000 |
parents | e12788365764 |
children | c51fc4daf81b |
comparison
equal
deleted
inserted
replaced
25172:125cac3e24ee | 25237:401f548e3544 |
---|---|
19 | 19 |
20 #include "myspace.h" | 20 #include "myspace.h" |
21 | 21 |
22 typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); | 22 typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); |
23 | 23 |
24 /* Internal functions */ | |
25 | |
26 static guint msim_point_to_purple_size(MsimSession *session, guint point); | |
27 static guint msim_purple_size_to_point(MsimSession *session, guint size); | |
28 static guint msim_height_to_point(MsimSession *session, guint height); | |
29 static guint msim_point_to_height(MsimSession *session, guint point); | |
30 | |
31 static int msim_markup_tag_to_html(MsimSession *, xmlnode *root, gchar **begin, gchar **end); | |
32 static int html_tag_to_msim_markup(MsimSession *, xmlnode *root, gchar **begin, gchar **end); | |
33 static gchar *msim_convert_xml(MsimSession *, const gchar *raw, MSIM_XMLNODE_CONVERT f); | |
34 static gchar *msim_convert_smileys_to_markup(gchar *before); | |
35 static double msim_round(double round); | |
36 | |
37 | |
38 /* Globals */ | 24 /* Globals */ |
39 | 25 |
40 /* The names in in emoticon_names (for <i n=whatever>) map to corresponding | 26 /* The names in in emoticon_names (for <i n=whatever>) map to corresponding |
41 * entries in emoticon_symbols (for the ASCII representation of the emoticon). | 27 * entries in emoticon_symbols (for the ASCII representation of the emoticon). |
42 * | 28 * |
43 * Multiple emoticon symbols in Pidgin can map to one name. List the | 29 * Multiple emoticon symbols in Pidgin can map to one name. List the |
44 * canonical form, as inserted by the "Smile!" dialog, first. For example, | 30 * canonical form, as inserted by the "Smile!" dialog, first. For example, |
45 * :) comes before :-), because although both are recognized as 'happy', | 31 * :) comes before :-), because although both are recognized as 'happy', |
88 { "worried", ":[" }, | 74 { "worried", ":[" }, |
89 { "kiss", ":x" }, | 75 { "kiss", ":x" }, |
90 { NULL, NULL } | 76 { NULL, NULL } |
91 }; | 77 }; |
92 | 78 |
93 | |
94 | |
95 /* Indexes of this array + 1 map HTML font size to scale of normal font size. * | 79 /* Indexes of this array + 1 map HTML font size to scale of normal font size. * |
96 * Based on _point_sizes from libpurple/gtkimhtml.c | 80 * Based on _point_sizes from libpurple/gtkimhtml.c |
97 * 1 2 3 4 5 6 7 */ | 81 * 1 2 3 4 5 6 7 */ |
98 static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 }; | 82 static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 }; |
99 | 83 |
100 #define MAX_FONT_SIZE 7 /* Purple maximum font size */ | 84 /* Purple maximum font size. Equivalent to sizeof(_font_scale) / sizeof(_font_scale[0]) */ |
85 #define MAX_FONT_SIZE 7 | |
86 | |
101 #define POINTS_PER_INCH 72 /* How many pt's in an inch */ | 87 #define POINTS_PER_INCH 72 /* How many pt's in an inch */ |
102 | 88 |
103 /* Text formatting bits for <f s=#> */ | 89 /* Text formatting bits for <f s=#> */ |
104 #define MSIM_TEXT_BOLD 1 | 90 #define MSIM_TEXT_BOLD 1 |
105 #define MSIM_TEXT_ITALIC 2 | 91 #define MSIM_TEXT_ITALIC 2 |
106 #define MSIM_TEXT_UNDERLINE 4 | 92 #define MSIM_TEXT_UNDERLINE 4 |
107 | 93 |
108 /* Default baseline size of purple's fonts, in points. What is size 3 in points. | 94 /* Default baseline size of purple's fonts, in points. What is size 3 in points. |
109 * _font_scale specifies scaling factor relative to this point size. Note this | 95 * _font_scale specifies scaling factor relative to this point size. Note this |
110 * is only the default; it is configurable in account options. */ | 96 * is only the default; it is configurable in account options. */ |
111 #define MSIM_BASE_FONT_POINT_SIZE 8 | 97 #define MSIM_BASE_FONT_POINT_SIZE 8 |
112 | 98 |
113 /* Default display's DPI. 96 is common but it can differ. Also configurable | 99 /* Default display's DPI. 96 is common but it can differ. Also configurable |
114 * in account options. */ | 100 * in account options. */ |
115 #define MSIM_DEFAULT_DPI 96 | 101 #define MSIM_DEFAULT_DPI 96 |
116 | 102 |
117 | |
118 /* round is part of C99, but sometimes is unavailable before then. | 103 /* round is part of C99, but sometimes is unavailable before then. |
119 * Based on http://forums.belution.com/en/cpp/000/050/13.shtml | 104 * Based on http://forums.belution.com/en/cpp/000/050/13.shtml |
120 */ | 105 */ |
121 double msim_round(double value) | 106 static double msim_round(double value) |
122 { | 107 { |
123 if (value < 0) { | 108 if (value < 0) { |
124 return -(floor(-value + 0.5)); | 109 return -(floor(-value + 0.5)); |
125 } else { | 110 } else { |
126 return floor( value + 0.5); | 111 return floor( value + 0.5); |
127 } | 112 } |
128 } | 113 } |
129 | 114 |
130 | 115 /** Convert typographical font point size to HTML font size. |
131 /** Convert typographical font point size to HTML font size. | |
132 * Based on libpurple/gtkimhtml.c */ | 116 * Based on libpurple/gtkimhtml.c */ |
133 static guint | 117 static guint |
134 msim_point_to_purple_size(MsimSession *session, guint point) | 118 msim_point_to_purple_size(MsimSession *session, guint point) |
135 { | 119 { |
136 guint size, this_point, base; | 120 guint size, this_point, base; |
137 gdouble scale; | 121 |
138 | |
139 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); | 122 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); |
140 | 123 |
141 for (size = 0; | 124 for (size = 0; size < MAX_FONT_SIZE; ++size) { |
142 size < sizeof(_font_scale) / sizeof(_font_scale[0]); | 125 this_point = (guint)msim_round(base * _font_scale[size]); |
143 ++size) { | |
144 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; | |
145 this_point = (guint)msim_round(scale * base); | |
146 | 126 |
147 if (this_point >= point) { | 127 if (this_point >= point) { |
148 purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n", | 128 purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n", |
149 point, size); | 129 point, size); |
150 return size; | 130 return size; |
174 | 154 |
175 return point; | 155 return point; |
176 } | 156 } |
177 | 157 |
178 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ | 158 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ |
179 static guint | 159 static guint |
180 msim_height_to_point(MsimSession *session, guint height) | 160 msim_height_to_point(MsimSession *session, guint height) |
181 { | 161 { |
182 guint dpi; | 162 guint dpi; |
183 | 163 |
184 dpi = purple_account_get_int(session->account, "dpi", MSIM_DEFAULT_DPI); | 164 dpi = purple_account_get_int(session->account, "dpi", MSIM_DEFAULT_DPI); |
199 | 179 |
200 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); | 180 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); |
201 } | 181 } |
202 | 182 |
203 /** Convert the msim markup <f> (font) tag into HTML. */ | 183 /** Convert the msim markup <f> (font) tag into HTML. */ |
204 static void | 184 static void |
205 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | 185 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
206 { | 186 { |
207 const gchar *face, *height_str, *decor_str; | 187 const gchar *face, *height_str, *decor_str; |
208 GString *gs_end, *gs_begin; | 188 GString *gs_end, *gs_begin; |
209 guint decor, height; | 189 guint decor, height; |
210 | 190 |
211 face = xmlnode_get_attrib(root, "f"); | 191 face = xmlnode_get_attrib(root, "f"); |
212 height_str = xmlnode_get_attrib(root, "h"); | 192 height_str = xmlnode_get_attrib(root, "h"); |
213 decor_str = xmlnode_get_attrib(root, "s"); | 193 decor_str = xmlnode_get_attrib(root, "s"); |
214 | 194 |
215 if (height_str) { | 195 /* Validate the font face, to avoid constructing invalid HTML later */ |
216 height = atol(height_str); | 196 if (face != NULL && strchr(face, '\'') != NULL) |
197 face = NULL; | |
198 | |
199 height = height_str != NULL ? atol(height_str) : 12; | |
200 decor = decor_str != NULL ? atol(decor_str) : 0; | |
201 | |
202 /* | |
203 * The HTML we're constructing here is a bit redudant. Ideally we | |
204 * would use only the font-family and font-size CSS span, but Pidgin | |
205 * doesn't support it (it's included for other UIs). For Pidgin we | |
206 * wrap the whole thing in an ugly font tag, and Pidgin will happily | |
207 * ignore the <span>. | |
208 */ | |
209 gs_begin = g_string_new(""); | |
210 if (height && !face) { | |
211 guint point_size = msim_height_to_point(session, height); | |
212 g_string_printf(gs_begin, | |
213 "<font size='%d'><span style='font-size: %dpt'>", | |
214 msim_point_to_purple_size(session, point_size), | |
215 point_size); | |
216 } else if (height && face) { | |
217 guint point_size = msim_height_to_point(session, height); | |
218 g_string_printf(gs_begin, | |
219 "<font face='%s' size='%d'><span style='font-family: %s; font-size: %dpt'>", | |
220 face, msim_point_to_purple_size(session, point_size), | |
221 face, point_size); | |
217 } else { | 222 } else { |
218 height = 12; | 223 g_string_printf(gs_begin, "<font><span>"); |
219 } | 224 } |
220 | 225 |
221 if (decor_str) { | 226 gs_end = g_string_new("</span></font>"); |
222 decor = atol(decor_str); | |
223 } else { | |
224 decor = 0; | |
225 } | |
226 | |
227 gs_begin = g_string_new(""); | |
228 /* TODO: get font size working */ | |
229 if (height && !face) { | |
230 g_string_printf(gs_begin, "<font size='%d'>", | |
231 msim_point_to_purple_size(session, msim_height_to_point(session, height))); | |
232 } else if (height && face) { | |
233 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face, | |
234 msim_point_to_purple_size(session, msim_height_to_point(session, height))); | |
235 } else { | |
236 g_string_printf(gs_begin, "<font>"); | |
237 } | |
238 | |
239 /* No support for font-size CSS? */ | |
240 /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, | |
241 msim_height_to_point(height)); */ | |
242 | |
243 gs_end = g_string_new("</font>"); | |
244 | 227 |
245 if (decor & MSIM_TEXT_BOLD) { | 228 if (decor & MSIM_TEXT_BOLD) { |
246 g_string_append(gs_begin, "<b>"); | 229 g_string_append(gs_begin, "<b>"); |
247 g_string_prepend(gs_end, "</b>"); | 230 g_string_prepend(gs_end, "</b>"); |
248 } | 231 } |
255 if (decor & MSIM_TEXT_UNDERLINE) { | 238 if (decor & MSIM_TEXT_UNDERLINE) { |
256 g_string_append(gs_begin, "<u>"); | 239 g_string_append(gs_begin, "<u>"); |
257 g_string_append(gs_end, "</u>"); | 240 g_string_append(gs_end, "</u>"); |
258 } | 241 } |
259 | 242 |
260 | |
261 *begin = g_string_free(gs_begin, FALSE); | 243 *begin = g_string_free(gs_begin, FALSE); |
262 *end = g_string_free(gs_end, FALSE); | 244 *end = g_string_free(gs_end, FALSE); |
263 } | 245 } |
264 | 246 |
265 /** Convert a msim markup color to a color suitable for libpurple. | 247 /** Convert a msim markup color to a color suitable for libpurple. |
266 * | 248 * |
267 * @param msim Either a color name, or an rgb(x,y,z) code. | 249 * @param msim Either a color name, or an rgb(x,y,z) code. |
268 * | 250 * |
269 * @return A new string, either a color name or #rrggbb code. Must g_free(). | 251 * @return A new string, either a color name or #rrggbb code. Must g_free(). |
270 */ | 252 */ |
271 static char * | 253 static char * |
272 msim_color_to_purple(const char *msim) | 254 msim_color_to_purple(const char *msim) |
273 { | 255 { |
274 guint red, green, blue; | 256 guint red, green, blue; |
275 | 257 |
285 | 267 |
286 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); | 268 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); |
287 } | 269 } |
288 | 270 |
289 /** Convert the msim markup <a> (anchor) tag into HTML. */ | 271 /** Convert the msim markup <a> (anchor) tag into HTML. */ |
290 static void | 272 static void |
291 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | 273 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
292 { | 274 { |
293 const gchar *href; | 275 const gchar *href; |
294 | 276 |
295 href = xmlnode_get_attrib(root, "h"); | 277 href = xmlnode_get_attrib(root, "h"); |
300 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href); | 282 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href); |
301 *end = g_strdup("</a>"); | 283 *end = g_strdup("</a>"); |
302 } | 284 } |
303 | 285 |
304 /** Convert the msim markup <p> (paragraph) tag into HTML. */ | 286 /** Convert the msim markup <p> (paragraph) tag into HTML. */ |
305 static void | 287 static void |
306 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | 288 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
307 { | 289 { |
308 /* Just pass through unchanged. | 290 /* Just pass through unchanged. |
309 * | 291 * |
310 * Note: attributes currently aren't passed, if there are any. */ | 292 * Note: attributes currently aren't passed, if there are any. */ |
311 *begin = g_strdup("<p>"); | 293 *begin = g_strdup("<p>"); |
312 *end = g_strdup("</p>"); | 294 *end = g_strdup("</p>"); |
313 } | 295 } |
314 | 296 |
315 /** Convert the msim markup <c> tag (text color) into HTML. TODO: Test */ | 297 /** |
316 static void | 298 * Convert the msim markup <c> tag (text color) into HTML. |
299 */ | |
300 static void | |
317 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | 301 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
318 { | 302 { |
319 const gchar *color; | 303 const gchar *color; |
320 gchar *purple_color; | 304 gchar *purple_color; |
321 | 305 |
328 return; | 312 return; |
329 } | 313 } |
330 | 314 |
331 purple_color = msim_color_to_purple(color); | 315 purple_color = msim_color_to_purple(color); |
332 | 316 |
333 *begin = g_strdup_printf("<font color='%s'>", purple_color); | 317 #ifdef USE_CSS_FORMATTING |
318 *begin = g_strdup_printf("<span style='color: %s'>", purple_color); | |
319 *end = g_strdup("</span>"); | |
320 #else | |
321 *begin = g_strdup_printf("<font color='%s'>", purple_color); | |
322 *end = g_strdup("</font>"); | |
323 #endif | |
334 | 324 |
335 g_free(purple_color); | 325 g_free(purple_color); |
336 | 326 } |
337 /* *begin = g_strdup_printf("<span style='color: %s'>", color); */ | 327 |
338 *end = g_strdup("</font>"); | 328 /** |
339 } | 329 * Convert the msim markup <b> tag (background color) into HTML. |
340 | 330 */ |
341 /** Convert the msim markup <b> tag (background color) into HTML. TODO: Test */ | 331 static void |
342 static void | |
343 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | 332 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
344 { | 333 { |
345 const gchar *color; | 334 const gchar *color; |
346 gchar *purple_color; | 335 gchar *purple_color; |
347 | 336 |
354 return; | 343 return; |
355 } | 344 } |
356 | 345 |
357 purple_color = msim_color_to_purple(color); | 346 purple_color = msim_color_to_purple(color); |
358 | 347 |
359 /* TODO: find out how to set background color. */ | 348 #ifdef USE_CSS_FORMATTING |
360 *begin = g_strdup_printf("<span style='background-color: %s'>", | 349 *begin = g_strdup_printf("<span style='background-color: %s'>", purple_color); |
361 purple_color); | 350 *end = g_strdup("</span>"); |
351 #else | |
352 *begin = g_strdup_printf("<body bgcolor='%s'>", purple_color); | |
353 *end = g_strdup("</body>"); | |
354 #endif | |
355 | |
362 g_free(purple_color); | 356 g_free(purple_color); |
363 | |
364 *end = g_strdup("</p>"); | |
365 } | 357 } |
366 | 358 |
367 /** Convert the msim markup <i> tag (emoticon image) into HTML. */ | 359 /** Convert the msim markup <i> tag (emoticon image) into HTML. */ |
368 static void | 360 static void |
369 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | 361 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
370 { | 362 { |
371 const gchar *name; | 363 const gchar *name; |
372 guint i; | 364 guint i; |
373 struct MSIM_EMOTICON *emote; | 365 struct MSIM_EMOTICON *emote; |
394 *begin = g_strdup_printf("**%s**", name); | 386 *begin = g_strdup_printf("**%s**", name); |
395 *end = g_strdup(""); | 387 *end = g_strdup(""); |
396 } | 388 } |
397 | 389 |
398 /** Convert an individual msim markup tag to HTML. */ | 390 /** Convert an individual msim markup tag to HTML. */ |
399 static int | 391 static int |
400 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, | 392 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, |
401 gchar **end) | 393 gchar **end) |
402 { | 394 { |
403 g_return_val_if_fail(root != NULL, 0); | 395 g_return_val_if_fail(root != NULL, 0); |
404 | 396 |
405 if (g_str_equal(root->name, "f")) { | 397 if (g_str_equal(root->name, "f")) { |
414 msim_markup_b_to_html(session, root, begin, end); | 406 msim_markup_b_to_html(session, root, begin, end); |
415 } else if (g_str_equal(root->name, "i")) { | 407 } else if (g_str_equal(root->name, "i")) { |
416 msim_markup_i_to_html(session, root, begin, end); | 408 msim_markup_i_to_html(session, root, begin, end); |
417 } else { | 409 } else { |
418 purple_debug_info("msim", "msim_markup_tag_to_html: " | 410 purple_debug_info("msim", "msim_markup_tag_to_html: " |
419 "unknown tag name=%s, ignoring", | 411 "unknown tag name=%s, ignoring\n", |
420 root->name ? root->name : "(NULL)"); | 412 root->name ? root->name : "(NULL)"); |
421 *begin = g_strdup(""); | 413 *begin = g_strdup(""); |
422 *end = g_strdup(""); | 414 *end = g_strdup(""); |
423 } | 415 } |
424 return 0; | 416 return 0; |
425 } | 417 } |
426 | 418 |
427 /** Convert an individual HTML tag to msim markup. */ | 419 /** Convert an individual HTML tag to msim markup. */ |
428 static int | 420 static int |
429 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, | 421 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, |
430 gchar **end) | 422 gchar **end) |
431 { | 423 { |
432 int ret = 0; | 424 int ret = 0; |
433 | 425 |
434 if (!purple_utf8_strcasecmp(root->name, "root") || | 426 if (!purple_utf8_strcasecmp(root->name, "root") || |
435 !purple_utf8_strcasecmp(root->name, "html")) { | 427 !purple_utf8_strcasecmp(root->name, "html")) { |
436 *begin = g_strdup(""); | 428 *begin = g_strdup(""); |
437 *end = g_strdup(""); | 429 *end = g_strdup(""); |
438 /* TODO: Coalesce nested tags into one <f> tag! | 430 /* TODO: Coalesce nested tags into one <f> tag! |
439 * Currently, the 's' value will be overwritten when b/i/u is nested | 431 * Currently, the 's' value will be overwritten when b/i/u is nested |
440 * within another one, and only the inner-most formatting will be | 432 * within another one, and only the inner-most formatting will be |
441 * applied to the text. */ | 433 * applied to the text. */ |
442 } else if (!purple_utf8_strcasecmp(root->name, "b")) { | 434 } else if (!purple_utf8_strcasecmp(root->name, "b")) { |
443 if (root->child->type == XMLNODE_TYPE_DATA) { | 435 if (root->child->type == XMLNODE_TYPE_DATA) { |
444 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); | 436 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); |
445 *end = g_strdup("</f>"); | 437 *end = g_strdup("</f>"); |
513 g_free(link_text); | 505 g_free(link_text); |
514 root->child = NULL; | 506 root->child = NULL; |
515 | 507 |
516 *end = g_strdup(""); | 508 *end = g_strdup(""); |
517 } else if (!purple_utf8_strcasecmp(root->name, "font")) { | 509 } else if (!purple_utf8_strcasecmp(root->name, "font")) { |
510 GString *tmpbegin, *tmpend; | |
518 const gchar *size; | 511 const gchar *size; |
519 const gchar *face; | 512 const gchar *face; |
513 const gchar *color; | |
520 | 514 |
521 size = xmlnode_get_attrib(root, "size"); | 515 size = xmlnode_get_attrib(root, "size"); |
522 face = xmlnode_get_attrib(root, "face"); | 516 face = xmlnode_get_attrib(root, "face"); |
523 | 517 color = xmlnode_get_attrib(root, "color"); |
524 if (face && size) { | 518 |
525 *begin = g_strdup_printf("<f f='%s' h='%d'>", face, | 519 tmpbegin = g_string_new("<f"); |
526 msim_point_to_height(session, | 520 tmpend = g_string_new("</f>"); |
527 msim_purple_size_to_point(session, atoi(size)))); | 521 |
528 } else if (face) { | 522 if (face != NULL) |
529 *begin = g_strdup_printf("<f f='%s'>", face); | 523 g_string_append_printf(tmpbegin, "f='%s'", face); |
530 } else if (size) { | 524 |
531 *begin = g_strdup_printf("<f h='%d'>", | 525 if (size != NULL) |
526 g_string_append_printf(tmpbegin, "h='%d'", | |
532 msim_point_to_height(session, | 527 msim_point_to_height(session, |
533 msim_purple_size_to_point(session, atoi(size)))); | 528 msim_purple_size_to_point(session, atoi(size)))); |
534 } else { | 529 |
535 *begin = g_strdup("<f>"); | 530 /* Close the <f> tag */ |
536 } | 531 g_string_append(tmpbegin, ">"); |
537 | 532 |
538 *end = g_strdup("</f>"); | 533 if (color != NULL) { |
539 | 534 g_string_append_printf(tmpbegin, "<c v='%s'>", color); |
540 /* TODO: color (bg uses <body>), emoticons */ | 535 g_string_prepend(tmpend, "</c>"); |
536 } | |
537 | |
538 *begin = g_string_free(tmpbegin, FALSE); | |
539 *end = g_string_free(tmpend, FALSE); | |
540 | |
541 } else if (!purple_utf8_strcasecmp(root->name, "body")) { | |
542 const gchar *bgcolor; | |
543 | |
544 bgcolor = xmlnode_get_attrib(root, "bgcolor"); | |
545 | |
546 if (bgcolor != NULL) { | |
547 *begin = g_strdup_printf("<b v='%s'>", bgcolor); | |
548 *end = g_strdup("</b>"); | |
549 } | |
550 | |
541 } else { | 551 } else { |
542 gchar *err; | 552 gchar *err; |
543 | 553 |
544 #ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS | 554 #ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS |
545 *begin = g_strdup_printf("[%s]", root->name); | 555 *begin = g_strdup_printf("[%s]", root->name); |
548 *begin = g_strdup(""); | 558 *begin = g_strdup(""); |
549 *end = g_strdup(""); | 559 *end = g_strdup(""); |
550 #endif | 560 #endif |
551 | 561 |
552 err = g_strdup_printf("html_tag_to_msim_markup: unrecognized " | 562 err = g_strdup_printf("html_tag_to_msim_markup: unrecognized " |
553 "HTML tag %s was sent by the IM client; ignoring", | 563 "HTML tag %s was sent by the IM client; ignoring", |
554 root->name ? root->name : "(NULL)"); | 564 root->name ? root->name : "(NULL)"); |
555 msim_unrecognized(NULL, NULL, err); | 565 msim_unrecognized(NULL, NULL, err); |
556 g_free(err); | 566 g_free(err); |
557 } | 567 } |
558 return ret; | 568 return ret; |
562 * | 572 * |
563 * @param f Function to convert tags. | 573 * @param f Function to convert tags. |
564 * | 574 * |
565 * @return An HTML string. Caller frees. | 575 * @return An HTML string. Caller frees. |
566 */ | 576 */ |
567 static gchar * | 577 static void |
568 msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed) | 578 msim_convert_xmlnode(MsimSession *session, GString *out, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed) |
569 { | 579 { |
570 xmlnode *node; | 580 xmlnode *node; |
571 gchar *begin, *inner, *end; | 581 gchar *begin, *inner, *end, *tmp; |
572 GString *final; | |
573 int descended = nodes_processed; | 582 int descended = nodes_processed; |
574 | 583 |
575 if (!root || !root->name) { | 584 if (!root || !root->name) |
576 return g_strdup(""); | 585 return; |
577 } | |
578 | 586 |
579 purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", | 587 purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", |
580 root->name); | 588 root->name); |
581 | 589 |
582 begin = inner = end = NULL; | 590 begin = inner = end = NULL; |
583 | 591 |
584 final = g_string_new(""); | |
585 | |
586 if (descended == 0) /* We've not formatted this yet.. :) */ | 592 if (descended == 0) /* We've not formatted this yet.. :) */ |
587 descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */ | 593 descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */ |
588 | 594 |
589 g_string_append(final, begin); | 595 g_string_append(out, begin); |
596 g_free(begin); | |
590 | 597 |
591 /* Loop over all child nodes. */ | 598 /* Loop over all child nodes. */ |
592 for (node = root->child; node != NULL; node = node->next) { | 599 for (node = root->child; node != NULL; node = node->next) { |
593 switch (node->type) { | 600 switch (node->type) { |
594 case XMLNODE_TYPE_ATTRIB: | 601 case XMLNODE_TYPE_ATTRIB: |
595 /* Attributes handled above. */ | 602 /* Attributes handled above. */ |
596 break; | 603 break; |
597 | 604 |
598 case XMLNODE_TYPE_TAG: | 605 case XMLNODE_TYPE_TAG: |
599 /* A tag or tag with attributes. Recursively descend. */ | 606 /* A tag or tag with attributes. Recursively descend. */ |
600 inner = msim_convert_xmlnode(session, node, f, descended); | 607 msim_convert_xmlnode(session, out, node, f, descended); |
601 g_return_val_if_fail(inner != NULL, NULL); | 608 |
602 | 609 purple_debug_info("msim", " ** node name=%s\n", |
603 purple_debug_info("msim", " ** node name=%s\n", | 610 node->name ? node->name : "(NULL)"); |
604 (node && node->name) ? node->name : "(NULL)"); | |
605 break; | 611 break; |
606 | 612 |
607 case XMLNODE_TYPE_DATA: | 613 case XMLNODE_TYPE_DATA: |
608 /* Literal text. */ | 614 /* Literal text. */ |
609 inner = g_strndup(node->data, node->data_sz); | 615 /* |
610 purple_debug_info("msim", " ** node data=%s\n", | 616 * TODO: Why is it necessary to escape here? I thought |
611 inner ? inner : "(NULL)"); | 617 * node->data was already escaped? |
618 */ | |
619 tmp = g_markup_escape_text(node->data, node->data_sz); | |
620 g_string_append(out, tmp); | |
621 g_free(tmp); | |
612 break; | 622 break; |
613 | 623 |
614 default: | 624 default: |
615 purple_debug_info("msim", | 625 purple_debug_warning("msim", |
616 "msim_convert_xmlnode: strange node\n"); | 626 "msim_convert_xmlnode: unknown node type\n"); |
617 } | |
618 | |
619 if (inner) { | |
620 g_string_append(final, inner); | |
621 g_free(inner); | |
622 inner = NULL; | |
623 } | 627 } |
624 } | 628 } |
625 | 629 |
626 /* TODO: Note that msim counts each piece of text enclosed by <f> as | 630 /* TODO: Note that msim counts each piece of text enclosed by <f> as |
627 * a paragraph and will display each on its own line. You actually have | 631 * a paragraph and will display each on its own line. You actually have |
628 * to _nest_ <f> tags to intersperse different text in one paragraph! | 632 * to _nest_ <f> tags to intersperse different text in one paragraph! |
629 * Comment out this line below to see. */ | 633 * Comment out this line below to see. */ |
630 g_string_append(final, end); | 634 g_string_append(out, end); |
631 | |
632 g_free(begin); | |
633 g_free(end); | 635 g_free(end); |
634 | |
635 purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n", | |
636 (final && final->str) ? final->str : "(NULL)"); | |
637 | |
638 return g_string_free(final, FALSE); | |
639 } | 636 } |
640 | 637 |
641 /** Convert XML to something based on MSIM_XMLNODE_CONVERT. */ | 638 /** Convert XML to something based on MSIM_XMLNODE_CONVERT. */ |
642 static gchar * | 639 static gchar * |
643 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) | 640 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) |
644 { | 641 { |
645 xmlnode *root; | 642 xmlnode *root; |
646 gchar *str; | 643 GString *str; |
647 gchar *enclosed_raw; | 644 gchar *enclosed_raw; |
648 | 645 |
649 g_return_val_if_fail(raw != NULL, NULL); | 646 g_return_val_if_fail(raw != NULL, NULL); |
650 | 647 |
651 /* Enclose text in one root tag, to try to make it valid XML for parsing. */ | 648 /* Enclose text in one root tag, to try to make it valid XML for parsing. */ |
652 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL); | 649 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL); |
653 | 650 |
654 root = xmlnode_from_str(enclosed_raw, -1); | 651 root = xmlnode_from_str(enclosed_raw, -1); |
655 | 652 |
656 if (!root) { | 653 if (!root) { |
657 purple_debug_info("msim", "msim_markup_to_html: couldn't parse " | 654 purple_debug_warning("msim", "msim_markup_to_html: couldn't parse " |
658 "%s as XML, returning raw: %s\n", enclosed_raw, raw); | 655 "%s as XML, returning raw: %s\n", enclosed_raw, raw); |
659 /* TODO: msim_unrecognized */ | 656 /* TODO: msim_unrecognized */ |
660 g_free(enclosed_raw); | 657 g_free(enclosed_raw); |
661 return g_strdup(raw); | 658 return g_strdup(raw); |
662 } | 659 } |
663 | 660 |
664 g_free(enclosed_raw); | 661 g_free(enclosed_raw); |
665 | 662 |
666 str = msim_convert_xmlnode(session, root, f, 0); | 663 str = g_string_new(NULL); |
667 g_return_val_if_fail(str != NULL, NULL); | 664 msim_convert_xmlnode(session, str, root, f, 0); |
668 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str); | |
669 | |
670 xmlnode_free(root); | 665 xmlnode_free(root); |
671 | 666 |
672 return str; | 667 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str->str); |
668 | |
669 return g_string_free(str, FALSE); | |
673 } | 670 } |
674 | 671 |
675 /** Convert plaintext smileys to <i> markup tags. | 672 /** Convert plaintext smileys to <i> markup tags. |
676 * | 673 * |
677 * @param before Original text with ASCII smileys. Will be freed. | 674 * @param before Original text with ASCII smileys. Will be freed. |
694 symbol = emote->symbol; | 691 symbol = emote->symbol; |
695 | 692 |
696 replacement = g_strdup_printf("<i n=\"%s\"/>", name); | 693 replacement = g_strdup_printf("<i n=\"%s\"/>", name); |
697 | 694 |
698 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", | 695 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", |
699 symbol ? symbol : "(NULL)", | 696 symbol ? symbol : "(NULL)", |
700 replacement ? replacement : "(NULL)"); | 697 replacement ? replacement : "(NULL)"); |
701 new = purple_strreplace(old, symbol, replacement); | 698 new = purple_strreplace(old, symbol, replacement); |
702 | 699 |
703 g_free(replacement); | 700 g_free(replacement); |
704 g_free(old); | 701 g_free(old); |
705 | 702 |
706 old = new; | 703 old = new; |
707 } | 704 } |
708 | 705 |
709 return new; | 706 return new; |
710 } | 707 } |
711 | 708 |
712 | 709 /** |
713 /** High-level function to convert MySpaceIM markup to Purple (HTML) markup. | 710 * High-level function to convert MySpaceIM markup to Purple (HTML) markup. |
714 * | 711 * |
715 * @return Purple markup string, must be g_free()'d. */ | 712 * @return Purple markup string, must be g_free()'d. */ |
716 gchar * | 713 gchar * |
717 msim_markup_to_html(MsimSession *session, const gchar *raw) | 714 msim_markup_to_html(MsimSession *session, const gchar *raw) |
718 { | 715 { |
719 return msim_convert_xml(session, raw, | 716 return msim_convert_xml(session, raw, msim_markup_tag_to_html); |
720 (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html)); | 717 } |
721 } | 718 |
722 | 719 /** |
723 /** High-level function to convert Purple (HTML) to MySpaceIM markup. | 720 * High-level function to convert Purple (HTML) to MySpaceIM markup. |
724 * | 721 * |
725 * TODO: consider using purple_markup_html_to_xhtml() to make valid XML. | 722 * TODO: consider using purple_markup_html_to_xhtml() to make valid XML. |
726 * | 723 * |
727 * @return HTML markup string, must be g_free()'d. */ | 724 * @return HTML markup string, must be g_free()'d. */ |
728 gchar * | 725 gchar * |
729 html_to_msim_markup(MsimSession *session, const gchar *raw) | 726 html_to_msim_markup(MsimSession *session, const gchar *raw) |
730 { | 727 { |
731 gchar *markup; | 728 gchar *markup; |
732 | 729 |
733 markup = msim_convert_xml(session, raw, | 730 markup = msim_convert_xml(session, raw, html_tag_to_msim_markup); |
734 (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup)); | 731 |
735 | |
736 if (purple_account_get_bool(session->account, "emoticons", TRUE)) { | 732 if (purple_account_get_bool(session->account, "emoticons", TRUE)) { |
737 /* Frees markup and allocates a new one. */ | 733 /* Frees markup and allocates a new one. */ |
738 markup = msim_convert_smileys_to_markup(markup); | 734 markup = msim_convert_smileys_to_markup(markup); |
739 } | 735 } |
740 | 736 |
741 return markup; | 737 return markup; |
742 } | 738 } |
743 | |
744 |