Code cleanups.
[gnumeric.git] / plugins / sylk / sylk-write.c
blob5448ea984f3d0e421e4c99b93036ced4e85ac380
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * sylk-write.c : export sylk
5 * Copyright (C) 2007 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <gnumeric-config.h>
25 #include "workbook-priv.h"
26 #include "workbook-view.h"
27 #include "sheet.h"
28 #include "sheet-style.h"
29 #include "expr.h"
30 #include "value.h"
31 #include "cell.h"
32 #include "mstyle.h"
33 #include "gutils.h"
34 #include "parse-util.h"
35 #include "style-border.h"
36 #include "ranges.h"
38 #include <goffice/goffice.h>
40 #include <gsf/gsf-output.h>
41 #include <gsf/gsf-utils.h>
42 #include <gsf/gsf-impl-utils.h>
43 #include <glib/gi18n-lib.h>
44 #include <gmodule.h>
46 static int
47 font_equal (gconstpointer a_, gconstpointer b_)
49 GnmStyle const *a = a_;
50 GnmStyle const *b = b_;
52 return g_str_equal (gnm_style_get_font_name (a), gnm_style_get_font_name (b)) &&
53 gnm_style_get_font_size (a) == gnm_style_get_font_size (b);
56 static guint
57 font_hash (gconstpointer s_)
59 GnmStyle const *s = s_;
61 return g_str_hash (gnm_style_get_font_name (s)) ^
62 (guint)(gnm_style_get_font_size (s));
66 typedef struct {
67 GsfOutput *output;
69 GnmConventions *convs;
71 Workbook *wb;
72 Sheet *sheet;
74 int cur_row;
76 GPtrArray *formats;
77 GHashTable *format_hash;
79 GPtrArray *fonts;
80 GHashTable *font_hash;
81 } SylkWriter;
83 static void
84 sylk_write (SylkWriter *state, char const *str)
86 char const *p, *next;
87 gunichar c;
89 /* export the valid chunks */
90 for (p = str ; *p ; p = next) {
91 next = g_utf8_next_char (p);
92 c = g_utf8_get_char (p);
94 if (c == ';') {
95 gsf_output_write (state->output, p - str, str);
96 gsf_output_write (state->output, 2, ";;");
97 str = next;
98 } else if ((next - p) > 1) {
99 gsf_output_write (state->output, p - str, str);
100 gsf_output_write (state->output, 1, "?");
101 str = next;
103 #warning "handle the magic ascii escaping"
105 gsf_output_write (state->output, p - str, str);
108 static void
109 sylk_output_string (GnmConventionsOut *out, GOString const *string)
111 g_string_append_c (out->accum, '\"');
112 g_string_append (out->accum, string->str);
113 g_string_append_c (out->accum, '\"');
116 static GnmValue *
117 cb_sylk_write_cell (GnmCellIter const *iter, SylkWriter *state)
119 GnmValue const *v;
120 GnmExprTop const *texpr;
122 if (iter->pp.eval.row != state->cur_row)
123 gsf_output_printf (state->output, "C;Y%d;X%d",
124 (state->cur_row = iter->pp.eval.row) + 1,
125 iter->pp.eval.col + 1);
126 else
127 gsf_output_printf (state->output, "C;X%d",
128 iter->pp.eval.col + 1);
130 v = iter->cell->value;
131 if (v) {
132 if (VALUE_IS_STRING (v)) {
133 gsf_output_write (state->output, 3, ";K\"");
134 sylk_write (state, v->v_str.val->str);
135 gsf_output_write (state->output, 1, "\"");
136 } else if (VALUE_IS_NUMBER (v) || VALUE_IS_ERROR (v)) {
137 GString *res = g_string_sized_new (10);
138 value_get_as_gstring (v, res, state->convs);
139 gsf_output_write (state->output, 2, ";K");
140 gsf_output_write (state->output, res->len, res->str);
141 g_string_free (res, TRUE);
142 } /* ignore the rest */
145 texpr = iter->cell->base.texpr;
146 if (texpr) {
147 if (gnm_expr_top_is_array_corner (texpr)) {
148 int cols, rows;
149 gnm_expr_top_get_array_size (texpr, &cols, &rows);
151 gsf_output_printf (state->output, ";R%d;C%d;M",
152 iter->pp.eval.row + rows,
153 iter->pp.eval.col + cols);
154 } else if (gnm_expr_top_is_array_elem (texpr, NULL, NULL)) {
155 gsf_output_write (state->output, 2, ";I");
156 texpr = NULL;
157 } else
158 gsf_output_write (state->output, 2, ";E");
160 if (texpr != NULL) {
161 GnmConventionsOut out;
162 out.accum = g_string_new (NULL);
163 out.pp = &iter->pp;
164 out.convs = state->convs;
165 gnm_expr_top_as_gstring (texpr, &out);
166 sylk_write (state, out.accum->str);
167 g_string_free (out.accum, TRUE);
170 gsf_output_write (state->output, 2, "\r\n");
171 return NULL;
174 static gboolean
175 sylk_get_border (GnmStyle const *style, GnmStyleElement border)
177 GnmBorder *b = gnm_style_get_border (style, border);
178 return b && b->line_type > GNM_STYLE_BORDER_NONE;
181 static void
182 sylk_write_style (SylkWriter *state, GnmStyle const *style)
184 GOFormat const *fmt;
185 unsigned n;
186 GnmHAlign halign;
188 gsf_output_printf (state->output, "F");
190 halign = gnm_style_get_align_h (style);
191 switch (halign) {
192 case GNM_HALIGN_LEFT: gsf_output_printf (state->output, ";FD0L"); break;
193 case GNM_HALIGN_RIGHT: gsf_output_printf (state->output, ";FD0R"); break;
194 case GNM_HALIGN_CENTER: gsf_output_printf (state->output, ";FD0C"); break;
195 case GNM_HALIGN_FILL: gsf_output_printf (state->output, ";FD0X"); break;
196 default:
197 ; // Nothing
200 fmt = gnm_style_get_format (style);
201 n = GPOINTER_TO_UINT (g_hash_table_lookup (state->format_hash, (gpointer)fmt));
202 gsf_output_printf (state->output, ";P%d", n);
204 n = GPOINTER_TO_UINT (g_hash_table_lookup (state->font_hash, style));
205 gsf_output_printf (state->output, ";SM%d", n + 1);
207 if (gnm_style_get_font_bold (style))
208 gsf_output_printf (state->output, ";SD");
209 if (gnm_style_get_font_italic (style))
210 gsf_output_printf (state->output, ";SI");
211 if (gnm_style_get_pattern (style) == 5)
212 gsf_output_printf (state->output, ";SS");
213 if (sylk_get_border (style, MSTYLE_BORDER_TOP))
214 gsf_output_printf (state->output, ";ST");
215 if (sylk_get_border (style, MSTYLE_BORDER_BOTTOM))
216 gsf_output_printf (state->output, ";SB");
217 if (sylk_get_border (style, MSTYLE_BORDER_LEFT))
218 gsf_output_printf (state->output, ";SL");
219 if (sylk_get_border (style, MSTYLE_BORDER_RIGHT))
220 gsf_output_printf (state->output, ";SR");
222 // Line not terminated
225 static void
226 sylk_write_pos (SylkWriter *state, int col, int row)
228 if (row != state->cur_row) {
229 state->cur_row = row;
230 gsf_output_printf (state->output, ";Y%d", row + 1);
232 gsf_output_printf (state->output, ";X%d\r\n", col + 1);
235 static GnmValue *
236 cb_sylk_write_cell_style (GnmCellIter const *iter, SylkWriter *state)
238 GnmStyle const *style = sheet_style_get (state->sheet, iter->pp.eval.col, iter->pp.eval.row);
240 sylk_write_style (state, style);
241 sylk_write_pos (state, iter->pp.eval.col, iter->pp.eval.row);
243 return NULL;
247 static void
248 cb_sylk_collect_styles (GnmStyle const *st, SylkWriter *state)
250 GOFormat const *fmt;
252 fmt = gnm_style_get_format (st);
253 if (!g_hash_table_lookup_extended (state->format_hash, fmt, NULL, NULL)) {
254 unsigned n = state->formats->len;
255 g_hash_table_insert (state->format_hash, (gpointer)fmt, GUINT_TO_POINTER (n));
256 g_ptr_array_add (state->formats, (gpointer)fmt);
259 if (!g_hash_table_lookup_extended (state->font_hash, st, NULL, NULL)) {
260 unsigned n = state->fonts->len;
261 g_hash_table_insert (state->font_hash, (gpointer)st, GUINT_TO_POINTER (n));
262 g_ptr_array_add (state->fonts, (gpointer)st);
267 static void
268 cb_sylk_collect_cell_styles (G_GNUC_UNUSED gpointer unused,
269 GnmCell *cell, SylkWriter *state)
273 static void
274 sylk_write_sheet (SylkWriter *state)
276 Sheet *sheet = state->sheet;
277 GnmRange extent;
278 unsigned ui;
279 GnmRange whole_sheet;
280 GnmStyle **col_defs;
281 ColRowInfo const *cr_def;
282 int col, row;
284 /* collect style and font info */
285 range_init_full_sheet (&whole_sheet, sheet);
286 extent = sheet_get_extent (sheet, FALSE, TRUE);
287 col_defs = sheet_style_most_common (sheet, TRUE);
288 sheet_style_get_nondefault_extent (sheet, &extent, &whole_sheet, col_defs);
290 sheet_style_foreach (sheet,
291 (GFunc)cb_sylk_collect_styles, state);
292 sheet_cell_foreach (sheet,
293 (GHFunc)cb_sylk_collect_cell_styles, state);
295 for (ui = 0; ui < state->formats->len; ui++) {
296 GOFormat const *fmt = g_ptr_array_index (state->formats, ui);
297 gsf_output_printf (state->output, "P;P%s\r\n",
298 go_format_as_XL (fmt));
300 for (ui = 0; ui < state->fonts->len; ui++) {
301 GnmStyle const *s = g_ptr_array_index (state->fonts, ui);
302 gsf_output_printf (state->output, "P;E%s;M%d\r\n",
303 gnm_style_get_font_name (s),
304 (int)(gnm_style_get_font_size (s) * 20 + 0.5));
307 // Column styles.
308 for (col = extent.start.col; col <= extent.end.col; col++) {
309 sylk_write_style (state, col_defs[col]);
310 gsf_output_printf (state->output, ";C%d\r\n", col + 1);
313 // Cell styles
314 state->cur_row = -1;
315 sheet_foreach_cell_in_range (sheet, 0, &extent,
316 (CellIterFunc) cb_sylk_write_cell_style, state);
318 // Column widths
319 cr_def = sheet_colrow_get_default (sheet, TRUE);
320 for (col = extent.start.col; col <= extent.end.col; col++) {
321 ColRowInfo *cr = sheet_col_get (sheet, col);
322 if (!cr || cr->size_pts == cr_def->size_pts)
323 continue;
324 gsf_output_printf (state->output, "F;W%d %d %d\r\n",
325 col + 1, col + 1, (int)(cr->size_pts / 7.45 + 0.5));
328 // Row heights
329 cr_def = sheet_colrow_get_default (sheet, FALSE);
330 for (row = extent.start.row; row <= extent.end.row; row++) {
331 ColRowInfo *cr = sheet_row_get (sheet, row);
332 if (!cr || cr->size_pts == cr_def->size_pts)
333 continue;
334 gsf_output_printf (state->output, "F;M%d;R%d\r\n",
335 (int)(cr->size_pts * 20 + 0.5),
336 row + 1);
339 /* Global Formatting */
340 /* F;P0;DG0G10;SM0;Z;M280;N3 10 */
342 /* Bounds */
343 gsf_output_printf (state->output, "B;Y%d;X%d;D0 0 %d %d\r\n",
344 extent.end.row + 1, extent.end.col + 1,
345 extent.end.row, extent.end.col);
347 /* Global options */
348 gsf_output_printf (state->output, "O;%c%d %f",
349 (state->wb->iteration.enabled ? 'A' : 'G'),
350 state->wb->iteration.max_number,
351 state->wb->iteration.tolerance);
352 if (!sheet->convs->r1c1_addresses)
353 gsf_output_puts (state->output, ";L");
354 if (!state->wb->recalc_auto)
355 gsf_output_puts (state->output, ";M");
356 gsf_output_printf (state->output, ";V%d",
357 workbook_date_conv (state->wb)->use_1904 ? 4 : 0);
358 if (sheet->hide_zero)
359 gsf_output_puts (state->output, ";Z");
360 gsf_output_write (state->output, 2, "\r\n");
362 /* dump content */
363 state->cur_row = -1;
364 sheet_foreach_cell_in_range (sheet, CELL_ITER_IGNORE_BLANK, &extent,
365 (CellIterFunc) cb_sylk_write_cell, state);
367 g_free (col_defs);
370 static GnmConventions *
371 sylk_conventions_new (void)
373 GnmConventions *res = gnm_conventions_new ();
375 res->range_sep_colon = TRUE;
376 res->r1c1_addresses = TRUE;
377 res->input.range_ref = rangeref_parse;
378 res->output.translated = FALSE;
379 res->output.string = sylk_output_string;
381 return res;
384 G_MODULE_EXPORT void
385 sylk_file_save (GOFileSaver const *fs, GOIOContext *io_context,
386 gconstpointer wb_view, GsfOutput *output);
387 void
388 sylk_file_save (GOFileSaver const *fs, GOIOContext *io_context,
389 gconstpointer wb_view, GsfOutput *output)
391 GnmLocale *locale;
392 SylkWriter state;
394 state.wb = wb_view_get_workbook (wb_view);
395 state.sheet = wb_view_cur_sheet (wb_view);
396 state.output = output;
397 state.convs = sylk_conventions_new ();
399 state.formats = g_ptr_array_new ();
400 state.format_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
402 state.fonts = g_ptr_array_new ();
403 state.font_hash = g_hash_table_new (font_hash, font_equal);
405 locale = gnm_push_C_locale ();
406 gsf_output_puts (output, "ID;PGnumeric;N;E\r\n");
408 sylk_write_sheet (&state);
410 gsf_output_puts (output, "E\r\n");
411 gnm_pop_C_locale (locale);
412 gnm_conventions_unref (state.convs);
414 g_hash_table_destroy (state.font_hash);
415 g_ptr_array_free (state.fonts, TRUE);
417 g_hash_table_destroy (state.format_hash);
418 g_ptr_array_free (state.formats, TRUE);