Esempio n. 1
0
/*
 * write_sheet:
 *
 * @output: the stream
 * @sheet: the gnumeric sheet
 *
 * set up a table and call write_row for each row
 */
static void
write_sheet (GsfOutput *output, Sheet *sheet,
	     html_version_t version, GOFileSaveScope save_scope)
{
	GnmRange total_range;
	gint row;

	switch (version) {
	case HTML40:
	case HTML40F:
	case XHTML:
		gsf_output_puts (output, "<p></p><table cellspacing=\"0\" cellpadding=\"3\">\n");
		break;
	default:
		gsf_output_puts (output, "<p><table border=\"1\">\n");
		break;
	}

	if (save_scope != GO_FILE_SAVE_RANGE) {
		gsf_output_puts (output, "<caption>");
		html_print_encoded (output, sheet->name_unquoted);
		gsf_output_puts (output, "</caption>\n");
	}
	total_range = sheet_get_extent (sheet, TRUE, TRUE);
	for (row = total_range.start.row; row <=  total_range.end.row; row++) {
		gsf_output_puts (output, "<tr>\n");
		write_row (output, sheet, row, &total_range, version);
		gsf_output_puts (output, "</tr>\n");
	}
	gsf_output_puts (output, "</table>\n");
}
Esempio n. 2
0
int gnumeric_sheet_get_size(GnumericSheetPtr sheet, int *w, int *h) {
  Sheet *s = (Sheet *)sheet;
  //GnmSheetSize const *size = gnm_sheet_get_size(sheet);
  GnmRange range = sheet_get_extent(s,FALSE);
  if (w!=NULL) *w = range.end.col+1;
  if (h!=NULL) *h = range.end.row+1;
}
Esempio n. 3
0
static GOFileSaver *
find_file_saver(const gchar *filename, WorkbookView *view, Workbook *workbook,
                GODoc *document, GError **error)
{
    GOFileSaver *saver = NULL;
    const gchar saver_id[] = "Gnumeric_stf:stf_assistant";
    GString *saver_options;
    gint i, n_sheets;
    GError *local_error = NULL;

    saver = go_file_saver_for_id(saver_id);
    if (!saver) {
        g_set_error(error,
                    CHUPA_DECOMPOSER_ERROR,
                    CHUPA_DECOMPOSER_ERROR_FEED,
                    "[decomposer][excel][feed][error][saver][create][%s]"
                    ": file saver doesn't found: <%s>",
                    filename, saver_id);
        return NULL;
    }

    saver_options = g_string_new("eol=unix separator='\t'");
    n_sheets = workbook_sheet_count(workbook);
    for (i = 0; i < n_sheets; i++) {
        Sheet *sheet;
        GnmRange total_range;

        sheet = workbook_sheet_by_index(workbook, i);
        total_range = sheet_get_extent(sheet, TRUE);
        if (sheet_is_region_empty(sheet, &total_range)) {
            chupa_debug("[decomposer][excel][feed][saver][sheet][ignore][%s]"
                        ": ignore empty sheet: <%s>(%d)",
                        filename, sheet->name_quoted, i);
            continue;
        }
        g_string_append_printf(saver_options, " sheet=%s", sheet->name_quoted);
        chupa_debug("[decomposer][excel][feed][saver][sheet][use][%s]: <%s>(%d)",
                    filename, sheet->name_quoted, i);
    }
    if (go_file_saver_set_export_options(saver, document,
                                         saver_options->str, &local_error)) {
        g_set_error(error,
                    CHUPA_DECOMPOSER_ERROR,
                    CHUPA_DECOMPOSER_ERROR_FEED,
                    "[decomposer][excel][feed][error][saver][option][%s]"
                    ": <%s>(%s): <%s>(%s:%d)",
                    filename, saver_id, saver_options->str,
                    local_error->message,
                    g_quark_to_string(local_error->domain),
                    local_error->code);
        g_error_free(local_error);
        saver = NULL;
    }
    g_string_free(saver_options, TRUE);

    return saver;
}
Esempio n. 4
0
/*
 * write_sheet:
 *
 * @output: the stream
 * @sheet: the gnumeric sheet
 *
 * set up a table and call write_row for each row
 */
static void
write_sheet (GsfOutput *output, Sheet *sheet, GOFileSaveScope save_scope)
{
	GnmRange total_range;
	gint row;


	total_range = sheet_get_extent (sheet, FALSE, TRUE);
	for (row = total_range.start.row; row <=  total_range.end.row; row++) {
		write_row (output, sheet, row, &total_range);
	}
	gsf_output_puts (output, "---\n");
}
Esempio n. 5
0
static void
sylk_write_sheet (SylkWriter *state)
{
	GnmRange extent;

/* collect style and font info */
	extent = sheet_get_extent (state->sheet, FALSE, TRUE);
	sheet_style_foreach (state->sheet,
			     (GFunc)cb_sylk_collect_styles, state);
	sheet_cell_foreach (state->sheet,
			    (GHFunc)cb_sylk_collect_cell_styles, state);

	/*
	 * 1) formats P;P.....
	 * 2.1) ??	fonts   P;F....
	 * 2.2) indexed fonts   P;E....
	 * 3) global formats F;
	 */

/* Global Formatting */
	/* F;P0;DG0G10;SM0;Z;M280;N3 10 */

/* Bounds */
	gsf_output_printf (state->output, "B;Y%d;X%d;D0 0 %d %d\r\n",
		extent.end.row + 1,	extent.end.col + 1,
		extent.end.row,		extent.end.col);

/* Global options */
	gsf_output_printf (state->output, "O;%c%d %f",
		(state->wb->iteration.enabled ? 'A' : 'G'),
		state->wb->iteration.max_number,
		state->wb->iteration.tolerance);
	if (!state->sheet->convs->r1c1_addresses)
		gsf_output_puts (state->output, ";L");
	if (!state->wb->recalc_auto)
		gsf_output_puts (state->output, ";M");
	gsf_output_printf (state->output, ";V%d",
		workbook_date_conv (state->wb)->use_1904 ? 4 : 0);
	if (state->sheet->hide_zero)
		gsf_output_puts (state->output, ";Z");
	gsf_output_write (state->output, 2, "\r\n");

/* dump content */
	state->cur_row = -1;
	sheet_foreach_cell_in_range (state->sheet, CELL_ITER_IGNORE_BLANK,
		extent.start.col, extent.start.row,
		extent.end.col,   extent.end.row,
		(CellIterFunc) cb_sylk_write_cell, state);
}
Esempio n. 6
0
/**
 * range_clip_to_finite :
 * @range :
 * @sheet : the sheet in which @range lives
 *
 * Clip the range to the area of the sheet with content.
 * if the range reaches the edge
 *
 * The idea here is that users may select a whole column or ow when they
 * really are only concerned with the extent of the sheet.
 * On the otehr hand, if users select any smaller region they probably
 * intend to selec tjust that.
 *
 * WARNING THIS IS EXPENSIVE!
 **/
void
range_clip_to_finite (GnmRange *range, Sheet *sheet)
{
	GnmRange extent;

	/* FIXME : This seems expensive.  We should see if there is a faster
	 * way of doing this.  possibly using a flag for content changes, and
	 * using the current values as a cache
	 */
	extent = sheet_get_extent (sheet, FALSE);
	if (range->end.col >= gnm_sheet_get_max_cols (sheet) - 1)
		range->end.col = extent.end.col;
	if (range->end.row >= gnm_sheet_get_max_rows (sheet) - 1)
		range->end.row = extent.end.row;
}
Esempio n. 7
0
/**
 * stf_export_sheet:
 * @stfe: an export options struct
 * @sheet: the sheet to export
 *
 * Writes the @sheet to the callback function
 *
 * Return value: returns TRUE on success, FALSE otherwise
 **/
static gboolean
stf_export_sheet (GnmStfExport *stfe, Sheet *sheet)
{
	int col, row;
	GnmRange r;
	GnmRangeRef *range;

	g_return_val_if_fail (stfe != NULL, FALSE);
	g_return_val_if_fail (IS_SHEET (sheet), FALSE);

	range = g_object_get_data (G_OBJECT (sheet->workbook), "ssconvert-range");
	if (range) {
		Sheet *start_sheet, *end_sheet;
		GnmEvalPos ep;

		gnm_rangeref_normalize (range,
					eval_pos_init_sheet (&ep, sheet),
					&start_sheet, &end_sheet,
					&r);

		if (start_sheet != sheet)
			return TRUE;
	} else
		r = sheet_get_extent (sheet, FALSE, TRUE);

	for (row = r.start.row; row <= r.end.row; row++) {
		for (col = r.start.col; col <= r.end.col; col++) {
			GnmCell *cell = sheet_cell_get (sheet, col, row);
			if (!stf_export_cell (stfe, cell))
				return FALSE;
		}
		if (!gsf_output_csv_write_eol (GSF_OUTPUT_CSV (stfe)))
			return FALSE;
	}

	return TRUE;
}
Esempio n. 8
0
G_MODULE_EXPORT void
paradox_file_save (GOFileSaver const *fs, GOIOContext *io_context,
		   WorkbookView const *wb_view, GsfOutput *output)
{
	Sheet *sheet;
	GnmRange r;
	gint row, col, i;

	pxdoc_t *pxdoc = NULL;
	pxfield_t *pxf;
	char *data;
	char *tmpfilename;

	sheet = wb_view_cur_sheet (wb_view);
	if (sheet == NULL) {
		go_io_error_string (io_context, _("Cannot get default sheet."));
		return;
	}

	r = sheet_get_extent (sheet, FALSE, TRUE);

#ifdef PX_MEMORY_DEBUGGING
	pxdoc = PX_new2 (gn_errorhandler, PX_mp_malloc, PX_mp_realloc, PX_mp_free);
#else
	pxdoc = PX_new2 (gn_errorhandler, gn_malloc, gn_realloc, gn_free);
#endif

	/* Read the field specification and build the field array for
	 * PX_create_fp(). The memory is freed by PX_delete() including
	 * the memory for the field name. */
	if ((pxf = (pxfield_t *) pxdoc->malloc (pxdoc, (r.end.col+1)*sizeof (pxfield_t), _("Allocate memory for field definitions."))) == NULL){
		go_io_error_string (io_context, _("Cannot allocate memory for field definitions."));
		PX_delete (pxdoc);
		return;
	}

	for (col = r.start.col; col <= r.end.col; col++) {
		GnmCell *cell = sheet_cell_get (sheet, col, 0);
		if (gnm_cell_is_empty (cell)) {
			go_io_error_string (io_context, _("First line of sheet must contain database specification."));
			PX_delete (pxdoc);
			return;
		} else {
			gchar *fieldstr, *tmp;
			int len, needsize, needprecision;

			i = col;
			fieldstr = gnm_cell_get_rendered_text (cell);
			needsize = 0;
			needprecision = 0;

			/* Search for the first comma which is the end of the field name. */
			tmp = strchr (fieldstr, ',');
			if (NULL == tmp) {
				g_warning (_("Field specification must be a comma separated value (Name,Type,Size,Prec)."));
				PX_delete (pxdoc);
				return;
			}
			len = tmp-fieldstr;
			if (NULL == (pxf[i].px_fname = pxdoc->malloc (pxdoc, len+1, _("Allocate memory for column name.")))) {
				g_warning (_("Could not allocate memory for %d. field name."), i);
				PX_delete (pxdoc);
				return;
			}
			strncpy (pxf[i].px_fname, fieldstr, len);
			pxf[i].px_fname[len] = '\0';

			/* Get the field Type */
			fieldstr = tmp+1;
			if (*fieldstr == '\0') {
				g_warning (_("%d. field specification ended unexpectedly."), i);
				PX_delete (pxdoc);
				return;
			}
			if (*fieldstr == ',') {
				g_warning (_("%d. field specification misses type."), i);
				PX_delete (pxdoc);
				return;
			}
			switch ((int) *fieldstr) {
			case 'S':
				pxf[i].px_ftype = pxfShort;
				pxf[i].px_flen = 2;
				break;
			case 'I':
				pxf[i].px_ftype = pxfLong;
				pxf[i].px_flen = 4;
				break;
			case 'A':
			case 'C':
				pxf[i].px_ftype = pxfAlpha;
				needsize = 1;
				break;
			case 'N':
				pxf[i].px_ftype = pxfNumber;
				pxf[i].px_flen = 8;
				break;
			case '$':
				pxf[i].px_ftype = pxfCurrency;
				pxf[i].px_flen = 8;
				break;
			case 'L':
				pxf[i].px_ftype = pxfLogical;
				pxf[i].px_flen = 1;
				break;
			case 'D':
				pxf[i].px_ftype = pxfDate;
				pxf[i].px_flen = 4;
				break;
			case '+':
				pxf[i].px_ftype = pxfAutoInc;
				pxf[i].px_flen = 4;
				break;
			case '@':
				pxf[i].px_ftype = pxfTimestamp;
				pxf[i].px_flen = 8;
				break;
			case 'T':
				pxf[i].px_ftype = pxfTime;
				pxf[i].px_flen = 4;
				break;
			case '#':
				pxf[i].px_ftype = pxfBCD;
				pxf[i].px_flen = 17;
				needprecision = 1;
				break;
			case 'M':
				pxf[i].px_ftype = pxfMemoBLOb;
				needsize = 1;
				break;
			case 'B':
				pxf[i].px_ftype = pxfBLOb;
				needsize = 1;
				break;
			case 'F':
				pxf[i].px_ftype = pxfFmtMemoBLOb;
				needsize = 1;
				break;
			case 'Y':
				pxf[i].px_ftype = pxfBytes;
				needsize = 1;
				break;
			default:
				g_warning (_("%d. field type '%c' is unknown."), i, *fieldstr);
				pxdoc->free (pxdoc, pxf);
				PX_delete (pxdoc);
				return;
			}

			if (needsize || needprecision) {
				char *endptr;
				/* find end of type definition */
				tmp = strchr (fieldstr, ',');
				if (NULL == tmp || *(tmp+1) == '\0') {
					g_warning (_("Field specification misses the column size."));
					PX_delete (pxdoc);
					return;
				}
				fieldstr = tmp+1;
				if (needsize)
					pxf[i].px_flen = strtol (fieldstr, &endptr, 10);
				else
					pxf[i].px_fdc = strtol (fieldstr, &endptr, 10);
				if ((endptr == NULL) || (fieldstr == endptr)) {
					g_warning (_("Field specification misses the column size."));
					PX_delete (pxdoc);
					return;
				}
				if (*endptr != '\0') {
					/* There is also precision which we do not care about. */
					fieldstr = endptr+1;
					g_warning (_("The remainder '%s' of the specification for field %d is being disregarded."), fieldstr, i+1);
				}
			}
		}
	}

	/* Create the paradox file */
	tmpfilename = tempnam ("/tmp", NULL);
	if (0 > PX_create_file (pxdoc, pxf, r.end.col+1, tmpfilename, pxfFileTypNonIndexDB)) {
		g_warning (_("Could not create output file."));
		PX_delete (pxdoc);
		return;
	}

	PX_set_inputencoding (pxdoc, "UTF-8");
	PX_set_parameter (pxdoc, "targetencoding", "CP1252");
	PX_set_tablename (pxdoc, sheet->name_unquoted);

	if ((data = (char *) pxdoc->malloc (pxdoc, pxdoc->px_head->px_recordsize, _("Allocate memory for record data."))) == NULL) {
		g_warning (_("Could not allocate memory for record data."));
		PX_close (pxdoc);
		PX_delete (pxdoc);
		return;
	}
	/* Process all cells */
	for (row = r.start.row+1; row <= r.end.row; row++) {
		int i;
		int offset;
		offset = 0;
		memset (data, 0, pxdoc->px_head->px_recordsize);
		for (col = r.start.col, i = 0; col <= r.end.col; col++) {
			GnmCell *cell = sheet_cell_get (sheet, col, row);
			if (!gnm_cell_is_empty (cell)) {
				char *fieldstr = gnm_cell_get_rendered_text (cell);
				switch (pxf[i].px_ftype) {
				case pxfShort: {
					int value = value_get_as_int (cell->value);
					PX_put_data_short (pxdoc, &data[offset], 2, (short int) value);
					break;
				}
				case pxfLong:
				case pxfAutoInc: {
					int value = value_get_as_int (cell->value);
					PX_put_data_long (pxdoc, &data[offset], 4, value);
					break;
				}
				case pxfTimestamp: {
					double value = value_get_as_float (cell->value);
					/* 60 would be 29.2.1900 which didn't exist. */
					if (value < 60)
						value += 1.0;
					value += 693594;
					value *= 86400000.0;
					PX_put_data_double (pxdoc, &data[offset], 8, value);
					break;
				}
				case pxfCurrency:
				case pxfNumber: {
					double value = value_get_as_float (cell->value);
					PX_put_data_double(pxdoc, &data[offset], 8, value);
					break;
				}
				case pxfAlpha: {
					char *value = fieldstr;
					int nlen = strlen (value);
					if (nlen > pxf[i].px_flen)
						/* xgettext : last %d gives the number of characters.*/
						/* This is input to ngettext. */
						g_warning
							(ngettext
							 ("Field %d in line %d has possibly "
							  "been cut off. Data has %d character.",
							  "Field %d in line %d has possibly "
							  "been cut off. Data has %d characters.",
							  nlen),
							 i+1, row+1, nlen);
					PX_put_data_alpha (pxdoc, &data[offset], pxf[i].px_flen, value);
					break;
				}
				case pxfMemoBLOb:
				case pxfFmtMemoBLOb: {
					char *value = fieldstr;
					if (0 > PX_put_data_blob (pxdoc, &data[offset], pxf[i].px_flen, value, strlen (value))) {
						g_warning (_("Field %d in row %d could not be written."), i+1, row+1);
					}
					break;
				}
				case pxfDate: {
					long value = value_get_as_int (cell->value);
					/* 60 would be 29.2.1900 which didn't exist. */
					if (value < 60)
						value++;
					value += 693594;
					PX_put_data_long (pxdoc, &data[offset], 4, value);
					break;
				}
				case pxfTime: {
					double dtmp;
					int value;
					dtmp  = value_get_as_float (cell->value);
					dtmp -= ((int) dtmp);
					value = (int) (dtmp * 86400000.0);
					PX_put_data_long (pxdoc, &data[offset], 4, value);
					break;
				}
				case pxfLogical: {
					gboolean err; /* Ignored */
					gboolean value = value_get_as_bool (cell->value, &err);
					PX_put_data_byte (pxdoc, &data[offset], 1, value ? 1 : 0);
					break;
				}
				case pxfBCD:
					PX_put_data_bcd (pxdoc, &data[offset], pxf[i].px_fdc, fieldstr);
					break;
				}
			}
			offset += pxf[i].px_flen;
			i++;
		}
		if ((i > 0) && (0 > PX_put_record (pxdoc, data))) {
			g_warning (_("Could not write record number %d."), i+1);
			pxdoc->free (pxdoc, data);
			PX_close (pxdoc);
			PX_delete (pxdoc);
			return;
		}
	}
	pxdoc->free (pxdoc, data);
	PX_close (pxdoc);
	PX_delete (pxdoc);

#ifdef PX_MEMORY_DEBUGGING
	PX_mp_list_unfreed ();
#endif

	{
		FILE *fp;
		size_t size;
		fp = fopen (tmpfilename, "r");
		if (fp) {
			data = g_malloc (8192);
			while (0 != (size = fread (data, 1, 8192, fp)))
				gsf_output_write (output, size, data);
			fclose (fp);
			g_free (data);
		} else
			g_warning ("Cannot open %s\n", tmpfilename);

		unlink (tmpfilename);
	}
}
Esempio n. 9
0
File: dif.c Progetto: GNOME/gnumeric
/*
 * Write _current_ sheet of the workbook to a DIF format file
 */
void
dif_file_save (GOFileSaver const *fs, GOIOContext *io_context,
               WorkbookView const *wbv, GsfOutput *out)
{
	GnmLocale *locale;
	Sheet *sheet;
	GnmRange r;
	gint row, col;
	gboolean ok = TRUE;

	sheet = wb_view_cur_sheet (wbv);
	if (sheet == NULL) {
		go_io_error_string (io_context, _("Cannot get default sheet."));
		return;
	}

	r = sheet_get_extent (sheet, FALSE, TRUE);

	/* Write out the standard headers */
	gsf_output_puts   (out, "TABLE\n"   "0,1\n" "\"GNUMERIC\"\n");
	gsf_output_printf (out, "VECTORS\n" "0,%d\n" "\"\"\n", r.end.col+1);
	gsf_output_printf (out, "TUPLES\n"  "0,%d\n" "\"\"\n", r.end.row+1);
	gsf_output_puts   (out, "DATA\n"    "0,0\n"  "\"\"\n");

	locale = gnm_push_C_locale ();

	/* Process all cells */
	for (row = r.start.row; ok && row <= r.end.row; row++) {
		gsf_output_puts (out, "-1,0\n" "BOT\n");
		for (col = r.start.col; col <= r.end.col; col++) {
			GnmCell *cell = sheet_cell_get (sheet, col, row);
			if (gnm_cell_is_empty (cell)) {
				gsf_output_puts(out, "1,0\n" "\"\"\n");
			} else if (VALUE_IS_BOOLEAN (cell->value)) {
				if (value_get_as_checked_bool (cell->value))
					gsf_output_puts(out, "0,1\n" "TRUE\n");
				else
					gsf_output_puts(out, "0,0\n" "FALSE\n");
			} else if (VALUE_IS_ERROR (cell->value)) {
				if (value_error_classify (cell->value) == GNM_ERROR_NA)
					gsf_output_puts(out, "0,0\n" "NA\n");
				else
					gsf_output_puts(out, "0,0\n" "ERROR\n");
			} else if (VALUE_IS_FLOAT (cell->value))
				gsf_output_printf (out, "0,%" GNM_FORMAT_g "\n" "V\n",
					value_get_as_float (cell->value));
			else {
				gchar *str = gnm_cell_get_rendered_text (cell);
				ok = gsf_output_printf (out,
							 "1,0\n" "\"%s\"\n",
							 str);
				g_free (str);
			}
		}
	}

	gsf_output_puts (out, "-1,0\n" "EOD\n");

	gnm_pop_C_locale (locale);

	if (!ok)
		go_io_error_string (io_context, _("Error while saving DIF file."));
}
Esempio n. 10
0
/*
 * write every sheet of the workbook to a roff file
 *
 * FIXME: Should roff quote sheet name (and everything else)
 */
void
roff_file_save (GOFileSaver const *fs, GOIOContext *io_context,
                WorkbookView const *wb_view, GsfOutput *output)
{
	GSList *sheets, *ptr;
	GnmCell *cell;
	int row, col, fontsize, v_size;
	Workbook *wb = wb_view_get_workbook (wb_view);

	g_return_if_fail (wb != NULL);

	gsf_output_printf (output, ".\\\" TROFF file\n");
	gsf_output_printf (output, ".fo ''%%''\n");
	sheets = workbook_sheets (wb);
	for (ptr = sheets ; ptr != NULL ; ptr = ptr->next) {
		Sheet *sheet = ptr->data;
		GnmRange r = sheet_get_extent (sheet, FALSE, TRUE);

		gsf_output_printf (output, "%s\n\n", sheet->name_unquoted);
		gsf_output_printf (output, ".TS H\n");
		gsf_output_printf (output, "allbox;\n");

		for (row = r.start.row; row <= r.end.row; row++) {
			ColRowInfo const * ri;
			ri = sheet_row_get_info (sheet, row);
			if (ri->needs_respan)
				row_calc_spans ((ColRowInfo *) ri, row, sheet);

			if (row > r.start.row)
				gsf_output_printf (output, ".T&\n");
			/* define alignments, bold etc. per cell */
			v_size = DEFSIZE;
			for (col = r.start.col; col <= r.end.col; col++) {
				cell = sheet_cell_get (sheet, col, row);
				if (col > r.start.col)
					gsf_output_printf (output, " ");
				if (!cell) {
					gsf_output_printf (output, "l");
				} else {
					GnmStyle const *style = gnm_cell_get_style (cell);
					if (!style)
						break;
					if (gnm_style_get_align_h (style) & GNM_HALIGN_RIGHT)
						gsf_output_printf (output, "r");
					else if (gnm_style_get_align_h (style) == GNM_HALIGN_CENTER ||
						 /* FIXME : center across selection is different */
						 gnm_style_get_align_h (style) == GNM_HALIGN_CENTER_ACROSS_SELECTION ||
						 gnm_style_get_align_h (style) == GNM_HALIGN_DISTRIBUTED)
						gsf_output_printf (output, "c");
					else
						gsf_output_printf (output, "l");
					if (font_is_monospaced (style)) {
						if (gnm_style_get_font_bold (style) &&
						    gnm_style_get_font_italic (style))
							gsf_output_printf (output, "fCBI");
						else if (gnm_style_get_font_bold (style))
							gsf_output_printf (output, "fCB");
						else if (gnm_style_get_font_italic (style))
							gsf_output_printf (output, "fCI");
						else
							gsf_output_printf (output, "fCR");
					} else if (font_is_helvetica (style)) {
						if (gnm_style_get_font_bold (style) &&
						    gnm_style_get_font_italic (style))
							gsf_output_printf (output, "fHBI");
						else if (gnm_style_get_font_bold (style))
							gsf_output_printf (output, "fHB");
						else if (gnm_style_get_font_italic (style))
							gsf_output_printf (output, "fHI");
						else
							gsf_output_printf (output, "fHR");
					} else {
						/* default is times */
						if (gnm_style_get_font_bold (style) &&
						    gnm_style_get_font_italic (style))
							gsf_output_printf (output, "fTBI");
						else if (gnm_style_get_font_bold (style))
							gsf_output_printf (output, "fTB");
						else if (gnm_style_get_font_italic (style))
							gsf_output_printf (output, "fTI");
					}
					fontsize = gnm_style_get_font_size (style);
					if (fontsize) {
						gsf_output_printf (output, "p%d", fontsize);
						v_size = v_size > fontsize ? v_size :
							fontsize;
					}
				}
			}
			gsf_output_printf (output, ".\n");
			gsf_output_printf (output, ".vs %.2fp\n", 2.5 + v_size);
			for (col = r.start.col; col <= r.end.col;  col++) {
				if (col > r.start.col)
					gsf_output_printf (output, "\t");
				cell = sheet_cell_get (sheet, col, row);
				if (!cell) {	/* empty cell */
					gsf_output_printf (output, " ");
				} else {
					roff_fprintf (output, cell);
				}
			}
			gsf_output_printf (output, "\n");
			if (row == r.start.row)
				gsf_output_printf (output, ".TH\n");
		}
		gsf_output_printf (output, ".TE\n\n");
	}
	g_slist_free (sheets);
}