Update Spanish translation
[gnumeric.git] / plugins / sylk / sylk-write.c
blob998ea4208a1f4c337a73d056aa374d2145610b59
1 /*
2 * sylk-write.c : export sylk
4 * Copyright (C) 2007 Jody Goldberg (jody@gnome.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
22 #include <gnumeric-config.h>
24 #include <workbook-priv.h>
25 #include <workbook-view.h>
26 #include <sheet.h>
27 #include <sheet-style.h>
28 #include <expr.h>
29 #include <value.h>
30 #include <cell.h>
31 #include <mstyle.h>
32 #include <gutils.h>
33 #include <parse-util.h>
34 #include <style-border.h>
35 #include <ranges.h>
37 #include <goffice/goffice.h>
39 #include <gsf/gsf-output.h>
40 #include <gsf/gsf-utils.h>
41 #include <gsf/gsf-impl-utils.h>
42 #include <glib/gi18n-lib.h>
43 #include <gmodule.h>
45 static int
46 font_equal (gconstpointer a_, gconstpointer b_)
48 GnmStyle const *a = a_;
49 GnmStyle const *b = b_;
51 return g_str_equal (gnm_style_get_font_name (a), gnm_style_get_font_name (b)) &&
52 gnm_style_get_font_size (a) == gnm_style_get_font_size (b);
55 static guint
56 font_hash (gconstpointer s_)
58 GnmStyle const *s = s_;
60 return g_str_hash (gnm_style_get_font_name (s)) ^
61 (guint)(gnm_style_get_font_size (s));
65 typedef struct {
66 GsfOutput *output;
68 GnmConventions *convs;
70 Workbook *wb;
71 Sheet *sheet;
73 int cur_row;
75 GPtrArray *formats;
76 GHashTable *format_hash;
78 GPtrArray *fonts;
79 GHashTable *font_hash;
80 } SylkWriter;
82 static void
83 sylk_write (SylkWriter *state, char const *str)
85 char const *p, *next;
86 gunichar c;
88 /* export the valid chunks */
89 for (p = str ; *p ; p = next) {
90 next = g_utf8_next_char (p);
91 c = g_utf8_get_char (p);
93 if (c == ';') {
94 gsf_output_write (state->output, p - str, str);
95 gsf_output_write (state->output, 2, ";;");
96 str = next;
97 } else if ((next - p) > 1) {
98 gsf_output_write (state->output, p - str, str);
99 gsf_output_write (state->output, 1, "?");
100 str = next;
102 #warning "handle the magic ascii escaping"
104 gsf_output_write (state->output, p - str, str);
107 static void
108 sylk_output_string (GnmConventionsOut *out, GOString const *string)
110 g_string_append_c (out->accum, '\"');
111 g_string_append (out->accum, string->str);
112 g_string_append_c (out->accum, '\"');
115 static GnmValue *
116 cb_sylk_write_cell (GnmCellIter const *iter, SylkWriter *state)
118 GnmValue const *v;
119 GnmExprTop const *texpr;
121 if (iter->pp.eval.row != state->cur_row)
122 gsf_output_printf (state->output, "C;Y%d;X%d",
123 (state->cur_row = iter->pp.eval.row) + 1,
124 iter->pp.eval.col + 1);
125 else
126 gsf_output_printf (state->output, "C;X%d",
127 iter->pp.eval.col + 1);
129 v = iter->cell->value;
130 if (v) {
131 if (VALUE_IS_STRING (v)) {
132 gsf_output_write (state->output, 3, ";K\"");
133 sylk_write (state, v->v_str.val->str);
134 gsf_output_write (state->output, 1, "\"");
135 } else if (VALUE_IS_NUMBER (v) || VALUE_IS_ERROR (v)) {
136 GString *res = g_string_sized_new (10);
137 value_get_as_gstring (v, res, state->convs);
138 gsf_output_write (state->output, 2, ";K");
139 gsf_output_write (state->output, res->len, res->str);
140 g_string_free (res, TRUE);
141 } /* ignore the rest */
144 texpr = iter->cell->base.texpr;
145 if (texpr) {
146 if (gnm_expr_top_is_array_corner (texpr)) {
147 int cols, rows;
148 gnm_expr_top_get_array_size (texpr, &cols, &rows);
150 gsf_output_printf (state->output, ";R%d;C%d;M",
151 iter->pp.eval.row + rows,
152 iter->pp.eval.col + cols);
153 } else if (gnm_expr_top_is_array_elem (texpr, NULL, NULL)) {
154 gsf_output_write (state->output, 2, ";I");
155 texpr = NULL;
156 } else
157 gsf_output_write (state->output, 2, ";E");
159 if (texpr != NULL) {
160 GnmConventionsOut out;
161 out.accum = g_string_new (NULL);
162 out.pp = &iter->pp;
163 out.convs = state->convs;
164 gnm_expr_top_as_gstring (texpr, &out);
165 sylk_write (state, out.accum->str);
166 g_string_free (out.accum, TRUE);
169 gsf_output_write (state->output, 2, "\r\n");
170 return NULL;
173 static gboolean
174 sylk_get_border (GnmStyle const *style, GnmStyleElement border)
176 GnmBorder *b = gnm_style_get_border (style, border);
177 return b && b->line_type > GNM_STYLE_BORDER_NONE;
180 static void
181 sylk_write_style (SylkWriter *state, GnmStyle const *style)
183 GOFormat const *fmt;
184 unsigned n;
185 GnmHAlign halign;
187 gsf_output_printf (state->output, "F");
189 halign = gnm_style_get_align_h (style);
190 switch (halign) {
191 case GNM_HALIGN_LEFT: gsf_output_printf (state->output, ";FD0L"); break;
192 case GNM_HALIGN_RIGHT: gsf_output_printf (state->output, ";FD0R"); break;
193 case GNM_HALIGN_CENTER: gsf_output_printf (state->output, ";FD0C"); break;
194 case GNM_HALIGN_FILL: gsf_output_printf (state->output, ";FD0X"); break;
195 default:
196 ; // Nothing
199 fmt = gnm_style_get_format (style);
200 n = GPOINTER_TO_UINT (g_hash_table_lookup (state->format_hash, (gpointer)fmt));
201 gsf_output_printf (state->output, ";P%d", n);
203 n = GPOINTER_TO_UINT (g_hash_table_lookup (state->font_hash, style));
204 gsf_output_printf (state->output, ";SM%d", n + 1);
206 if (gnm_style_get_font_bold (style))
207 gsf_output_printf (state->output, ";SD");
208 if (gnm_style_get_font_italic (style))
209 gsf_output_printf (state->output, ";SI");
210 if (gnm_style_get_pattern (style) == 5)
211 gsf_output_printf (state->output, ";SS");
212 if (sylk_get_border (style, MSTYLE_BORDER_TOP))
213 gsf_output_printf (state->output, ";ST");
214 if (sylk_get_border (style, MSTYLE_BORDER_BOTTOM))
215 gsf_output_printf (state->output, ";SB");
216 if (sylk_get_border (style, MSTYLE_BORDER_LEFT))
217 gsf_output_printf (state->output, ";SL");
218 if (sylk_get_border (style, MSTYLE_BORDER_RIGHT))
219 gsf_output_printf (state->output, ";SR");
221 // Line not terminated
224 static void
225 sylk_write_pos (SylkWriter *state, int col, int row)
227 if (row != state->cur_row) {
228 state->cur_row = row;
229 gsf_output_printf (state->output, ";Y%d", row + 1);
231 gsf_output_printf (state->output, ";X%d\r\n", col + 1);
234 static GnmValue *
235 cb_sylk_write_cell_style (GnmCellIter const *iter, SylkWriter *state)
237 GnmStyle const *style = sheet_style_get (state->sheet, iter->pp.eval.col, iter->pp.eval.row);
239 sylk_write_style (state, style);
240 sylk_write_pos (state, iter->pp.eval.col, iter->pp.eval.row);
242 return NULL;
246 static void
247 cb_sylk_collect_styles (GnmStyle const *st, SylkWriter *state)
249 GOFormat const *fmt;
251 fmt = gnm_style_get_format (st);
252 if (!g_hash_table_lookup_extended (state->format_hash, fmt, NULL, NULL)) {
253 unsigned n = state->formats->len;
254 g_hash_table_insert (state->format_hash, (gpointer)fmt, GUINT_TO_POINTER (n));
255 g_ptr_array_add (state->formats, (gpointer)fmt);
258 if (!g_hash_table_lookup_extended (state->font_hash, st, NULL, NULL)) {
259 unsigned n = state->fonts->len;
260 g_hash_table_insert (state->font_hash, (gpointer)st, GUINT_TO_POINTER (n));
261 g_ptr_array_add (state->fonts, (gpointer)st);
266 static void
267 cb_sylk_collect_cell_styles (G_GNUC_UNUSED gpointer unused,
268 GnmCell *cell, SylkWriter *state)
272 static void
273 sylk_write_sheet (SylkWriter *state)
275 Sheet *sheet = state->sheet;
276 GnmRange extent;
277 unsigned ui;
278 GnmRange whole_sheet;
279 GnmStyle **col_defs;
280 ColRowInfo const *cr_def;
281 int col, row;
283 /* collect style and font info */
284 range_init_full_sheet (&whole_sheet, sheet);
285 extent = sheet_get_extent (sheet, FALSE, TRUE);
286 col_defs = sheet_style_most_common (sheet, TRUE);
287 sheet_style_get_nondefault_extent (sheet, &extent, &whole_sheet, col_defs);
289 sheet_style_foreach (sheet,
290 (GFunc)cb_sylk_collect_styles, state);
291 sheet_cell_foreach (sheet,
292 (GHFunc)cb_sylk_collect_cell_styles, state);
294 for (ui = 0; ui < state->formats->len; ui++) {
295 GOFormat const *fmt = g_ptr_array_index (state->formats, ui);
296 gsf_output_printf (state->output, "P;P%s\r\n",
297 go_format_as_XL (fmt));
299 for (ui = 0; ui < state->fonts->len; ui++) {
300 GnmStyle const *s = g_ptr_array_index (state->fonts, ui);
301 gsf_output_printf (state->output, "P;E%s;M%d\r\n",
302 gnm_style_get_font_name (s),
303 (int)(gnm_style_get_font_size (s) * 20 + 0.5));
306 // Column styles.
307 for (col = extent.start.col; col <= extent.end.col; col++) {
308 sylk_write_style (state, col_defs[col]);
309 gsf_output_printf (state->output, ";C%d\r\n", col + 1);
312 // Cell styles
313 state->cur_row = -1;
314 sheet_foreach_cell_in_range (sheet, 0, &extent,
315 (CellIterFunc) cb_sylk_write_cell_style, state);
317 // Column widths
318 cr_def = sheet_colrow_get_default (sheet, TRUE);
319 for (col = extent.start.col; col <= extent.end.col; col++) {
320 ColRowInfo *cr = sheet_col_get (sheet, col);
321 if (!cr || cr->size_pts == cr_def->size_pts)
322 continue;
323 gsf_output_printf (state->output, "F;W%d %d %d\r\n",
324 col + 1, col + 1, (int)(cr->size_pts / 7.45 + 0.5));
327 // Row heights
328 cr_def = sheet_colrow_get_default (sheet, FALSE);
329 for (row = extent.start.row; row <= extent.end.row; row++) {
330 ColRowInfo *cr = sheet_row_get (sheet, row);
331 if (!cr || cr->size_pts == cr_def->size_pts)
332 continue;
333 gsf_output_printf (state->output, "F;M%d;R%d\r\n",
334 (int)(cr->size_pts * 20 + 0.5),
335 row + 1);
338 /* Global Formatting */
339 /* F;P0;DG0G10;SM0;Z;M280;N3 10 */
341 /* Bounds */
342 gsf_output_printf (state->output, "B;Y%d;X%d;D0 0 %d %d\r\n",
343 extent.end.row + 1, extent.end.col + 1,
344 extent.end.row, extent.end.col);
346 /* Global options */
347 gsf_output_printf (state->output, "O;%c%d %f",
348 (state->wb->iteration.enabled ? 'A' : 'G'),
349 state->wb->iteration.max_number,
350 state->wb->iteration.tolerance);
351 if (!sheet->convs->r1c1_addresses)
352 gsf_output_puts (state->output, ";L");
353 if (!state->wb->recalc_auto)
354 gsf_output_puts (state->output, ";M");
355 gsf_output_printf (state->output, ";V%d",
356 workbook_date_conv (state->wb)->use_1904 ? 4 : 0);
357 if (sheet->hide_zero)
358 gsf_output_puts (state->output, ";Z");
359 gsf_output_write (state->output, 2, "\r\n");
361 /* dump content */
362 state->cur_row = -1;
363 sheet_foreach_cell_in_range (sheet, CELL_ITER_IGNORE_BLANK, &extent,
364 (CellIterFunc) cb_sylk_write_cell, state);
366 g_free (col_defs);
369 static GnmConventions *
370 sylk_conventions_new (void)
372 GnmConventions *res = gnm_conventions_new ();
374 res->range_sep_colon = TRUE;
375 res->r1c1_addresses = TRUE;
376 res->input.range_ref = rangeref_parse;
377 res->output.translated = FALSE;
378 res->output.string = sylk_output_string;
380 return res;
383 G_MODULE_EXPORT void
384 sylk_file_save (GOFileSaver const *fs, GOIOContext *io_context,
385 gconstpointer wb_view, GsfOutput *output);
386 void
387 sylk_file_save (GOFileSaver const *fs, GOIOContext *io_context,
388 gconstpointer wb_view, GsfOutput *output)
390 GnmLocale *locale;
391 SylkWriter state;
393 state.wb = wb_view_get_workbook (wb_view);
394 state.sheet = wb_view_cur_sheet (wb_view);
395 state.output = output;
396 state.convs = sylk_conventions_new ();
398 state.formats = g_ptr_array_new ();
399 state.format_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
401 state.fonts = g_ptr_array_new ();
402 state.font_hash = g_hash_table_new (font_hash, font_equal);
404 locale = gnm_push_C_locale ();
405 gsf_output_puts (output, "ID;PGnumeric;N;E\r\n");
407 sylk_write_sheet (&state);
409 gsf_output_puts (output, "E\r\n");
410 gnm_pop_C_locale (locale);
411 gnm_conventions_unref (state.convs);
413 g_hash_table_destroy (state.font_hash);
414 g_ptr_array_free (state.fonts, TRUE);
416 g_hash_table_destroy (state.format_hash);
417 g_ptr_array_free (state.formats, TRUE);