libpurple/protocols/myspace/markup.c

changeset 25145
5fdbb80f2e93
parent 25144
af2f709d543d
child 25146
20209c3c8b2e
equal deleted inserted replaced
25144:af2f709d543d 25145:5fdbb80f2e93
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 /* Globals */ 24 /* Globals */
25 25
26 /* 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
27 * entries in emoticon_symbols (for the ASCII representation of the emoticon). 27 * entries in emoticon_symbols (for the ASCII representation of the emoticon).
28 * 28 *
29 * 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
30 * canonical form, as inserted by the "Smile!" dialog, first. For example, 30 * canonical form, as inserted by the "Smile!" dialog, first. For example,
31 * :) comes before :-), because although both are recognized as 'happy', 31 * :) comes before :-), because although both are recognized as 'happy',
77 }; 77 };
78 78
79 79
80 80
81 /* Indexes of this array + 1 map HTML font size to scale of normal font size. * 81 /* Indexes of this array + 1 map HTML font size to scale of normal font size. *
82 * Based on _point_sizes from libpurple/gtkimhtml.c 82 * Based on _point_sizes from libpurple/gtkimhtml.c
83 * 1 2 3 4 5 6 7 */ 83 * 1 2 3 4 5 6 7 */
84 static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 }; 84 static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 };
85 85
86 #define MAX_FONT_SIZE 7 /* Purple maximum font size */ 86 #define MAX_FONT_SIZE 7 /* Purple maximum font size */
87 #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 */
88 88
89 /* Text formatting bits for <f s=#> */ 89 /* Text formatting bits for <f s=#> */
90 #define MSIM_TEXT_BOLD 1 90 #define MSIM_TEXT_BOLD 1
91 #define MSIM_TEXT_ITALIC 2 91 #define MSIM_TEXT_ITALIC 2
92 #define MSIM_TEXT_UNDERLINE 4 92 #define MSIM_TEXT_UNDERLINE 4
93 93
94 /* 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.
95 * _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
96 * is only the default; it is configurable in account options. */ 96 * is only the default; it is configurable in account options. */
97 #define MSIM_BASE_FONT_POINT_SIZE 8 97 #define MSIM_BASE_FONT_POINT_SIZE 8
98 98
99 /* 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
100 * in account options. */ 100 * in account options. */
112 return floor( value + 0.5); 112 return floor( value + 0.5);
113 } 113 }
114 } 114 }
115 115
116 116
117 /** Convert typographical font point size to HTML font size. 117 /** Convert typographical font point size to HTML font size.
118 * Based on libpurple/gtkimhtml.c */ 118 * Based on libpurple/gtkimhtml.c */
119 static guint 119 static guint
120 msim_point_to_purple_size(MsimSession *session, guint point) 120 msim_point_to_purple_size(MsimSession *session, guint point)
121 { 121 {
122 guint size, this_point, base; 122 guint size, this_point, base;
123 gdouble scale; 123 gdouble scale;
124 124
125 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); 125 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
126 126
127 for (size = 0; 127 for (size = 0;
128 size < sizeof(_font_scale) / sizeof(_font_scale[0]); 128 size < sizeof(_font_scale) / sizeof(_font_scale[0]);
129 ++size) { 129 ++size) {
130 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; 130 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1];
131 this_point = (guint)msim_round(scale * base); 131 this_point = (guint)msim_round(scale * base);
132 132
160 160
161 return point; 161 return point;
162 } 162 }
163 163
164 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ 164 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */
165 static guint 165 static guint
166 msim_height_to_point(MsimSession *session, guint height) 166 msim_height_to_point(MsimSession *session, guint height)
167 { 167 {
168 guint dpi; 168 guint dpi;
169 169
170 dpi = purple_account_get_int(session->account, "dpi", MSIM_DEFAULT_DPI); 170 dpi = purple_account_get_int(session->account, "dpi", MSIM_DEFAULT_DPI);
185 185
186 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); 186 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point);
187 } 187 }
188 188
189 /** Convert the msim markup <f> (font) tag into HTML. */ 189 /** Convert the msim markup <f> (font) tag into HTML. */
190 static void 190 static void
191 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 191 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
192 { 192 {
193 const gchar *face, *height_str, *decor_str; 193 const gchar *face, *height_str, *decor_str;
194 GString *gs_end, *gs_begin; 194 GString *gs_end, *gs_begin;
195 guint decor, height; 195 guint decor, height;
211 } 211 }
212 212
213 gs_begin = g_string_new(""); 213 gs_begin = g_string_new("");
214 /* TODO: get font size working */ 214 /* TODO: get font size working */
215 if (height && !face) { 215 if (height && !face) {
216 g_string_printf(gs_begin, "<font size='%d'>", 216 g_string_printf(gs_begin, "<font size='%d'>",
217 msim_point_to_purple_size(session, msim_height_to_point(session, height))); 217 msim_point_to_purple_size(session, msim_height_to_point(session, height)));
218 } else if (height && face) { 218 } else if (height && face) {
219 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face, 219 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face,
220 msim_point_to_purple_size(session, msim_height_to_point(session, height))); 220 msim_point_to_purple_size(session, msim_height_to_point(session, height)));
221 } else { 221 } else {
222 g_string_printf(gs_begin, "<font>"); 222 g_string_printf(gs_begin, "<font>");
223 } 223 }
224 224
225 /* No support for font-size CSS? */ 225 /* No support for font-size CSS? */
226 /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, 226 /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face,
227 msim_height_to_point(height)); */ 227 msim_height_to_point(height)); */
228 228
229 gs_end = g_string_new("</font>"); 229 gs_end = g_string_new("</font>");
230 230
231 if (decor & MSIM_TEXT_BOLD) { 231 if (decor & MSIM_TEXT_BOLD) {
247 *begin = g_string_free(gs_begin, FALSE); 247 *begin = g_string_free(gs_begin, FALSE);
248 *end = g_string_free(gs_end, FALSE); 248 *end = g_string_free(gs_end, FALSE);
249 } 249 }
250 250
251 /** Convert a msim markup color to a color suitable for libpurple. 251 /** Convert a msim markup color to a color suitable for libpurple.
252 * 252 *
253 * @param msim Either a color name, or an rgb(x,y,z) code. 253 * @param msim Either a color name, or an rgb(x,y,z) code.
254 * 254 *
255 * @return A new string, either a color name or #rrggbb code. Must g_free(). 255 * @return A new string, either a color name or #rrggbb code. Must g_free().
256 */ 256 */
257 static char * 257 static char *
258 msim_color_to_purple(const char *msim) 258 msim_color_to_purple(const char *msim)
259 { 259 {
260 guint red, green, blue; 260 guint red, green, blue;
261 261
271 271
272 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); 272 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue);
273 } 273 }
274 274
275 /** Convert the msim markup <a> (anchor) tag into HTML. */ 275 /** Convert the msim markup <a> (anchor) tag into HTML. */
276 static void 276 static void
277 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 277 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
278 { 278 {
279 const gchar *href; 279 const gchar *href;
280 280
281 href = xmlnode_get_attrib(root, "h"); 281 href = xmlnode_get_attrib(root, "h");
286 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href); 286 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href);
287 *end = g_strdup("</a>"); 287 *end = g_strdup("</a>");
288 } 288 }
289 289
290 /** Convert the msim markup <p> (paragraph) tag into HTML. */ 290 /** Convert the msim markup <p> (paragraph) tag into HTML. */
291 static void 291 static void
292 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 292 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
293 { 293 {
294 /* Just pass through unchanged. 294 /* Just pass through unchanged.
295 * 295 *
296 * Note: attributes currently aren't passed, if there are any. */ 296 * Note: attributes currently aren't passed, if there are any. */
297 *begin = g_strdup("<p>"); 297 *begin = g_strdup("<p>");
298 *end = g_strdup("</p>"); 298 *end = g_strdup("</p>");
299 } 299 }
300 300
301 /** Convert the msim markup <c> tag (text color) into HTML. TODO: Test */ 301 /** Convert the msim markup <c> tag (text color) into HTML. TODO: Test */
302 static void 302 static void
303 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 303 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
304 { 304 {
305 const gchar *color; 305 const gchar *color;
306 gchar *purple_color; 306 gchar *purple_color;
307 307
314 return; 314 return;
315 } 315 }
316 316
317 purple_color = msim_color_to_purple(color); 317 purple_color = msim_color_to_purple(color);
318 318
319 *begin = g_strdup_printf("<font color='%s'>", purple_color); 319 *begin = g_strdup_printf("<font color='%s'>", purple_color);
320 320
321 g_free(purple_color); 321 g_free(purple_color);
322 322
323 /* *begin = g_strdup_printf("<span style='color: %s'>", color); */ 323 /* *begin = g_strdup_printf("<span style='color: %s'>", color); */
324 *end = g_strdup("</font>"); 324 *end = g_strdup("</font>");
325 } 325 }
326 326
327 /** Convert the msim markup <b> tag (background color) into HTML. TODO: Test */ 327 /** Convert the msim markup <b> tag (background color) into HTML. TODO: Test */
328 static void 328 static void
329 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 329 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
330 { 330 {
331 const gchar *color; 331 const gchar *color;
332 gchar *purple_color; 332 gchar *purple_color;
333 333
341 } 341 }
342 342
343 purple_color = msim_color_to_purple(color); 343 purple_color = msim_color_to_purple(color);
344 344
345 /* TODO: find out how to set background color. */ 345 /* TODO: find out how to set background color. */
346 *begin = g_strdup_printf("<span style='background-color: %s'>", 346 *begin = g_strdup_printf("<span style='background-color: %s'>",
347 purple_color); 347 purple_color);
348 g_free(purple_color); 348 g_free(purple_color);
349 349
350 *end = g_strdup("</p>"); 350 *end = g_strdup("</p>");
351 } 351 }
352 352
353 /** Convert the msim markup <i> tag (emoticon image) into HTML. */ 353 /** Convert the msim markup <i> tag (emoticon image) into HTML. */
354 static void 354 static void
355 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 355 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
356 { 356 {
357 const gchar *name; 357 const gchar *name;
358 guint i; 358 guint i;
359 struct MSIM_EMOTICON *emote; 359 struct MSIM_EMOTICON *emote;
380 *begin = g_strdup_printf("**%s**", name); 380 *begin = g_strdup_printf("**%s**", name);
381 *end = g_strdup(""); 381 *end = g_strdup("");
382 } 382 }
383 383
384 /** Convert an individual msim markup tag to HTML. */ 384 /** Convert an individual msim markup tag to HTML. */
385 static int 385 static int
386 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, 386 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin,
387 gchar **end) 387 gchar **end)
388 { 388 {
389 g_return_val_if_fail(root != NULL, 0); 389 g_return_val_if_fail(root != NULL, 0);
390 390
391 if (g_str_equal(root->name, "f")) { 391 if (g_str_equal(root->name, "f")) {
400 msim_markup_b_to_html(session, root, begin, end); 400 msim_markup_b_to_html(session, root, begin, end);
401 } else if (g_str_equal(root->name, "i")) { 401 } else if (g_str_equal(root->name, "i")) {
402 msim_markup_i_to_html(session, root, begin, end); 402 msim_markup_i_to_html(session, root, begin, end);
403 } else { 403 } else {
404 purple_debug_info("msim", "msim_markup_tag_to_html: " 404 purple_debug_info("msim", "msim_markup_tag_to_html: "
405 "unknown tag name=%s, ignoring", 405 "unknown tag name=%s, ignoring",
406 root->name ? root->name : "(NULL)"); 406 root->name ? root->name : "(NULL)");
407 *begin = g_strdup(""); 407 *begin = g_strdup("");
408 *end = g_strdup(""); 408 *end = g_strdup("");
409 } 409 }
410 return 0; 410 return 0;
411 } 411 }
412 412
413 /** Convert an individual HTML tag to msim markup. */ 413 /** Convert an individual HTML tag to msim markup. */
414 static int 414 static int
415 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, 415 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin,
416 gchar **end) 416 gchar **end)
417 { 417 {
418 int ret = 0; 418 int ret = 0;
419 419
420 if (!purple_utf8_strcasecmp(root->name, "root") || 420 if (!purple_utf8_strcasecmp(root->name, "root") ||
421 !purple_utf8_strcasecmp(root->name, "html")) { 421 !purple_utf8_strcasecmp(root->name, "html")) {
422 *begin = g_strdup(""); 422 *begin = g_strdup("");
423 *end = g_strdup(""); 423 *end = g_strdup("");
424 /* TODO: Coalesce nested tags into one <f> tag! 424 /* TODO: Coalesce nested tags into one <f> tag!
425 * Currently, the 's' value will be overwritten when b/i/u is nested 425 * Currently, the 's' value will be overwritten when b/i/u is nested
426 * within another one, and only the inner-most formatting will be 426 * within another one, and only the inner-most formatting will be
427 * applied to the text. */ 427 * applied to the text. */
428 } else if (!purple_utf8_strcasecmp(root->name, "b")) { 428 } else if (!purple_utf8_strcasecmp(root->name, "b")) {
429 if (root->child->type == XMLNODE_TYPE_DATA) { 429 if (root->child->type == XMLNODE_TYPE_DATA) {
430 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); 430 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD);
431 *end = g_strdup("</f>"); 431 *end = g_strdup("</f>");
506 506
507 size = xmlnode_get_attrib(root, "size"); 507 size = xmlnode_get_attrib(root, "size");
508 face = xmlnode_get_attrib(root, "face"); 508 face = xmlnode_get_attrib(root, "face");
509 509
510 if (face && size) { 510 if (face && size) {
511 *begin = g_strdup_printf("<f f='%s' h='%d'>", face, 511 *begin = g_strdup_printf("<f f='%s' h='%d'>", face,
512 msim_point_to_height(session, 512 msim_point_to_height(session,
513 msim_purple_size_to_point(session, atoi(size)))); 513 msim_purple_size_to_point(session, atoi(size))));
514 } else if (face) { 514 } else if (face) {
515 *begin = g_strdup_printf("<f f='%s'>", face); 515 *begin = g_strdup_printf("<f f='%s'>", face);
516 } else if (size) { 516 } else if (size) {
517 *begin = g_strdup_printf("<f h='%d'>", 517 *begin = g_strdup_printf("<f h='%d'>",
518 msim_point_to_height(session, 518 msim_point_to_height(session,
519 msim_purple_size_to_point(session, atoi(size)))); 519 msim_purple_size_to_point(session, atoi(size))));
520 } else { 520 } else {
521 *begin = g_strdup("<f>"); 521 *begin = g_strdup("<f>");
522 } 522 }
534 *begin = g_strdup(""); 534 *begin = g_strdup("");
535 *end = g_strdup(""); 535 *end = g_strdup("");
536 #endif 536 #endif
537 537
538 err = g_strdup_printf("html_tag_to_msim_markup: unrecognized " 538 err = g_strdup_printf("html_tag_to_msim_markup: unrecognized "
539 "HTML tag %s was sent by the IM client; ignoring", 539 "HTML tag %s was sent by the IM client; ignoring",
540 root->name ? root->name : "(NULL)"); 540 root->name ? root->name : "(NULL)");
541 msim_unrecognized(NULL, NULL, err); 541 msim_unrecognized(NULL, NULL, err);
542 g_free(err); 542 g_free(err);
543 } 543 }
544 return ret; 544 return ret;
569 569
570 final = g_string_new(""); 570 final = g_string_new("");
571 571
572 if (descended == 0) /* We've not formatted this yet.. :) */ 572 if (descended == 0) /* We've not formatted this yet.. :) */
573 descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */ 573 descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */
574 574
575 g_string_append(final, begin); 575 g_string_append(final, begin);
576 576
577 /* Loop over all child nodes. */ 577 /* Loop over all child nodes. */
578 for (node = root->child; node != NULL; node = node->next) { 578 for (node = root->child; node != NULL; node = node->next) {
579 switch (node->type) { 579 switch (node->type) {
583 583
584 case XMLNODE_TYPE_TAG: 584 case XMLNODE_TYPE_TAG:
585 /* A tag or tag with attributes. Recursively descend. */ 585 /* A tag or tag with attributes. Recursively descend. */
586 inner = msim_convert_xmlnode(session, node, f, descended); 586 inner = msim_convert_xmlnode(session, node, f, descended);
587 g_return_val_if_fail(inner != NULL, NULL); 587 g_return_val_if_fail(inner != NULL, NULL);
588 588
589 purple_debug_info("msim", " ** node name=%s\n", 589 purple_debug_info("msim", " ** node name=%s\n",
590 (node && node->name) ? node->name : "(NULL)"); 590 (node && node->name) ? node->name : "(NULL)");
591 break; 591 break;
592 592
593 case XMLNODE_TYPE_DATA: 593 case XMLNODE_TYPE_DATA:
594 /* Literal text. */ 594 /* Literal text. */
595 inner = g_strndup(node->data, node->data_sz); 595 inner = g_strndup(node->data, node->data_sz);
596 purple_debug_info("msim", " ** node data=%s\n", 596 purple_debug_info("msim", " ** node data=%s\n",
597 inner ? inner : "(NULL)"); 597 inner ? inner : "(NULL)");
598 break; 598 break;
599 599
600 default: 600 default:
601 purple_debug_info("msim", 601 purple_debug_info("msim",
602 "msim_convert_xmlnode: strange node\n"); 602 "msim_convert_xmlnode: strange node\n");
603 } 603 }
604 604
680 symbol = emote->symbol; 680 symbol = emote->symbol;
681 681
682 replacement = g_strdup_printf("<i n=\"%s\"/>", name); 682 replacement = g_strdup_printf("<i n=\"%s\"/>", name);
683 683
684 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", 684 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n",
685 symbol ? symbol : "(NULL)", 685 symbol ? symbol : "(NULL)",
686 replacement ? replacement : "(NULL)"); 686 replacement ? replacement : "(NULL)");
687 new = purple_strreplace(old, symbol, replacement); 687 new = purple_strreplace(old, symbol, replacement);
688 688
689 g_free(replacement); 689 g_free(replacement);
690 g_free(old); 690 g_free(old);
691 691
692 old = new; 692 old = new;
693 } 693 }
694 694
695 return new; 695 return new;
696 } 696 }
697 697
698 698 /** High-level function to convert MySpaceIM markup to Purple (HTML) markup.
699 /** High-level function to convert MySpaceIM markup to Purple (HTML) markup.
700 * 699 *
701 * @return Purple markup string, must be g_free()'d. */ 700 * @return Purple markup string, must be g_free()'d. */
702 gchar * 701 gchar *
703 msim_markup_to_html(MsimSession *session, const gchar *raw) 702 msim_markup_to_html(MsimSession *session, const gchar *raw)
704 { 703 {
705 return msim_convert_xml(session, raw, 704 return msim_convert_xml(session, raw,
706 (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html)); 705 (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html));
707 } 706 }
708 707
709 /** High-level function to convert Purple (HTML) to MySpaceIM markup. 708 /** High-level function to convert Purple (HTML) to MySpaceIM markup.
710 * 709 *
716 { 715 {
717 gchar *markup; 716 gchar *markup;
718 717
719 markup = msim_convert_xml(session, raw, 718 markup = msim_convert_xml(session, raw,
720 (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup)); 719 (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup));
721 720
722 if (purple_account_get_bool(session->account, "emoticons", TRUE)) { 721 if (purple_account_get_bool(session->account, "emoticons", TRUE)) {
723 /* Frees markup and allocates a new one. */ 722 /* Frees markup and allocates a new one. */
724 markup = msim_convert_smileys_to_markup(markup); 723 markup = msim_convert_smileys_to_markup(markup);
725 } 724 }
726 725
727 return markup; 726 return markup;
728 } 727 }
729
730

mercurial