print-cell.c 26.5 KB
Newer Older
1
/*
Morten Welinder's avatar
Morten Welinder committed
2
 * print-cell.c: Printing of cell regions and cells.
3 4 5
 *
 * Author:
 *    Miguel de Icaza 1999 (miguel@kernel.org)
6 7
 *
 * g_unichar_to_utf8: Copyright Red Hat, Inc
8
 * i18n of printing: Copyright 2001 by Vlad Harchev <hvv@hippo.ru>
9
 */
10
#include <gnumeric-config.h>
11
#include "gnumeric.h"
12 13
#include "print-cell.h"

14 15
#include "eval.h"
#include "format.h"
16
#include "style-color.h"
17
#include "parse-util.h"
18
#include "cell.h"
19
#include "value.h"
20
#include "style-border.h"
21 22
#include "pattern.h"
#include "cellspan.h"
23
#include "ranges.h"
24
#include "sheet.h"
Jody Goldberg's avatar
Jody Goldberg committed
25
#include "sheet-style.h"
26
#include "sheet-merge.h"
27
#include "rendered-value.h"
Jody Goldberg's avatar
Jody Goldberg committed
28
#include "str.h"
29

30
#include <ctype.h>
31
#include <locale.h>
32

33 34 35 36 37 38 39 40 41 42 43 44
/*
  Define this to enable i18n-wise printing and string measuring - it requires
  mbstowcs to be available. Most probably printing work fine for ANY locale
  (though gnome-print doesn't support CJK yet - but when it will be ready, no
  changes will be needed in the code used when _PROPER_I18N is defined.
  
  If this macro is undefined, printing will work only for iso-8859-1, so please
  try hard to avoid undefining it.
      - Vlad Harchev <hvv@hippo.ru>
*/
#define _PROPER_I18N

45
#if 0
46
#define MERGE_DEBUG(range, str) do { range_dump (range, str); } while (0)
47 48 49 50
#else
#define MERGE_DEBUG(range, str)
#endif

51
static inline void
52
print_hline (GnomePrintContext *context,
53
	     float x1, float x2, float y)
54
{
55 56 57
	gnome_print_moveto (context, x1, y);
	gnome_print_lineto (context, x2, y);
	gnome_print_stroke (context);
58
}
59

60
#ifndef _PROPER_I18N
61 62 63 64 65 66 67
/*
 * print_show_iso8859_1
 *
 * Like gnome_print_show, but expects an ISO 8859.1 string.
 *
 * NOTE: This function got introduced when gnome-print switched to UTF-8,
 * and will disappear again once Gnumeric makes the switch. Deprecated at
68
 * birth!
69 70 71 72
 */
int
print_show_iso8859_1 (GnomePrintContext *pc, char const *text)
{
73
	gchar *p, *utf, *udyn, ubuf[4096];
74
	gint len, ret, i;
75 76 77 78 79 80

	g_return_val_if_fail (pc && text, -1);

	if (!*text)
		return 0;

81 82 83 84 85
	/* We need only length * 2, because iso-8859-1 is encoded in 1-2 bytes */
	len = strlen (text);
	if (len * 2 > sizeof (ubuf)) {
		udyn = g_new (gchar, len * 2);
		utf = udyn;
86
	} else {
87 88
		udyn = NULL;
		utf = ubuf;
89
	}
90
	p = utf;
91

92 93 94
	for (i = 0; i < len; i++) {
		p += g_unichar_to_utf8 (((guchar *) text)[i], p);
	}
95

96
	ret = gnome_print_show_sized (pc, utf, p - utf);
97

98 99
	if (udyn)
		g_free (udyn);
100 101 102

	return ret;
}
103 104 105 106 107 108 109 110 111
#endif

int
print_show (GnomePrintContext *pc, char const *text)
{
#ifdef _PROPER_I18N
	wchar_t* wcs,wcbuf[4096];
	char* utf8,utf8buf[4096];
	
Morten Welinder's avatar
Morten Welinder committed
112 113
	size_t conv_status;
	int n = strlen (text);
114
	int retval;
Morten Welinder's avatar
Morten Welinder committed
115
	const int wcbuf_len = sizeof (wcbuf) / sizeof (wcbuf[0]);
116 117 118

	g_return_val_if_fail (pc && text, -1);	
	
Morten Welinder's avatar
Morten Welinder committed
119 120
	if ( n > wcbuf_len)
		wcs = g_new (wchar_t,n);
121 122 123
	else
		wcs = wcbuf;

Morten Welinder's avatar
Morten Welinder committed
124
	conv_status = mbstowcs (wcs, text, n);
125 126 127 128 129 130

	if (conv_status == (size_t)(-1)){
		if (wcs != wcbuf)
			g_free (wcs);
		return 0;
	};
Morten Welinder's avatar
Morten Welinder committed
131 132
	if (conv_status * 6 > sizeof (utf8buf))
		utf8 = g_new (gchar, conv_status * 6);
133 134
	else
		utf8 = utf8buf;
Morten Welinder's avatar
Morten Welinder committed
135

136
	{
Morten Welinder's avatar
Morten Welinder committed
137
		size_t i;
138 139 140 141
		char* p = utf8;
		for(i = 0; i < conv_status; ++i)
			p += g_unichar_to_utf8 ( (gint) wcs[i], p);
		if (wcs != wcbuf)
Morten Welinder's avatar
Morten Welinder committed
142
			g_free (wcs);			
143 144 145 146
		retval = gnome_print_show_sized (pc, utf8, p - utf8);			
	}	

	if (utf8 != utf8buf)
Morten Welinder's avatar
Morten Welinder committed
147
		g_free (utf8);
148 149 150 151 152 153 154
	return retval;		
#else
	return print_show_iso8859_1 (pc, text);
#endif
};

double
Morten Welinder's avatar
Morten Welinder committed
155
get_width_string_n (GnomeFont *font, char const* text, guint n)
156 157
{
#ifdef _PROPER_I18N
Morten Welinder's avatar
Morten Welinder committed
158 159
	wchar_t* wcs, wcbuf[4000];
	size_t conv_status, i;
160 161 162
	double total = 0;	
	
	if ( n > (sizeof(wcbuf)/sizeof(wcbuf[0])))
Morten Welinder's avatar
Morten Welinder committed
163
		wcs = g_new (wchar_t,n);
164 165 166
	else
		wcs = wcbuf;

Morten Welinder's avatar
Morten Welinder committed
167
	conv_status = mbstowcs (wcs, text, n);
168 169 170 171 172 173 174

	if (conv_status == (size_t)(-1)){
		if (wcs != wcbuf)
			g_free (wcs);
		return 0;
	};
	for (i = 0; i < conv_status; ++i)
Morten Welinder's avatar
Morten Welinder committed
175 176
		total += gnome_font_get_glyph_width (font, 
				gnome_font_lookup_default (font, wcs[i]));
177 178

	if (wcs != wcbuf)
Morten Welinder's avatar
Morten Welinder committed
179
		g_free (wcs);
180 181 182 183 184 185 186 187 188 189 190 191
	return total;
#else
	return gnome_font_get_width_string_n (font, text, n);
#endif
};


double
get_width_string (GnomeFont *font, char const* text)
{
	return get_width_string_n (font, text, strlen(text));
};
192

193
/***********************************************************/
194

195 196
/*
 * WARNING : This code is an almost exact duplicate of
197
 *          cell-draw.c
198 199
 * Try to keep it that way.
 */
200 201 202 203 204 205 206

static inline void
print_text (GnomePrintContext *context,
	    double x, double text_base, char const * text, double len_pts,
	    double const * const line_offset, int num_lines)
{
	gnome_print_moveto (context, x, text_base);
207
	print_show (context, text);
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

	/* FIXME how to handle small fonts ?
	 * the text_base should be at least 2 pixels above the bottom */
	while (--num_lines >= 0) {
		double y = text_base - line_offset[num_lines];
		gnome_print_newpath (context);
		gnome_print_setlinewidth (context, 0);
		print_hline (context, x, x+len_pts, y);
	}
}

static void
print_overflow (GnomePrintContext *context, GnomeFont *font,
		double x1, double text_base, double width,
		double const * const line_offset, int num_lines)
{
224
	double const len = get_width_string_n (font, "#", 1);
225 226 227 228 229 230 231
	int count = 0;

	if (len != 0)  {
		count = width / len;
		if (count == 0)
			count = 1;
	}
232 233 234 235 236 237 238

	/* Center */
	for (x1 += (width - count*len) / 2; --count >= 0 ; x1 += len )
		print_text (context, x1, text_base, "#", len,
			   line_offset, num_lines);
}

239 240 241 242 243 244 245 246
/*
 * WARNING : This code is an almost exact duplicate of
 *          cell-draw.c:cell_split_text
 * and is very similar to
 *          rendered-value.c:rendered_value_calc_size_ext
 *
 * Try to keep it that way.
 */
247
static GList *
248
cell_split_text (GnomeFont *font, char const *text, int const width)
249
{
250 251 252 253 254 255
	char const *p, *line_begin;
	char const *first_whitespace = NULL;
	char const *last_whitespace = NULL;
	gboolean prev_was_space = FALSE;
	GList *list = NULL;
	double used = 0., used_last_space = 0.;
256

257
	for (line_begin = p = text; *p; p++) {
258
		double const len_current =
259
			get_width_string_n (font, p, 1);
260

261
		/* Wrap if there is an embeded newline, or we have overflowed */
262
		if (*p == '\n' || used + len_current > width) {
263
			char const *begin = line_begin;
264 265
			int len;

266
			if (*p == '\n') {
267 268 269 270
				/* start after newline, preserve whitespace */
				line_begin = p+1;
				len = p - begin;
				used = 0.;
271
			} else if (last_whitespace != NULL) {
272 273 274 275
				/* Split at the run of whitespace */
				line_begin = last_whitespace + 1;
				len = first_whitespace - begin;
				used = len_current + used - used_last_space;
276
			} else {
277 278 279 280
				/* Split before the current character */
				line_begin = p; /* next line starts here */
				len = p - begin;
				used = len_current;
281 282
			}

283 284 285 286 287
			list = g_list_append (list, g_strndup (begin, len));
			first_whitespace = last_whitespace = NULL;
			prev_was_space = FALSE;
			continue;
		}
288

289
		used += len_current;
290 291 292 293 294 295
		if (*p == '-') {
			used_last_space = used;
			last_whitespace = p;
			first_whitespace = p+1;
			prev_was_space = TRUE;
		} else if (isspace (*(unsigned char *)p)) {
296 297 298 299 300 301 302
			used_last_space = used;
			last_whitespace = p;
			if (!prev_was_space)
				first_whitespace = p;
			prev_was_space = TRUE;
		} else
			prev_was_space = FALSE;
303 304
	}

305 306 307 308 309
	/* Catch the final bit that did not wrap */
	if (*line_begin)
		list = g_list_append (list,
				      g_strndup (line_begin, p - line_begin));

310 311 312
	return list;
}

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
/*
 * print_make_rectangle_path
 * @pc      print context
 * @left    left side x coordinate
 * @bottom  bottom side y coordinate
 * @right   right side x coordinate
 * @top     top side y coordinate
 *
 * Make a rectangular path.
 */
void
print_make_rectangle_path (GnomePrintContext *pc,
			   double left, double bottom,
			   double right, double top)
{
	g_return_if_fail (pc != NULL);

	gnome_print_newpath   (pc);
	gnome_print_moveto    (pc, left, bottom);
	gnome_print_lineto    (pc, left, top);
	gnome_print_lineto    (pc, right, top);
	gnome_print_lineto    (pc, right, bottom);
	gnome_print_closepath (pc);
}

Jody Goldberg's avatar
Jody Goldberg committed
338
/*
339
 * base_[xy] : Coordinates of the upper left corner of the cell.
Jody Goldberg's avatar
Jody Goldberg committed
340 341
 *             INCLUSIVE of the near grid line
 *
342 343 344
 *      /--- (x1, y1)
 *      v
 *      g------\
Jody Goldberg's avatar
Jody Goldberg committed
345
 *      |      |
346
 *      \------/
Jody Goldberg's avatar
Jody Goldberg committed
347
 */
348
static void
349
print_cell (Cell const *cell, MStyle const *mstyle, GnomePrintContext *context,
Jody Goldberg's avatar
Jody Goldberg committed
350
	    double x1, double y1, double width, double height, double h_center)
351
{
352
	StyleFont *style_font = mstyle_get_font (mstyle, 1.0);
Jody Goldberg's avatar
Jody Goldberg committed
353 354
#warning FIXME
	GnomeFont *print_font = NULL; /* style_font->font; */
Jody Goldberg's avatar
Jody Goldberg committed
355
	double const font_descent = gnome_font_get_descender (print_font);
356
	double const font_ascent = gnome_font_get_ascender (print_font);
357
	double rect_x, rect_width, rect_y, rect_height;
358

359 360 361
	Sheet const * const sheet = cell->base.sheet;
	ColRowInfo const * const ci = cell->col_info; /* DEPRECATED */
	ColRowInfo const * const ri = cell->row_info; /* DEPRECATED */
Jody Goldberg's avatar
Jody Goldberg committed
362
	double text_base;
363
	double font_height;
364 365
	StyleHAlignFlags halign;
	StyleVAlignFlags valign;
Jody Goldberg's avatar
Jody Goldberg committed
366
	int num_lines = 0;
367
	double line_offset [3]; /* There are up to 3 lines, double underlined strikethroughs */
Jody Goldberg's avatar
Jody Goldberg committed
368
	char const *text;
Jody Goldberg's avatar
Jody Goldberg committed
369
	StyleColor *fore;
370
	double cell_width_pts, indent = 0.;
Jody Goldberg's avatar
Jody Goldberg committed
371

372
	/* Don't print zeros if they should be ignored. */
373 374
	if (sheet && sheet->hide_zero && cell_is_zero (cell) &&
	    (!sheet->display_formulas || !cell_has_expr (cell)))
375 376
		return;

377 378 379
	if (cell->rendered_value == NULL)
		cell_render_value ((Cell *)cell, TRUE);

Jody Goldberg's avatar
Jody Goldberg committed
380 381
	g_return_if_fail (cell->rendered_value->rendered_text);

382 383
	if (cell->rendered_value->rendered_text->str == NULL) {
		g_warning ("Serious cell error at '%s'\n", cell_name (cell));
Jody Goldberg's avatar
Jody Goldberg committed
384 385 386
		/* This can occur when eg. a plugin function fires up a dialog */
		text = "Pending";
	} else
387
		text = cell->rendered_value->rendered_text->str;
Jody Goldberg's avatar
Jody Goldberg committed
388

389
	/* Get the sizes exclusive of margins and grids */
390 391
	/* FIXME : all callers will eventually pass in their cell size */
	if (width < 0) /* DEPRECATED */
392
		width  = ci->size_pts - (ci->margin_b + ci->margin_a + 1.);
393
	if (height < 0) /* DEPRECATED */
394
		height = ri->size_pts - (ri->margin_b + ri->margin_a + 1.);
395

396 397 398 399 400
	/* This rectangle has the whole area used by this cell
	 * excluding the surrounding grid lines and margins */
	if (width <= 0 || height <= 0)
		return;

401 402 403 404
	/* This rectangle has the whole area used by this cell
	 * excluding the surrounding grid lines and margins */
	rect_x = x1 + 1 + ci->margin_a;
	rect_y = y1 - 1 - ri->margin_a;
Jody Goldberg's avatar
Jody Goldberg committed
405 406
	rect_width = width + 1;
	rect_height = height + 1;
407

Jody Goldberg's avatar
Jody Goldberg committed
408
	font_height = style_font->size_pts;
409
	valign = mstyle_get_align_v (mstyle);
410

411
	switch (valign) {
Jody Goldberg's avatar
Jody Goldberg committed
412 413
	default:
		g_warning ("Unhandled cell vertical alignment\n");
414

Jody Goldberg's avatar
Jody Goldberg committed
415 416 417
	case VALIGN_JUSTIFY:
	case VALIGN_TOP:
		/*
418
		 * rect.y == first pixel past margin
Jody Goldberg's avatar
Jody Goldberg committed
419 420
		 * add font ascent
		 */
421
		text_base = rect_y - font_ascent;
Jody Goldberg's avatar
Jody Goldberg committed
422
		break;
423

Jody Goldberg's avatar
Jody Goldberg committed
424
	case VALIGN_CENTER:
425
		text_base = rect_y - font_ascent -
Jody Goldberg's avatar
Jody Goldberg committed
426 427
		    (height - font_height) / 2;
		break;
428

Jody Goldberg's avatar
Jody Goldberg committed
429
	case VALIGN_BOTTOM:
430
		/*
431 432
		 * rect.y == first pixel past margin
		 * add height == first pixel in lower margin
Jody Goldberg's avatar
Jody Goldberg committed
433
		 * subtract font descent
434
		 */
435
		text_base = rect_y - height + font_descent;
Jody Goldberg's avatar
Jody Goldberg committed
436 437
		break;
	}
438

439
	/* Do not allow text to impinge upon the grid lines or margins
440
	 * FIXME : Should use margins from spaninfo->left and spaninfo->right
441
	 *
Jody Goldberg's avatar
Jody Goldberg committed
442
	 * NOTE : postscript clip paths exclude near the border, gdk includes it.
443
	 */
444
	gnome_print_gsave (context);
445 446 447 448 449
	print_make_rectangle_path (context,
				   rect_x - 1.,
				   rect_y - rect_height - 1.,
				   rect_x + rect_width + 1.,
				   rect_y + 1.);
450
	gnome_print_clip (context);
451 452

	/* Set the font colour */
453
	fore = cell->rendered_value->render_color;
Jody Goldberg's avatar
Jody Goldberg committed
454
	if (fore == NULL)
455
		fore = mstyle_get_color (mstyle, MSTYLE_COLOR_FORE);
Jody Goldberg's avatar
Jody Goldberg committed
456
	g_return_if_fail (fore != NULL); /* Be extra careful */
457 458 459 460 461
	gnome_print_setrgbcolor (context,
				 fore->red   / (double) 0xffff,
				 fore->green / (double) 0xffff,
				 fore->blue  / (double) 0xffff);

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
	/* Handle underlining and strikethrough */
	switch (mstyle_get_font_uline (mstyle)) {
	case UNDERLINE_SINGLE : num_lines = 1;
				line_offset[0] = 1.;
				break;

	case UNDERLINE_DOUBLE : num_lines = 2;
				line_offset[0] = 0.;
				line_offset[1] = 2.;

	default :
				break;
	};
	if (mstyle_get_font_strike (mstyle))
		line_offset[num_lines++] = font_ascent/-2;

478
	/* FIXME : This will be wrong for JUSTIFIED halignments */
479
	halign = style_default_halign (mstyle, cell);
480
	cell_width_pts = get_width_string (print_font, text);
481 482 483 484 485
	if (halign == HALIGN_LEFT || halign == HALIGN_RIGHT) {
		/* 2*width seems to be pretty close to XL's notion */
		indent = mstyle_get_indent (mstyle) *
			2. * style_font->approx_width.pts;
	}
Jody Goldberg's avatar
Jody Goldberg committed
486 487

	/* if a number overflows, do special drawing */
488
	if ((cell_width_pts + indent) > width && cell_is_number (cell) &&
489
	    sheet && !sheet->display_formulas) {
490
		print_overflow (context, print_font, rect_x,
491
				text_base, width, line_offset, num_lines);
Jody Goldberg's avatar
Jody Goldberg committed
492
		style_font_unref (style_font);
493
		gnome_print_grestore (context);
Jody Goldberg's avatar
Jody Goldberg committed
494 495 496
		return;
	}

497 498 499
	if (halign == HALIGN_CENTER_ACROSS_SELECTION || h_center <= 0.)
		h_center = width / 2.;

500
	if (halign != HALIGN_JUSTIFY && valign != VALIGN_JUSTIFY &&
501
	    !mstyle_get_wrap_text (mstyle)) {
502
		double x, total, len = cell_width_pts;
503 504

		switch (halign) {
505
		case HALIGN_FILL: /* fall through */
506
		case HALIGN_LEFT:
Jody Goldberg's avatar
Jody Goldberg committed
507
			x = rect_x + indent;
508
			break;
Jody Goldberg's avatar
Jody Goldberg committed
509

510
		case HALIGN_RIGHT:
Jody Goldberg's avatar
Jody Goldberg committed
511
			x = rect_x + rect_width - 1 - cell_width_pts -indent;
512 513 514
			break;

		case HALIGN_CENTER:
515
		case HALIGN_CENTER_ACROSS_SELECTION:
Jody Goldberg's avatar
Jody Goldberg committed
516
			x = rect_x + h_center - cell_width_pts / 2;
517 518
			break;

519 520
		default:
			g_warning ("Single-line justitfication style not supported\n");
521
			x = rect_x;
522 523 524 525
			break;
		}

		gnome_print_setfont (context, print_font);
526
		total = len; /* don't include partial copies after the first */
527
		do {
528
			print_text (context, x, text_base, text, len,
529
				    line_offset, num_lines);
Jody Goldberg's avatar
Jody Goldberg committed
530

531
			x += len;
532
			total += len;
533
		} while (halign == HALIGN_FILL && total < rect_width && len > 0);
534
	} else {
535
		GList *lines, *l;
536
		int line_count;
537
		double x, y_offset, inter_space;
Jody Goldberg's avatar
Jody Goldberg committed
538

539
		lines = cell_split_text (print_font, text, width);
540
		line_count = g_list_length (lines);
Jody Goldberg's avatar
Jody Goldberg committed
541

542
		switch (valign) {
543
		case VALIGN_TOP:
544
			y_offset = 0.;
545 546
			inter_space = font_height;
			break;
Jody Goldberg's avatar
Jody Goldberg committed
547

548
		case VALIGN_CENTER:
Jody Goldberg's avatar
Jody Goldberg committed
549
			y_offset = ((height -
550
				       (line_count * font_height)) / 2);
551 552
			inter_space = font_height;
			break;
Jody Goldberg's avatar
Jody Goldberg committed
553

554
		case VALIGN_JUSTIFY:
555
			if (line_count > 1) {
556
				y_offset = 0;
Jody Goldberg's avatar
Jody Goldberg committed
557
				inter_space = font_height +
Jody Goldberg's avatar
Jody Goldberg committed
558
					(height - (line_count * font_height))
559
					/ (line_count - 1);
560 561 562 563

				/* lines should not overlap */
				if (inter_space < font_height)
					inter_space = font_height;
564
				break;
Jody Goldberg's avatar
Jody Goldberg committed
565
			}
566
			/* Else, we become a VALIGN_BOTTOM line */
Jody Goldberg's avatar
Jody Goldberg committed
567

568
		case VALIGN_BOTTOM:
Jody Goldberg's avatar
Jody Goldberg committed
569
			y_offset = (height - (line_count * font_height));
570 571
			inter_space = font_height;
			break;
Jody Goldberg's avatar
Jody Goldberg committed
572

573 574 575 576 577 578 579 580
		default:
			g_warning ("Unhandled cell vertical alignment\n");
			y_offset = 0;
			inter_space = font_height;
		}

		gnome_print_setfont (context, print_font);

Jody Goldberg's avatar
Jody Goldberg committed
581
		y_offset += font_height - 1;
582
		for (l = lines; l; l = l->next) {
583
			char const * const str = l->data;
584
			double len = 0.;
585

586
			switch (halign) {
587 588
			default:
				g_warning ("Multi-line justification style not supported\n");
Jody Goldberg's avatar
Jody Goldberg committed
589
			case HALIGN_JUSTIFY:
590
				/* fall through */
591
			case HALIGN_LEFT:
Jody Goldberg's avatar
Jody Goldberg committed
592
				x = rect_x + indent;
593 594 595

				/* Be cheap, only calculate the width of the
				 * string if we need to. */
596
				if (num_lines > 0)
597
					len = get_width_string (print_font, str);
598
				break;
Jody Goldberg's avatar
Jody Goldberg committed
599

600
			case HALIGN_RIGHT:
601
				len = get_width_string (print_font, str);
Jody Goldberg's avatar
Jody Goldberg committed
602
				x = rect_x + rect_width - 1 - len - indent;
603 604 605
				break;

			case HALIGN_CENTER:
606
			case HALIGN_CENTER_ACROSS_SELECTION:
607
				len = get_width_string (print_font, str);
Jody Goldberg's avatar
Jody Goldberg committed
608
				x = rect_x + h_center - len / 2;
609 610
			}

611
			print_text (context,
612
				    x, y1 - y_offset, str,
613
				    len, line_offset, num_lines);
Jody Goldberg's avatar
Jody Goldberg committed
614

Jody Goldberg's avatar
Jody Goldberg committed
615 616
			y_offset += inter_space;

617 618 619 620
			g_free (l->data);
		}
		g_list_free (lines);
	}
621
	style_font_unref (style_font);
Jody Goldberg's avatar
Jody Goldberg committed
622

623
	gnome_print_grestore (context);
624 625
}

626 627
/* We do not use print_make_rectangle_path here - because we do not want a
 * new path.  */
628
static void
629 630
print_rectangle (GnomePrintContext *context,
		 double x, double y, double w, double h)
631
{
632 633 634
	/* Mirror gdk which excludes the far point */
	w -= 1.;
	h -= 1.;
635 636 637 638 639 640 641
	gnome_print_moveto (context, x, y);
	gnome_print_lineto (context, x+w, y);
	gnome_print_lineto (context, x+w, y-h);
	gnome_print_lineto (context, x, y-h);
	gnome_print_lineto (context, x, y);
	gnome_print_fill (context);
}
642

643
static void
644 645 646
print_cell_background (GnomePrintContext *context,
		       MStyle const *style, int col, int row,
		       float x, float y, float w, float h)
647
{
648 649
	if (gnumeric_background_set_pc (style, context))
		/* Fill the entire cell (API excludes far pixel) */
650
		print_rectangle (context, x, y, w+1, h+1);
651 652
}

653 654 655 656 657 658 659 660
/**
 * print_merged_range:
 *
 * Handle the special drawing requirements for a 'merged cell'.
 * First draw the entire range (clipped to the visible region) then redraw any
 * segments that are selected.
 */
static void
661
print_merged_range (GnomePrintContext *context, Sheet const *sheet,
662 663 664
		    double start_x, double start_y,
		    Range const *view, Range const *range)
{
665
	float l, r, t, b;
666 667
	Cell  const *cell    = sheet_cell_get (sheet, range->start.col, range->start.row);
	MStyle const *mstyle = sheet_style_get (sheet, range->start.col, range->start.row);
668

669 670 671 672 673 674 675 676 677
	l = sheet_col_get_distance_pts (sheet,
		view->start.col, range->start.col) + start_x;
	r = sheet_col_get_distance_pts (sheet,
		view->start.col, range->end.col+1) + start_x;

	t = sheet_row_get_distance_pts (sheet,
		view->start.row, range->start.row) + start_y;
	b = sheet_row_get_distance_pts (sheet,
		view->start.row, range->end.row+1) + start_y;
678

679 680 681
	if (gnumeric_background_set_pc (mstyle, context))
		/* Remember api excludes the far pixels */
		print_rectangle (context, l, t, r-l+1, b-t+1);
682 683 684 685 686

	if (cell != NULL) {
		ColRowInfo const * const ri = cell->row_info;
		ColRowInfo const * const ci = cell->col_info;

687
		/* FIXME : get the margins from the far col/row too */
688
		print_cell (cell, mstyle, context,
689 690 691
			    l, t,
			    r - l - ci->margin_b - ci->margin_a,
			    b - t - ri->margin_b - ri->margin_a, -1.);
692 693 694 695 696 697 698 699 700
	}
}

static gint
merged_col_cmp (Range const *a, Range const *b)
{
	return a->start.col - b->start.col;
}

701
void
702
print_cell_range (GnomePrintContext *context,
703
		  Sheet const *sheet, Range *range,
Miguel de Icaza's avatar
Miguel de Icaza committed
704
		  double base_x, double base_y,
705
		  gboolean hide_grid)
706
{
707
	int n, col, row;
708
	double x, y;
709
	ColRowInfo const *ri = NULL, *next_ri = NULL;
710
	int start_row, start_col, end_col, end_row;
711

712 713 714 715
	StyleRow sr, next_sr;
	MStyle const **styles;
	StyleBorder const **borders, **prev_vert;
	StyleBorder const *none =
716
		hide_grid ? NULL : style_border_none ();
717

718 719 720
	Range     view;
	GSList	 *merged_active, *merged_active_seen,
		 *merged_used, *merged_unused, *ptr, **lag;
721

722 723
	g_return_if_fail (GNOME_IS_PRINT_CONTEXT (context));
	g_return_if_fail (IS_SHEET (sheet));
724 725 726 727 728 729 730 731
	g_return_if_fail (range != NULL);
	g_return_if_fail (range->start.col <= range->end.col);
	g_return_if_fail (range->start.row <= range->end.row);

	start_col = range->start.col;
	start_row = range->start.row;
	end_col = range->end.col;
	end_row = range->end.row;
732

733 734 735 736 737 738
	/* Skip any hidden cols/rows at the start */
	for (; start_col <= end_col ; ++start_col) {
		ri = sheet_col_get_info (sheet, start_col);
		if (ri->visible)
			break;
	}
739 740 741 742 743
	for (; start_row <= end_row ; ++start_row) {
		ri = sheet_row_get_info (sheet, start_row);
		if (ri->visible)
			break;
	}
744

745 746
	sheet_style_update_grid_color (sheet);
	
747
	/* Get ordered list of merged regions */
748 749 750 751
	merged_active = merged_active_seen = merged_used = NULL;
	merged_unused = sheet_merge_get_overlap (sheet,
		range_init (&view, start_col, start_row, end_col, end_row));

752 753 754 755 756 757 758 759 760 761
	/*
	 * allocate a single blob of memory for all 8 arrays of pointers.
	 * 	- 6 arrays of n StyleBorder const *
	 * 	- 2 arrays of n MStyle const *
	 *
	 * then alias the arrays for easy access so that array [col] is valid
	 * for all elements start_col-1 .. end_col+1 inclusive.
	 * Note that this means that in some cases array [-1] is legal.
	 */
	n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */
762 763
	style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col,
			g_alloca (n * 8 * sizeof (gpointer)), hide_grid);
764 765 766 767 768 769 770 771 772

	/* load up the styles for the first row */
	next_sr.row = sr.row = row = start_row;
	sheet_style_get_row (sheet, &sr);

	for (y = base_y; row <= end_row; row = sr.row = next_sr.row, ri = next_ri) {
		/* Restore the set of ranges seen, but still active.
		 * Reinverting list to maintain the original order */
		g_return_if_fail (merged_active == NULL);
773 774 775 776 777 778 779 780 781

		while (merged_active_seen != NULL) {
			GSList *tmp = merged_active_seen->next;
			merged_active_seen->next = merged_active;
			merged_active = merged_active_seen;
			merged_active_seen = tmp;
			MERGE_DEBUG (merged_active->data, " : seen -> active\n");
		}

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
		/* find the next visible row */
		while (1) {
			++next_sr.row;
			if (next_sr.row <= end_row) {
				next_ri = sheet_row_get_info (sheet, next_sr.row);
				if (next_ri->visible) {
					sheet_style_get_row (sheet, &next_sr);
					break;
				}
			} else {
				for (col = start_col ; col <= end_col; ++col)
					next_sr.vertical [col] =
					next_sr.bottom [col] = none;
				break;
			}
		}

799 800
		/* look for merges that start on this row, on the first painted row
		 * also check for merges that start above. */
801
		view.start.row = row;
802 803 804 805
		lag = &merged_unused;
		for (ptr = merged_unused; ptr != NULL; ) {
			Range * const r = ptr->data;

806
			if (r->start.row <= row) {
807 808
				GSList *tmp = ptr;
				ptr = *lag = tmp->next;
809 810 811 812 813 814 815
				if (r->end.row < row) {
					tmp->next = merged_used;
					merged_used = tmp;
					MERGE_DEBUG (r, " : unused -> used\n");
				} else {
					ColRowInfo const *ci =
						sheet_col_get_info (sheet, r->start.col);
816 817 818 819 820
				g_slist_free_1 (tmp);
				merged_active = g_slist_insert_sorted (merged_active, r,
							(GCompareFunc)merged_col_cmp);
				MERGE_DEBUG (r, " : unused -> active\n");

821
					if (ci->visible)
822
				print_merged_range (context, sheet,
823
						    base_x, y, &view, r);
824
				}
825 826 827 828 829 830
			} else {
				lag = &(ptr->next);
				ptr = ptr->next;
			}
		}

831 832 833 834
		for (col = start_col, x = base_x; col <= end_col ; col++) {
			MStyle const *style;
			CellSpanInfo const *span;
			ColRowInfo const *ci = sheet_col_get_info (sheet, col);
Miguel de Icaza's avatar
Miguel de Icaza committed
835

Jody Goldberg's avatar
Jody Goldberg committed
836 837 838 839