1 /*
2 * html.c
3 *
4 * Copyright (C) 1999, 2000 Rasca, Berlin
5 * EMail: thron@gmx.de
6 * Copyright (c) 2001-2013 Andreas J. Guelzow
7 * EMail: aguelzow@pyrshep.ca
8 * Copyright 2013 Morten Welinder <terra@gnone.org>
9 *
10 * Contributors :
11 * Almer. S. Tigelaar <almer1@dds.nl>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses/>.
25 */
26
27 #include <gnumeric-config.h>
28 #include <gnumeric.h>
29 #include <goffice/goffice.h>
30 #include <workbook-view.h>
31 #include <workbook.h>
32 #include <sheet-style.h>
33 #include <style.h>
34 #include <style-color.h>
35 #include "html.h"
36 #include <cell.h>
37 #include <sheet.h>
38 #include <sheet-merge.h>
39 #include <value.h>
40 #include "font.h"
41 #include <cellspan.h>
42 #include <style-border.h>
43 #include <rendered-value.h>
44 #include <style.h>
45 #include <hlink.h>
46 #include <gutils.h>
47
48 #include <gsf/gsf-output.h>
49 #include <string.h>
50
51 /*
52 * html_version_t:
53 *
54 * version selector
55 *
56 */
57 typedef enum {
58 HTML40 = 0,
59 HTML32 = 1,
60 HTML40F = 2,
61 XHTML = 3
62 } html_version_t;
63
64 /*
65 * html_print_encoded:
66 *
67 * @output: the stream
68 * @str: the string
69 *
70 * print the string to output encoded all special chars
71 *
72 */
73 static void
html_print_encoded(GsfOutput * output,char const * str)74 html_print_encoded (GsfOutput *output, char const *str)
75 {
76 gunichar c;
77
78 if (str == NULL)
79 return;
80 for (; *str != '\0' ; str = g_utf8_next_char (str)) {
81 switch (*str) {
82 case '<':
83 gsf_output_puts (output, "<");
84 break;
85 case '>':
86 gsf_output_puts (output, ">");
87 break;
88 case '&':
89 gsf_output_puts (output, "&");
90 break;
91 case '\"':
92 gsf_output_puts (output, """);
93 break;
94 case '\n':
95 gsf_output_puts (output, "<br>\n");
96 break;
97 case '\r':
98 gsf_output_puts (output, "<br>\r");
99 if( *(str+1) == '\n' ) {
100 gsf_output_puts (output, "\n");
101 str++;
102 }
103 break;
104 default:
105 c = g_utf8_get_char (str);
106 if (((c >= 0x20) && (c < 0x80)) ||
107 (c == '\n') || (c == '\r') || (c == '\t'))
108 gsf_output_write (output, 1, str);
109 else
110 gsf_output_printf (output, "&#%u;", c);
111 break;
112 }
113 }
114 }
115
116 static void
html_get_text_color(GnmCell * cell,GnmStyle const * style,guint * r,guint * g,guint * b)117 html_get_text_color (GnmCell *cell, GnmStyle const *style, guint *r, guint *g, guint *b)
118 {
119 GOColor fore = gnm_cell_get_render_color (cell);
120
121 if (fore == 0)
122 *r = *g = *b = 0;
123 else {
124 *r = GO_COLOR_UINT_R (fore);
125 *g = GO_COLOR_UINT_G (fore);
126 *b = GO_COLOR_UINT_B (fore);
127 }
128 }
129 static void
html_get_back_color(GnmStyle const * style,guint * r,guint * g,guint * b)130 html_get_back_color (GnmStyle const *style, guint *r, guint *g, guint *b)
131 {
132 GnmColor const *color = gnm_style_get_back_color (style);
133 *r = GO_COLOR_UINT_R (color->go_color);
134 *g = GO_COLOR_UINT_G (color->go_color);
135 *b = GO_COLOR_UINT_B (color->go_color);
136 }
137
138 /*****************************************************************************/
139
140
141 static void
cb_html_add_chars(GsfOutput * output,char const * text,int len)142 cb_html_add_chars (GsfOutput *output, char const *text, int len)
143 {
144 char * str;
145
146 g_return_if_fail (len > 0);
147
148 str = g_strndup (text, len);
149 html_print_encoded (output, str);
150 g_free (str);
151 }
152
153 static char const *
cb_html_attrs_as_string(GsfOutput * output,PangoAttribute * a,html_version_t version)154 cb_html_attrs_as_string (GsfOutput *output, PangoAttribute *a, html_version_t version)
155 {
156 /* PangoColor const *c; */
157 char const *closure = NULL;
158
159 switch (a->klass->type) {
160 case PANGO_ATTR_FAMILY :
161 break; /* ignored */
162 case PANGO_ATTR_SIZE :
163 break; /* ignored */
164 case PANGO_ATTR_RISE:
165 if (((PangoAttrInt *)a)->value > 5) {
166 gsf_output_puts (output, "<sup>");
167 closure = "</sup>";
168 } else if (((PangoAttrInt *)a)->value < -5) {
169 gsf_output_puts (output, "<sub>");
170 closure = "</sub>";
171 }
172 break;
173 case PANGO_ATTR_STYLE :
174 if (((PangoAttrInt *)a)->value == PANGO_STYLE_ITALIC) {
175 gsf_output_puts (output, "<i>");
176 closure = "</i>";
177 }
178 break;
179 case PANGO_ATTR_WEIGHT :
180 if (((PangoAttrInt *)a)->value > 600){
181 gsf_output_puts (output, "<b>");
182 closure = "</b>";
183 }
184 break;
185 case PANGO_ATTR_STRIKETHROUGH :
186 if (((PangoAttrInt *)a)->value == 1) {
187 if (version == HTML32) {
188 gsf_output_puts (output, "<strike>");
189 closure = "</strike>";
190 } else {
191 gsf_output_puts
192 (output,
193 "<span style=\"text-decoration: "
194 "line-through;\">");
195 closure = "</span>";
196 }
197 }
198 break;
199 case PANGO_ATTR_UNDERLINE :
200 if ((version != HTML40) &&
201 (((PangoAttrInt *)a)->value != PANGO_UNDERLINE_NONE)) {
202 gsf_output_puts (output, "<u>");
203 closure = "</u>";
204 }
205 break;
206 case PANGO_ATTR_FOREGROUND :
207 /* c = &((PangoAttrColor *)a)->color; */
208 /* g_string_append_printf (accum, "[color=%02xx%02xx%02x", */
209 /* ((c->red & 0xff00) >> 8), */
210 /* ((c->green & 0xff00) >> 8), */
211 /* ((c->blue & 0xff00) >> 8)); */
212 break;/* ignored */
213 default :
214 if (a->klass->type ==
215 go_pango_attr_subscript_get_attr_type ()) {
216 if (((GOPangoAttrSubscript *)a)->val) {
217 gsf_output_puts (output, "<sub>");
218 closure = "</sub>";
219 }
220 } else if (a->klass->type ==
221 go_pango_attr_superscript_get_attr_type ()) {
222 if (((GOPangoAttrSuperscript *)a)->val) {
223 gsf_output_puts (output, "<sup>");
224 closure = "</sup>";
225 }
226 }
227 break; /* ignored */
228 }
229
230 return closure;
231 }
232
233 static void
html_new_markup(GsfOutput * output,const PangoAttrList * markup,char const * text,html_version_t version)234 html_new_markup (GsfOutput *output, const PangoAttrList *markup, char const *text,
235 html_version_t version)
236 {
237 int handled = 0;
238 PangoAttrIterator * iter;
239 int from, to;
240 int len = strlen (text);
241 GString *closure = g_string_new ("");
242
243 iter = pango_attr_list_get_iterator ((PangoAttrList *) markup);
244
245 do {
246 GSList *list, *l;
247
248 g_string_erase (closure, 0, -1);
249 pango_attr_iterator_range (iter, &from, &to);
250 from = (from > len) ? len : from; /* Since "from" can be really big! */
251 to = (to > len) ? len : to; /* Since "to" can be really big! */
252 if (from > handled)
253 cb_html_add_chars (output, text + handled, from - handled);
254 list = pango_attr_iterator_get_attrs (iter);
255 for (l = list; l != NULL; l = l->next) {
256 char const *result = cb_html_attrs_as_string (output, l->data, version);
257 if (result != NULL)
258 g_string_prepend (closure, result);
259 }
260 g_slist_free (list);
261 if (to > from)
262 cb_html_add_chars (output, text + from, to - from);
263 gsf_output_puts (output, closure->str);
264 handled = to;
265 } while (pango_attr_iterator_next (iter));
266
267 g_string_free (closure, TRUE);
268 pango_attr_iterator_destroy (iter);
269
270 return;
271 }
272
273
274 /*****************************************************************************/
275
276 static void
html_write_cell_content(GsfOutput * output,GnmCell * cell,GnmStyle const * style,html_version_t version)277 html_write_cell_content (GsfOutput *output, GnmCell *cell, GnmStyle const *style, html_version_t version)
278 {
279 guint r = 0;
280 guint g = 0;
281 guint b = 0;
282 char *rendered_string;
283 gboolean hidden = gnm_style_get_contents_hidden (style);
284 GnmHLink* hlink = gnm_style_get_hlink (style);
285 char* hlink_target = NULL;
286
287 if (hlink) {
288 const char *target = gnm_hlink_get_target (hlink);
289 if (GNM_IS_HLINK_URL (hlink)) {
290 hlink_target = go_url_encode (target, 1);
291 } else if (GNM_IS_HLINK_EXTERNAL (hlink)) {
292 char *et = go_url_encode (target, 1);
293 hlink_target = g_strconcat ("file://", et, NULL);
294 g_free (et);
295 }
296 }
297
298 if (version == HTML32 && hidden)
299 gsf_output_puts (output, "<!-- 'HIDDEN DATA' -->");
300 else {
301 if (style != NULL) {
302 if (gnm_style_get_font_italic (style))
303 gsf_output_puts (output, "<i>");
304 if (gnm_style_get_font_bold (style))
305 gsf_output_puts (output, "<b>");
306 if (gnm_style_get_font_uline (style) != UNDERLINE_NONE)
307 gsf_output_puts (output, "<u>");
308 if (font_is_monospaced (style))
309 gsf_output_puts (output, "<tt>");
310 if (gnm_style_get_font_strike (style)) {
311 if (version == HTML32)
312 gsf_output_puts (output, "<strike>");
313 else
314 gsf_output_puts (output,
315 "<span style=\"text-decoration: line-through;\">");
316 }
317 switch (gnm_style_get_font_script (style)) {
318 case GO_FONT_SCRIPT_SUB:
319 gsf_output_puts (output, "<sub>");
320 break;
321 case GO_FONT_SCRIPT_SUPER:
322 gsf_output_puts (output, "<sup>");
323 break;
324 default:
325 break;
326 }
327 }
328
329 if (hlink_target) {
330 gsf_output_printf (output, "<a href=\"%s\">", hlink_target);
331 g_free (hlink_target);
332 }
333
334 if (cell != NULL) {
335 const PangoAttrList * markup = NULL;
336
337 if (style != NULL && version != HTML40) {
338 html_get_text_color (cell, style, &r, &g, &b);
339 if (r > 0 || g > 0 || b > 0)
340 gsf_output_printf (output, "<font color=\"#%02X%02X%02X\">", r, g, b);
341 }
342
343 if (VALUE_IS_STRING (cell->value)
344 && (VALUE_FMT (cell->value) != NULL)
345 && go_format_is_markup (VALUE_FMT (cell->value)))
346 markup = go_format_get_markup (VALUE_FMT (cell->value));
347
348 if (markup != NULL) {
349 GString *str = g_string_new ("");
350 value_get_as_gstring (cell->value, str, NULL);
351 html_new_markup (output, markup, str->str, version);
352 g_string_free (str, TRUE);
353 } else {
354 rendered_string = gnm_cell_get_rendered_text (cell);
355 html_print_encoded (output, rendered_string);
356 g_free (rendered_string);
357 }
358 }
359
360 if (r > 0 || g > 0 || b > 0)
361 gsf_output_puts (output, "</font>");
362 if (hlink_target)
363 gsf_output_puts (output, "</a>");
364 if (style != NULL) {
365 if (gnm_style_get_font_strike (style)) {
366 if (version == HTML32)
367 gsf_output_puts (output, "</strike>");
368 else
369 gsf_output_puts (output, "</span>");
370 }
371 switch (gnm_style_get_font_script (style)) {
372 case GO_FONT_SCRIPT_SUB:
373 gsf_output_puts (output, "</sub>");
374 break;
375 case GO_FONT_SCRIPT_SUPER:
376 gsf_output_puts (output, "</sup>");
377 break;
378 default:
379 break;
380 }
381 if (font_is_monospaced (style))
382 gsf_output_puts (output, "</tt>");
383 if (gnm_style_get_font_uline (style) != UNDERLINE_NONE)
384 gsf_output_puts (output, "</u>");
385 if (gnm_style_get_font_bold (style))
386 gsf_output_puts (output, "</b>");
387 if (gnm_style_get_font_italic (style))
388 gsf_output_puts (output, "</i>");
389 }
390 }
391 }
392
393 static char *
html_get_border_style(GnmBorder * border)394 html_get_border_style (GnmBorder *border)
395 {
396 GString *text = g_string_new (NULL);
397 char *result;
398
399 switch (border->line_type) {
400 case GNM_STYLE_BORDER_THIN:
401 g_string_append (text, "thin solid");
402 break;
403 case GNM_STYLE_BORDER_MEDIUM:
404 g_string_append (text, "medium solid");
405 break;
406 case GNM_STYLE_BORDER_DASHED:
407 g_string_append (text, "thin dashed");
408 break;
409 case GNM_STYLE_BORDER_DOTTED:
410 g_string_append (text, "thin dotted");
411 break;
412 case GNM_STYLE_BORDER_THICK:
413 g_string_append (text, "thick solid");
414 break;
415 case GNM_STYLE_BORDER_DOUBLE:
416 g_string_append (text, "thick double");
417 break;
418 case GNM_STYLE_BORDER_HAIR:
419 g_string_append (text, "0.5pt solid");
420 break;
421 case GNM_STYLE_BORDER_MEDIUM_DASH:
422 g_string_append (text, "medium dashed");
423 break;
424 case GNM_STYLE_BORDER_DASH_DOT:
425 g_string_append (text, "thin dashed");
426 break;
427 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT:
428 g_string_append (text, "medium dashed");
429 break;
430 case GNM_STYLE_BORDER_DASH_DOT_DOT:
431 g_string_append (text, "thin dotted");
432 break;
433 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT:
434 g_string_append (text, "medium dotted");
435 break;
436 case GNM_STYLE_BORDER_SLANTED_DASH_DOT:
437 g_string_append (text, "thin dashed");
438 break;
439 default:
440 break;
441 }
442
443 if (border->color) {
444 guint r, g, b;
445 r = GO_COLOR_UINT_R (border->color->go_color);
446 g = GO_COLOR_UINT_G (border->color->go_color);
447 b = GO_COLOR_UINT_B (border->color->go_color);
448 g_string_append_printf (text, " #%02X%02X%02X", r, g, b);
449 }
450
451 result = text->str;
452 g_string_free (text, FALSE);
453 return result;
454 }
455
456 static void
html_write_one_border_style_40(GsfOutput * output,GnmBorder * border,char const * border_name)457 html_write_one_border_style_40 (GsfOutput *output, GnmBorder *border, char const *border_name)
458 {
459 char *text;
460 text = html_get_border_style (border);
461 if (text == NULL || strlen (text) == 0)
462 return;
463 gsf_output_printf (output, " %s:%s;", border_name, text);
464 g_free (text);
465 }
466
467 static void
html_write_border_style_40(GsfOutput * output,GnmStyle const * style)468 html_write_border_style_40 (GsfOutput *output, GnmStyle const *style)
469 {
470 GnmBorder *border;
471
472 border = gnm_style_get_border (style, MSTYLE_BORDER_TOP);
473 if (!gnm_style_border_is_blank (border))
474 html_write_one_border_style_40 (output, border, "border-top");
475 border = gnm_style_get_border (style, MSTYLE_BORDER_BOTTOM);
476 if (!gnm_style_border_is_blank (border))
477 html_write_one_border_style_40 (output, border, "border-bottom");
478 border = gnm_style_get_border (style, MSTYLE_BORDER_LEFT);
479 if (!gnm_style_border_is_blank (border))
480 html_write_one_border_style_40 (output, border, "border-left");
481 border = gnm_style_get_border (style, MSTYLE_BORDER_RIGHT);
482 if (!gnm_style_border_is_blank (border))
483 html_write_one_border_style_40 (output, border, "border-right");
484 }
485
486 static void
html_write_border_style_40_for_merged_cell(GsfOutput * output,GnmStyle const * style,Sheet * sheet,gint row,gint col)487 html_write_border_style_40_for_merged_cell (GsfOutput *output, GnmStyle const *style,
488 Sheet *sheet, gint row, gint col)
489 {
490 GnmBorder *border;
491 GnmRange const *merge_range;
492 GnmCellPos pos;
493 pos.col = col;
494 pos.row = row;
495
496
497 border = gnm_style_get_border (style, MSTYLE_BORDER_TOP);
498 if (!gnm_style_border_is_blank (border))
499 html_write_one_border_style_40 (output, border, "border-top");
500 border = gnm_style_get_border (style, MSTYLE_BORDER_LEFT);
501 if (!gnm_style_border_is_blank (border))
502 html_write_one_border_style_40 (output, border, "border-left");
503
504 merge_range = gnm_sheet_merge_contains_pos (sheet, &pos);
505 if (merge_range != NULL) {
506 style = sheet_style_get (sheet, merge_range->end.col, merge_range->end.row);
507 if (style == NULL)
508 return;
509 }
510
511 border = gnm_style_get_border (style, MSTYLE_BORDER_BOTTOM);
512 if (!gnm_style_border_is_blank (border))
513 html_write_one_border_style_40 (output, border, "border-bottom");
514 border = gnm_style_get_border (style, MSTYLE_BORDER_RIGHT);
515 if (!gnm_style_border_is_blank (border))
516 html_write_one_border_style_40 (output, border, "border-right");
517 }
518
519 static void
write_cell(GsfOutput * output,Sheet * sheet,gint row,gint col,html_version_t version,gboolean is_merge)520 write_cell (GsfOutput *output, Sheet *sheet, gint row, gint col, html_version_t version, gboolean is_merge)
521 {
522 GnmCell *cell;
523 GnmStyle const *style;
524 guint r, g, b;
525
526 style = sheet_style_get (sheet, col, row);
527 if (style != NULL && version != HTML32 && version != HTML40 &&
528 gnm_style_get_pattern (style) != 0 &&
529 gnm_style_is_element_set (style, MSTYLE_COLOR_BACK)) {
530 html_get_back_color (style, &r, &g, &b);
531 gsf_output_printf (output, " bgcolor=\"#%02X%02X%02X\"", r, g, b);
532 }
533
534 cell = sheet_cell_get (sheet, col, row);
535 if (cell != NULL) {
536
537 switch (gnm_style_get_align_v (style)) {
538 case GNM_VALIGN_TOP:
539 gsf_output_puts (output, " valign=\"top\" ");
540 break;
541 case GNM_VALIGN_BOTTOM:
542 gsf_output_puts (output, " valign=\"bottom\" ");
543 break;
544 case GNM_VALIGN_DISTRIBUTED:
545 case GNM_VALIGN_CENTER:
546 gsf_output_puts (output, " valign=\"center\" ");
547 break;
548 case GNM_VALIGN_JUSTIFY:
549 gsf_output_puts (output, " valign=\"baseline\" ");
550 break;
551 default:
552 break;
553 }
554 switch (gnm_style_default_halign (style, cell)) {
555 case GNM_HALIGN_RIGHT:
556 gsf_output_puts (output, " align=\"right\" ");
557 break;
558 case GNM_HALIGN_DISTRIBUTED:
559 case GNM_HALIGN_CENTER:
560 case GNM_HALIGN_CENTER_ACROSS_SELECTION:
561 gsf_output_puts (output, " align=\"center\" ");
562 break;
563 case GNM_HALIGN_LEFT:
564 gsf_output_puts (output, " align=\"left\" ");
565 break;
566 case GNM_HALIGN_JUSTIFY:
567 gsf_output_puts (output, " align=\"justify\" ");
568 break;
569 default:
570 break;
571 }
572
573 }
574 if (version == HTML40 || version == HTML40F || version ==XHTML) {
575 if (style != NULL) {
576 gsf_output_printf (output, " style=\"");
577 if (gnm_style_get_pattern (style) != 0 &&
578 gnm_style_is_element_set (style, MSTYLE_COLOR_BACK)) {
579 html_get_back_color (style, &r, &g, &b);
580 gsf_output_printf (output, "background:#%02X%02X%02X;", r, g, b);
581 }
582 if (cell != NULL) {
583 gint size = (int) (gnm_style_get_font_size (style) + 0.5);
584 gsf_output_printf (output, " font-size:%ipt;", size);
585 html_get_text_color (cell, style, &r, &g, &b);
586 if (r > 0 || g > 0 || b > 0)
587 gsf_output_printf (output, " color:#%02X%02X%02X;", r, g, b);
588 if (gnm_style_get_contents_hidden (style))
589 gsf_output_puts (output, " visibility:hidden;");
590 }
591 if (is_merge)
592 html_write_border_style_40_for_merged_cell (output, style, sheet, row, col);
593 else
594 html_write_border_style_40 (output, style);
595 gsf_output_printf (output, "\"");
596 }
597 }
598 gsf_output_printf (output, ">");
599 html_write_cell_content (output, cell, style, version);
600 gsf_output_puts (output, "</td>\n");
601 }
602
603
604 /*
605 * write_row:
606 *
607 * @output: the stream
608 * @sheet: the gnumeric sheet
609 * @row: the row number
610 *
611 * Set up a TD node for each cell in the given row, witht eh appropriate
612 * colspan and rowspan.
613 * Call write_cell for each cell.
614 */
615 static void
write_row(GsfOutput * output,Sheet * sheet,gint row,GnmRange * range,html_version_t version)616 write_row (GsfOutput *output, Sheet *sheet, gint row, GnmRange *range, html_version_t version)
617 {
618 gint col;
619 ColRowInfo const *ri = sheet_row_get_info (sheet, row);
620 if (ri->needs_respan)
621 row_calc_spans ((ColRowInfo *) ri, row, sheet);
622
623 for (col = range->start.col; col <= range->end.col; col++) {
624 CellSpanInfo const *the_span;
625 GnmRange const *merge_range;
626 GnmCellPos pos;
627 pos.col = col;
628 pos.row = row;
629
630 /* Is this a span */
631 the_span = row_span_get (ri, col);
632 if (the_span != NULL) {
633 gsf_output_printf (output, "<td colspan=\"%i\" ", the_span->right - col + 1);
634 write_cell (output, sheet, row, the_span->cell->pos.col, version, FALSE);
635 col = the_span->right;
636 continue;
637 }
638
639 /* is this covered by a merge */
640 merge_range = gnm_sheet_merge_contains_pos (sheet, &pos);
641 if (merge_range != NULL) {
642 if (merge_range->start.col != col ||
643 merge_range->start.row != row)
644 continue;
645 gsf_output_printf (output, "<td colspan=\"%i\" rowspan=\"%i\" ",
646 merge_range->end.col - merge_range->start.col + 1,
647 merge_range->end.row - merge_range->start.row + 1);
648 write_cell (output, sheet, row, col, version, TRUE);
649 col = merge_range->end.col;
650 continue;
651 }
652 gsf_output_puts (output, "<td ");
653 write_cell (output, sheet, row, col, version, FALSE);
654 }
655 }
656
657 /*
658 * write_sheet:
659 *
660 * @output: the stream
661 * @sheet: the gnumeric sheet
662 *
663 * set up a table and call write_row for each row
664 */
665 static void
write_sheet(GsfOutput * output,Sheet * sheet,html_version_t version,GOFileSaveScope save_scope)666 write_sheet (GsfOutput *output, Sheet *sheet,
667 html_version_t version, GOFileSaveScope save_scope)
668 {
669 GnmRange total_range;
670 gint row;
671
672 switch (version) {
673 case HTML40:
674 case HTML40F:
675 case XHTML:
676 gsf_output_puts (output, "<p></p><table cellspacing=\"0\" cellpadding=\"3\">\n");
677 break;
678 default:
679 gsf_output_puts (output, "<p><table border=\"1\">\n");
680 break;
681 }
682
683 if (save_scope != GO_FILE_SAVE_RANGE) {
684 gsf_output_puts (output, "<caption>");
685 html_print_encoded (output, sheet->name_unquoted);
686 gsf_output_puts (output, "</caption>\n");
687 }
688 total_range = sheet_get_extent (sheet, TRUE, TRUE);
689 for (row = total_range.start.row; row <= total_range.end.row; row++) {
690 gsf_output_puts (output, "<tr>\n");
691 write_row (output, sheet, row, &total_range, version);
692 gsf_output_puts (output, "</tr>\n");
693 }
694 gsf_output_puts (output, "</table>\n");
695 }
696
697 /*
698 * html_file_save:
699 *
700 * write the html file (version of html according to version argument)
701 */
702 static void
html_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wb_view,GsfOutput * output,html_version_t version)703 html_file_save (GOFileSaver const *fs, GOIOContext *io_context,
704 WorkbookView const *wb_view, GsfOutput *output, html_version_t version)
705 {
706 Workbook *wb = wb_view_get_workbook (wb_view);
707 GOFileSaveScope save_scope;
708 GPtrArray *sel;
709 unsigned ui;
710
711 g_return_if_fail (fs != NULL);
712 g_return_if_fail (wb != NULL);
713 g_return_if_fail (output != NULL);
714
715 switch (version) {
716 case HTML32:
717 gsf_output_puts (output,
718 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
719 "<html>\n"
720 "<head>\n\t<title>Tables</title>\n"
721 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
722 "<meta name=\"generator\" content=\"Gnumeric " GNM_VERSION_FULL " via " G_PLUGIN_FOR_HTML "\">\n"
723 "<style><!--\n"
724 "tt {\n"
725 "\tfont-family: courier;\n"
726 "}\n"
727 "td {\n"
728 "\tfont-family: helvetica, sans-serif;\n"
729 "}\n"
730 "caption {\n"
731 "\tfont-family: helvetica, sans-serif;\n"
732 "\tfont-size: 14pt;\n"
733 "\ttext-align: left;\n"
734 "}\n"
735 "--></style>\n"
736 "</head>\n<body>\n");
737 break;
738 case HTML40:
739 gsf_output_puts (output,
740 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n"
741 "\t\t\"http://www.w3.org/TR/html4/strict.dtd\">\n"
742 "<html>\n"
743 "<head>\n\t<title>Tables</title>\n"
744 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
745 "<meta name=\"generator\" content=\"Gnumeric " GNM_VERSION_FULL " via " G_PLUGIN_FOR_HTML "\">\n"
746 "<style type=\"text/css\">\n"
747 "tt {\n"
748 "\tfont-family: courier;\n"
749 "}\n"
750 "td {\n"
751 "\tfont-family: helvetica, sans-serif;\n"
752 "}\n"
753 "caption {\n"
754 "\tfont-family: helvetica, sans-serif;\n"
755 "\tfont-size: 14pt;\n"
756 "\ttext-align: left;\n"
757 "}\n"
758 "</style>\n"
759 "</head>\n<body>\n");
760 break;
761 case XHTML :
762 gsf_output_puts (output,
763 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
764 "\t\t\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
765 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
766 "<head>\n\t<title>Tables</title>\n"
767 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
768 "<meta name=\"generator\" content=\"Gnumeric " GNM_VERSION_FULL " via " G_PLUGIN_FOR_HTML "\" />\n"
769 "<style type=\"text/css\">\n"
770 "tt {\n"
771 "\tfont-family: courier;\n"
772 "}\n"
773 "td {\n"
774 "\tfont-family: helvetica, sans-serif;\n"
775 "}\n"
776 "caption {\n"
777 "\tfont-family: helvetica, sans-serif;\n"
778 "\tfont-size: 14pt;\n"
779 "\ttext-align: left;\n"
780 "}\n"
781 "</style>\n"
782 "</head>\n<body>\n");
783 break;
784 default:
785 break;
786 }
787
788 save_scope = go_file_saver_get_save_scope (fs);
789
790 sel = gnm_file_saver_get_sheets (fs, wb_view, TRUE);
791 for (ui = 0; ui < sel->len; ui++) {
792 Sheet *sheet = g_ptr_array_index (sel, ui);
793 write_sheet (output, sheet, version, save_scope);
794 }
795 g_ptr_array_unref (sel);
796
797 if (version == HTML32 || version == HTML40 || version == XHTML)
798 gsf_output_puts (output, "</body>\n</html>\n");
799 }
800
801 void
html40_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wb_view,GsfOutput * output)802 html40_file_save (GOFileSaver const *fs, GOIOContext *io_context,
803 WorkbookView const *wb_view, GsfOutput *output)
804 {
805 html_file_save (fs, io_context, wb_view, output, HTML40);
806 }
807
808 void
html32_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wb_view,GsfOutput * output)809 html32_file_save (GOFileSaver const *fs, GOIOContext *io_context,
810 WorkbookView const *wb_view, GsfOutput *output)
811 {
812 html_file_save (fs, io_context, wb_view, output, HTML32);
813 }
814
815 void
html40frag_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wb_view,GsfOutput * output)816 html40frag_file_save (GOFileSaver const *fs, GOIOContext *io_context,
817 WorkbookView const *wb_view, GsfOutput *output)
818 {
819 html_file_save (fs, io_context, wb_view, output, HTML40F);
820 }
821
822 void
xhtml_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wb_view,GsfOutput * output)823 xhtml_file_save (GOFileSaver const *fs, GOIOContext *io_context,
824 WorkbookView const *wb_view, GsfOutput *output)
825 {
826 html_file_save (fs, io_context, wb_view, output, XHTML);
827 }
828
829 void
xhtml_range_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wb_view,GsfOutput * output)830 xhtml_range_file_save (GOFileSaver const *fs, GOIOContext *io_context,
831 WorkbookView const *wb_view, GsfOutput *output)
832 {
833 /* Identical, but fs->save_scope is different */
834 xhtml_file_save (fs, io_context, wb_view, output);
835 }
836