/* * html.c * * Copyright (C) 1999, 2000 Rasca, Berlin * EMail: thron@gmx.de * Copyright (c) 2001 Andreas J. Guelzow * EMail: aguelzow@taliesin.ca * * Contributors : * Almer. S. Tigelaar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "io-context.h" #include "workbook-view.h" #include "workbook.h" #include "sheet-style.h" #include "style.h" #include "style-color.h" #include "html.h" #include "cell.h" #include "cellspan.h" #include "sheet.h" #include "sheet-merge.h" #include "value.h" #include "font.h" #include "plugin-util.h" #include "error-info.h" #include "style-border.h" #include #include "mstyle.h" #include "hlink.h" #include #include /* * html_version_t: * * version selector * */ typedef enum { HTML40 = 0, HTML32 = 1, HTML40F = 2, XHTML = 3 } html_version_t; /* * html_print_encoded: * * @output: the stream * @str: the string * * print the string to output encoded all special chars * */ static void html_print_encoded (GsfOutput *output, char const *str) { gunichar c; if (str == NULL) return; for (; *str != '\0' ; str = g_utf8_next_char (str)) { switch (*str) { case '<': gsf_output_puts (output, "<"); break; case '>': gsf_output_puts (output, ">"); break; case '&': gsf_output_puts (output, "&"); break; case '\"': gsf_output_puts (output, """); break; case '\n': gsf_output_puts (output, "
\n"); break; case '\r': gsf_output_puts (output, "
\r"); if( *(str+1) == '\n' ) { gsf_output_puts (output, "\n"); str++; } break; default: c = g_utf8_get_char (str); if (((c >= 0x20) && (c < 0x80)) || (c == '\n') || (c == '\r') || (c == '\t')) gsf_output_write (output, 1, str); else gsf_output_printf (output, "&#%u;", c); break; } } } /* * html_get_color: * * @mstyle: the cellstyle * @t: which color * @r: red component * @g: green component * @b: blue component * * Determine rgb components * */ static void html_get_text_color (GnmCell *cell, GnmStyle *mstyle, guint *r, guint *g, guint *b) { PangoColor const *fore = cell_get_render_color (cell); if (fore == NULL) *r = *g = *b = 0; else { *r = fore->red >> 8; *g = fore->green >> 8; *b = fore->blue >> 8; } } static void html_get_color (GnmStyle *mstyle, MStyleElementType t, guint *r, guint *g, guint *b) { GnmColor *color; color = mstyle_get_color (mstyle, t); *r = color->color.red >> 8; *g = color->color.green >> 8; *b = color->color.blue >> 8; } static void html_write_cell_content (GsfOutput *output, GnmCell *cell, GnmStyle *mstyle, html_version_t version) { guint r = 0; guint g = 0; guint b = 0; char *rendered_string; gboolean hidden = mstyle_get_content_hidden (mstyle); GnmHLink* hlink = mstyle_get_hlink (mstyle); const guchar* hlink_target = NULL; if (hlink && IS_GNM_HLINK_URL (hlink)) { hlink_target = gnm_hlink_get_target (hlink); } if (version == HTML32 && hidden) gsf_output_puts (output, ""); else { if (mstyle != NULL) { if (mstyle_get_font_italic (mstyle)) gsf_output_puts (output, ""); if (mstyle_get_font_bold (mstyle)) gsf_output_puts (output, ""); if (font_is_monospaced (mstyle)) gsf_output_puts (output, ""); } if (hlink_target) gsf_output_printf (output, "", hlink_target); if (cell != NULL) { if (mstyle != NULL && version != HTML40) { html_get_text_color (cell, mstyle, &r, &g, &b); if (r > 0 || g > 0 || b > 0) gsf_output_printf (output, "", r, g, b); } rendered_string = cell_get_rendered_text (cell); html_print_encoded (output, rendered_string); g_free (rendered_string); } if (r > 0 || g > 0 || b > 0) gsf_output_puts (output, ""); if (hlink_target) gsf_output_puts (output, ""); if (mstyle != NULL) { if (font_is_monospaced (mstyle)) gsf_output_puts (output, ""); if (mstyle_get_font_bold (mstyle)) gsf_output_puts (output, ""); if (mstyle_get_font_italic (mstyle)) gsf_output_puts (output, ""); } } } static char * html_get_border_style (GnmBorder *border) { GString *text = g_string_new (NULL); char *result; switch (border->line_type) { case STYLE_BORDER_THIN: g_string_append (text, "thin solid"); break; case STYLE_BORDER_MEDIUM: g_string_append (text, "medium solid"); break; case STYLE_BORDER_DASHED: g_string_append (text, "thin dashed"); break; case STYLE_BORDER_DOTTED: g_string_append (text, "thin dotted"); break; case STYLE_BORDER_THICK: g_string_append (text, "thick solid"); break; case STYLE_BORDER_DOUBLE: g_string_append (text, "thick double"); break; case STYLE_BORDER_HAIR: g_string_append (text, "0.5pt solid"); break; case STYLE_BORDER_MEDIUM_DASH: g_string_append (text, "medium dashed"); break; case STYLE_BORDER_DASH_DOT: g_string_append (text, "thin dashed"); break; case STYLE_BORDER_MEDIUM_DASH_DOT: g_string_append (text, "medium dashed"); break; case STYLE_BORDER_DASH_DOT_DOT: g_string_append (text, "thin dotted"); break; case STYLE_BORDER_MEDIUM_DASH_DOT_DOT: g_string_append (text, "medium dotted"); break; case STYLE_BORDER_SLANTED_DASH_DOT: g_string_append (text, "thin dashed"); break; default: break; } if (border->color) { guint r, g, b; r = border->color->color.red >> 8; g = border->color->color.green >> 8; b = border->color->color.blue >> 8; g_string_append_printf (text, " #%02X%02X%02X", r, g, b); } result = text->str; g_string_free (text, FALSE); return result; } static void html_write_one_border_style_40 (GsfOutput *output, GnmBorder *border, char const *border_name) { char *text; text = html_get_border_style (border); if (text == NULL || strlen (text) == 0) return; gsf_output_printf (output, " %s:%s;", border_name, text); g_free (text); } static void html_write_border_style_40 (GsfOutput *output, GnmStyle *mstyle) { GnmBorder *border; border = mstyle_get_border (mstyle, MSTYLE_BORDER_TOP); if (!style_border_is_blank (border)) html_write_one_border_style_40 (output, border, "border-top"); border = mstyle_get_border (mstyle, MSTYLE_BORDER_BOTTOM); if (!style_border_is_blank (border)) html_write_one_border_style_40 (output, border, "border-bottom"); border = mstyle_get_border (mstyle, MSTYLE_BORDER_LEFT); if (!style_border_is_blank (border)) html_write_one_border_style_40 (output, border, "border-left"); border = mstyle_get_border (mstyle, MSTYLE_BORDER_RIGHT); if (!style_border_is_blank (border)) html_write_one_border_style_40 (output, border, "border-right"); } static void write_cell (GsfOutput *output, Sheet *sheet, gint row, gint col, html_version_t version) { GnmCell *cell; GnmStyle *mstyle; guint r, g, b; mstyle = sheet_style_get (sheet, col, row); if (mstyle != NULL && version != HTML32 && version != HTML40 && mstyle_get_pattern (mstyle) != 0 && mstyle_is_element_set (mstyle, MSTYLE_COLOR_BACK)) { html_get_color (mstyle, MSTYLE_COLOR_BACK, &r, &g, &b); gsf_output_printf (output, " bgcolor=\"#%02X%02X%02X\"", r, g, b); } cell = sheet_cell_get (sheet, col, row); if (cell != NULL) { switch (mstyle_get_align_v (mstyle)) { case VALIGN_TOP: gsf_output_puts (output, " valign=\"top\" "); break; case VALIGN_BOTTOM: gsf_output_puts (output, " valign=\"bottom\" "); break; case VALIGN_CENTER: gsf_output_puts (output, " valign=\"center\" "); break; case VALIGN_JUSTIFY: gsf_output_puts (output, " valign=\"baseline\" "); break; default: break; } switch (style_default_halign(mstyle, cell)) { case HALIGN_RIGHT: gsf_output_puts (output, " align=\"right\" "); break; case HALIGN_CENTER: case HALIGN_CENTER_ACROSS_SELECTION: gsf_output_puts (output, " align=\"center\" "); break; case HALIGN_LEFT: gsf_output_puts (output, " align=\"left\" "); break; case HALIGN_JUSTIFY: gsf_output_puts (output, " align=\"justify\" "); break; default: break; } } if (version == HTML40 || version == HTML40F) { if (mstyle != NULL) { gsf_output_printf (output, " style=\""); if (mstyle_get_pattern (mstyle) != 0 && mstyle_is_element_set (mstyle, MSTYLE_COLOR_BACK)) { html_get_color (mstyle, MSTYLE_COLOR_BACK, &r, &g, &b); gsf_output_printf (output, "background:#%02X%02X%02X;", r, g, b); } if (cell != NULL) { gint size = (int) (mstyle_get_font_size (mstyle) + 0.5); gsf_output_printf (output, " font-size:%ipt;", size); html_get_text_color (cell, mstyle, &r, &g, &b); if (r > 0 || g > 0 || b > 0) gsf_output_printf (output, " color:#%02X%02X%02X;", r, g, b); if (mstyle_get_content_hidden (mstyle)) gsf_output_puts (output, " visibility:hidden;"); } html_write_border_style_40 (output, mstyle); gsf_output_printf (output, "\""); } } gsf_output_printf (output, ">"); html_write_cell_content (output, cell, mstyle, version); gsf_output_puts (output, "\n"); } /* * write_row: * * @output: the stream * @sheet: the gnumeric sheet * @row: the row number * * Set up a TD node for each cell in the given row, witht eh appropriate * colspan and rowspan. * Call write_cell for each cell. */ static void write_row (GsfOutput *output, Sheet *sheet, gint row, GnmRange *range, html_version_t version) { gint col; ColRowInfo const * ri; ri = sheet_row_get_info (sheet, row); if (ri->needs_respan) row_calc_spans ((ColRowInfo *) ri, sheet); for (col = range->start.col; col <= range->end.col; col++) { CellSpanInfo const *the_span; GnmRange const *merge_range; GnmCellPos pos; pos.col = col; pos.row = row; /* Is this a span */ the_span = row_span_get (ri, col); if (the_span != NULL) { gsf_output_printf (output, "right - col + 1); write_cell (output, sheet, row, the_span->cell->pos.col, version); col = the_span->right; continue; } /* is this covered by a merge */ merge_range = sheet_merge_contains_pos (sheet, &pos); if (merge_range != NULL) { if (merge_range->start.col != col || merge_range->start.row != row) continue; gsf_output_printf (output, "end.col - merge_range->start.col + 1, merge_range->end.row - merge_range->start.row + 1); write_cell (output, sheet, row, col, version); col = merge_range->end.col; continue; } gsf_output_puts (output, "\n"); break; case XHTML: gsf_output_puts (output, "

\n"); break; default: gsf_output_puts (output, "

\n"); break; } if (save_scope != FILE_SAVE_RANGE) { gsf_output_puts (output, "\n"); } total_range = sheet_get_extent (sheet, TRUE); for (row = total_range.start.row; row <= total_range.end.row; row++) { gsf_output_puts (output, "\n"); write_row (output, sheet, row, &total_range, (version == XHTML) ? HTML40 : version); gsf_output_puts (output, "\n"); } gsf_output_puts (output, "
"); html_print_encoded (output, sheet->name_unquoted); gsf_output_puts (output, "
\n"); } /* * html_file_save: * * write the html file (version of html according to version argument) */ static void html_file_save (GnmFileSaver const *fs, IOContext *io_context, WorkbookView const *wb_view, GsfOutput *output, html_version_t version) { GList *sheets, *ptr; Workbook *wb = wb_view_workbook (wb_view); FileSaveScope save_scope; g_return_if_fail (fs != NULL); g_return_if_fail (wb != NULL); g_return_if_fail (output != NULL); switch (version) { case HTML32: gsf_output_puts (output, "\n" "\n" "\n\tTables\n" "\n" "\t\n" "\n" "\n\n"); break; case HTML40: gsf_output_puts (output, "\n" "\n" "\n\tTables\n" "\n" "\t\n" "\n" "\n\n"); break; case XHTML : gsf_output_puts (output, "\n" "\n" "\n" "\n\tTables\n" "\n" "\t\n" "\n" "\n\n"); break; default: break; } sheets = workbook_sheets (wb); save_scope = gnm_file_saver_get_save_scope (fs); for (ptr = sheets ; ptr != NULL ; ptr = ptr->next) { write_sheet (output, (Sheet *) ptr->data, version, save_scope); } g_list_free (sheets); if (version == HTML32 || version == HTML40 || version == XHTML) gsf_output_puts (output, "\n\n"); } void html40_file_save (GnmFileSaver const *fs, IOContext *io_context, WorkbookView const *wb_view, GsfOutput *output) { html_file_save (fs, io_context, wb_view, output, HTML40); } void html32_file_save (GnmFileSaver const *fs, IOContext *io_context, WorkbookView const *wb_view, GsfOutput *output) { html_file_save (fs, io_context, wb_view, output, HTML32); } void html40frag_file_save (GnmFileSaver const *fs, IOContext *io_context, WorkbookView const *wb_view, GsfOutput *output) { html_file_save (fs, io_context, wb_view, output, HTML40F); } void xhtml_file_save (GnmFileSaver const *fs, IOContext *io_context, WorkbookView const *wb_view, GsfOutput *output) { html_file_save (fs, io_context, wb_view, output, XHTML); } void xhtml_range_file_save (GnmFileSaver const *fs, IOContext *io_context, WorkbookView const *wb_view, GsfOutput *output) { /* Identical, but fs->save_scope is different */ xhtml_file_save (fs, io_context, wb_view, output); }