print.c 41.1 KB
Newer Older
1 2 3 4 5
/*
 * print.c: Printing routines for Gnumeric
 *
 * Author:
 *    Miguel de Icaza (miguel@gnu.org)
Miguel de Icaza's avatar
Miguel de Icaza committed
6
 *
7
 * Handles printing of Sheets.
8
 */
9
#include <gnumeric-config.h>
10
#include <glib/gi18n.h>
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
11
#include "gnumeric.h"
12 13
#include "print.h"

14
#include "gui-util.h"
15
#include "sheet-object.h"
16
#include "sheet-object-impl.h"
17
#include "selection.h"
18
#include "workbook.h"
Jody Goldberg's avatar
Jody Goldberg committed
19
#include "workbook-control.h"
20
#include "workbook-edit.h"
21
#include "command-context.h"
22
#include "dialogs.h"
23
#include "gnumeric-gconf.h"
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
24
#include "libgnumeric.h"
25 26
#include "sheet.h"
#include "value.h"
27
#include "cellspan.h"
28 29
#include "print-info.h"
#include "print-cell.h"
30
#include "application.h"
Morten Welinder's avatar
Morten Welinder committed
31
#include "sheet-style.h"
Jeffrey Stedfast's avatar
Jeffrey Stedfast committed
32
#include "ranges.h"
Jody Goldberg's avatar
Jody Goldberg committed
33
#include "style.h"
34
#include "gnumeric-gconf.h"
35

36
#include <libgnomeprint/gnome-print-job.h>
Jody Goldberg's avatar
Jody Goldberg committed
37
#include <libgnomeprint/gnome-print-config.h>
38 39 40
#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
#include <libgnomeprint/gnome-print-pango.h>
#endif
41
#include <libgnomeprintui/gnome-print-job-preview.h>
Jody Goldberg's avatar
Jody Goldberg committed
42
#include <libgnomeprintui/gnome-print-dialog.h>
43

44 45
#define PRINT_DIALOG_KEY "Gnumeric_Print_Dialog"

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/*
 * Margins
 *
 * In the internal format, top and bottom margins, header and footer have
 * the same semantics as in Excel.  That is:
 *
 * +----------------------------------------+
 * |   ^            ^                       |
 * |   | top        |header                 |
 * |   |margin      v                       |
 * |-- |-------------                       |
 * |   v           header text              |
 * |------+--------------------------+------|
 * |      |                          |      |
 * |<---->|                          |<---->| ^
 * | left |           Cell           |right | |Increasing
 * |margin|                          |margin| |y
 * |      |           Grid           |      | |
 *
 * ~      ~                          ~      ~
 *
 * |      |                          |      |
 * |------+--------------------------+------|
 * |   ^           footer text              |
 * |-- |------------------------------------|
 * |   |bottom      ^                       |
 * |   |margin      |footer                 |
 * |   v            v                       |
 * +----------------------------------------+
 *
 * In the GUI on the other hand, top margin means the empty space above the
 * header text, and header area means the area from the top of the header
 * text to the top of the cell grid.  */

80 81 82 83 84 85 86
typedef struct {
	/*
	 * Part 1: The information the user configures on the Print Dialog
	 */
	PrintRange range;
	int start_page, end_page; /* Interval */
	gboolean sorted_print;
87
	gboolean is_preview;
88

89 90
	int current_output_sheet;		/* current sheet number during output */

91 92 93
	/*
	 * Part 2: Handy pre-computed information passed around
	 */
Miguel de Icaza's avatar
Miguel de Icaza committed
94 95 96 97 98 99 100 101 102 103
	double width, height;	/* total dimensions */
	double x_points;	/* real usable X (ie, width - margins) */
	double y_points;	/* real usable Y (ie, height - margins) */
	double titles_used_x;	/* points used by the X titles */
	double titles_used_y;	/* points used by the Y titles */

	/* The repeat columns/rows ranges used space */
	double repeat_cols_used_x;
	double repeat_rows_used_y;

104 105 106
	/*
	 * Part 3: Handy pointers
	 */
107
	Sheet *sheet;
108 109
	PrintInformation *pi;
	GnomePrintContext *print_context;
110

111 112
	/*
	 * Part 5: Headers and footers
113 114 115
	 */
	HFRenderInfo *render_info;
	GnomeFont    *decoration_font;
116 117 118

	/* 6: The config */
	GnomePrintConfig *gp_config;
119 120 121
} PrintJobInfo;

static void
Jody Goldberg's avatar
Jody Goldberg committed
122
print_titles (PrintJobInfo const *pj, Sheet const *sheet, GnmRange *range,
123
	      double base_x, double base_y)
Miguel de Icaza's avatar
Miguel de Icaza committed
124 125 126
{
}

127
static void
Jody Goldberg's avatar
Jody Goldberg committed
128
print_sheet_objects (PrintJobInfo const *pj, Sheet const *sheet, GnmRange *range,
129 130 131
		     double base_x, double base_y)
{
	GList *l;
132
	double end_x, end_y;
133 134 135

	g_return_if_fail (IS_SHEET (sheet));
	g_return_if_fail (pj != NULL);
136
	g_return_if_fail (range != NULL);
137

138 139 140 141 142 143
	gnome_print_gsave (pj->print_context);

	/*
	 * Make sure the object doesn't go beyond the specified
	 * cells.
	 */
144 145 146 147
	end_x = base_x + sheet_col_get_distance_pts (sheet, range->start.col,
						     range->end.col + 1);
	end_y = base_y - sheet_row_get_distance_pts (sheet, range->start.row,
						     range->end.row + 1);
148 149 150 151 152 153 154 155 156 157 158 159 160 161
	print_make_rectangle_path (pj->print_context, base_x, base_y,
				   end_x, end_y);
#ifndef NO_DEBUG_PRINT
	if (print_debugging > 0) {
		gnome_print_gsave (pj->print_context);
		gnome_print_stroke (pj->print_context);
		gnome_print_moveto (pj->print_context, base_x, base_y);
		gnome_print_lineto (pj->print_context, end_x, end_y);
		gnome_print_stroke (pj->print_context);
		gnome_print_grestore (pj->print_context);
	}
#endif
	gnome_print_clip (pj->print_context);

162 163 164 165
	for (l = sheet->sheet_objects; l; l = l->next) {
		SheetObject *so = SHEET_OBJECT (l->data);
		double coords [4];

166
		if (!sheet_object_can_print (so) ||
Jody Goldberg's avatar
Jody Goldberg committed
167
		    !range_overlap (range, &so->anchor.cell_bound))
168 169
			continue;

170
		sheet_object_position_pts_get (so, coords);
171 172 173 174 175 176 177
		gnome_print_gsave (pj->print_context);
		/* move to top left */
		gnome_print_translate (pj->print_context,
			base_x + (MIN (coords [0], coords [2])
			    - sheet_col_get_distance_pts (sheet, 0, range->start.col)),
			base_y - (MIN (coords [3], coords [1])
			    - sheet_row_get_distance_pts (sheet, 0, range->start.row)));
178 179

		sheet_object_print (so, pj->print_context,
180 181 182
			fabs (coords[2] - coords[0]),
			fabs (coords[3] - coords[1]));
		gnome_print_grestore (pj->print_context);
183
	}
184 185

	gnome_print_grestore (pj->print_context);
186 187
}

188
static void
Jody Goldberg's avatar
Jody Goldberg committed
189
print_page_cells (PrintJobInfo const *pj, Sheet const *sheet, GnmRange *range,
190
		  double base_x, double base_y)
191
{
192
	/* Invert PostScript Y coordinates to make X&Y cases the same */
193
	base_y = (pj->height / (pj->pi->scaling.percentage.y / 100.)) - base_y;
194

Jody Goldberg's avatar
Jody Goldberg committed
195
	print_cell_range (pj->print_context, sheet, range,
196
			  base_x, base_y, !pj->pi->print_grid_lines);
197
	print_sheet_objects (pj, sheet, range, base_x, base_y);
198 199
}

200 201 202 203 204 205
/*
 * print_page_repeated_rows
 *
 * It is up to the caller to determine if repeated rows should be printed on
 * the page.
 */
Miguel de Icaza's avatar
Miguel de Icaza committed
206
static void
207
print_page_repeated_rows (PrintJobInfo const *pj, Sheet const *sheet,
208
			  int start_col, int end_col,
209
			  double base_x, double base_y)
Miguel de Icaza's avatar
Miguel de Icaza committed
210
{
Jody Goldberg's avatar
Jody Goldberg committed
211 212
	GnmRange const *r = &pj->pi->repeat_top.range;
	GnmRange range;
213

214 215 216
	range_init (&range, start_col, MIN (r->start.row, r->end.row),
			    end_col,   MAX (r->start.row, r->end.row));
	print_page_cells (pj, sheet, &range, base_x, base_y);
Miguel de Icaza's avatar
Miguel de Icaza committed
217 218
}

219 220 221 222
/*
 * print_page_repeated_cols
 *
 * It is up to the caller to determine if repeated columns should be printed
223
 * on the page.
224
 */
Miguel de Icaza's avatar
Miguel de Icaza committed
225
static void
226
print_page_repeated_cols (PrintJobInfo const *pj, Sheet const *sheet,
227
			  int start_row, int end_row,
228
			  double base_x, double base_y)
Miguel de Icaza's avatar
Miguel de Icaza committed
229
{
Jody Goldberg's avatar
Jody Goldberg committed
230 231
	GnmRange const *r = &pj->pi->repeat_left.range;
	GnmRange range;
232

233 234 235
	range_init (&range, MIN (r->start.col, r->end.col), start_row,
			    MAX (r->start.col, r->end.col), end_row);
	print_page_cells (pj, sheet, &range, base_x, base_y);
Miguel de Icaza's avatar
Miguel de Icaza committed
236 237
}

238 239
/*
 * print_page_repeated_intersect
240
 *
241 242 243 244 245 246
 * Print the corner where repeated rows and columns intersect.
 *
 * It is impossible to print both from rows and columns. XL prints the cells
 * from the rows, whether order is row and column major. We do the same.
 */
static void
247
print_page_repeated_intersect (PrintJobInfo const *pj, Sheet const *sheet,
248
			       double base_x, double base_y,
249
			       double print_width, double print_height)
250
{
251
	print_page_repeated_rows (pj, sheet,
252 253
				  pj->pi->repeat_left.range.start.col,
				  pj->pi->repeat_left.range.end.col,
254
				  base_x, base_y);
255 256
}

257 258 259
typedef enum {
	LEFT_HEADER,
	RIGHT_HEADER,
260
	MIDDLE_HEADER
261 262
} HFSide;

263 264 265 266 267 268 269
static const PangoAlignment hfside_to_alignment[] = {
	PANGO_ALIGN_LEFT,
	PANGO_ALIGN_RIGHT,
	PANGO_ALIGN_CENTER
};


270
/*
271
 * print_hf_element
272
 * @pj: printing context
273 274 275
 * @format:
 * @side:
 * @y:
276
 *
277 278 279 280 281
 * Print a header/footer line.
 *
 * Position at y, and clip to rectangle. If print_debugging is > 0, display
 * the rectangle.
 */
282
static void
283 284
print_hf_element (PrintJobInfo const *pj, char const *format,
		  HFSide side, double y)
285 286
{
	char *text;
287

288 289 290 291
	g_return_if_fail (pj != NULL);
	g_return_if_fail (pj->decoration_font != NULL);
	g_return_if_fail (pj->render_info != NULL);

292 293
	text = hf_format_render (format, pj->render_info, HF_RENDER_PRINT);

294 295
	g_return_if_fail (text != NULL);

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 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 338 339 340 341 342 343 344
	if (text[0]) {
#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
		const double dummy_dpi = 300; /* FIXME: What exactly is this?  */
		PangoLayout *layout =
			gnome_print_pango_create_layout (pj->print_context);
		PangoFontDescription *pango_font =
			gnome_font_get_pango_description (pj->decoration_font, dummy_dpi);
		int height;
		double header = 0, footer = 0, left = 0, right = 0;

		print_info_get_margins (pj->pi, &header, &footer, &left, &right);
		pango_layout_set_alignment (layout,
					    hfside_to_alignment[side]);
		pango_layout_set_font_description (layout, pango_font);
		pango_layout_set_width (layout, (pj->width - left - right) * PANGO_SCALE);
		pango_layout_set_text (layout, text, -1);

		pango_layout_get_size (layout, NULL, &height);

		gnome_print_moveto (pj->print_context,
				    left,
				    y + height / (double)PANGO_SCALE);
		gnome_print_pango_layout (pj->print_context, layout);

		g_object_unref (layout);
		pango_font_description_free (pango_font);
#else
		/* This code will die when we require libgnomeprint 2.8  */
		double x;
		double len = gnome_font_get_width_utf8 (pj->decoration_font, text);
		double header = 0, footer = 0, left = 0, right = 0;

		print_info_get_margins (pj->pi, &header, &footer, &left, &right);
		switch (side){
		case LEFT_HEADER:
			x = left;
			break;
		case RIGHT_HEADER:
			x = pj->width - right - len;
			break;
		case MIDDLE_HEADER:
			x = (pj->x_points - len) / 2 + left;
			break;
		default:
			x = 0;
		}
		gnome_print_moveto (pj->print_context, x, y);
		gnome_print_show (pj->print_context, text);
#endif
345
	}
346 347 348
	g_free (text);
}

349 350
/*
 * print_hf_line
351
 * @pj:     printing context
352 353 354 355 356 357
 * @hf:     header/footer descriptor
 * @y:      vertical position
 * @left:   left coordinate of clip rectangle
 * @bottom: bottom coordinate of clip rectangle
 * @right:  right coordinate of clip rectangel
 * @top:    top coordinate of clip rectangle
358
 *
359 360 361 362 363
 * Print a header/footer line.
 *
 * Position at y, and clip to rectangle. If print_debugging is > 0, display
 * the rectangle.
 */
364
static void
365 366
print_hf_line (PrintJobInfo const *pj, PrintHF const *hf,
	       double y, double left, double bottom, double right, double top)
367 368
{
	gnome_print_setfont (pj->print_context, pj->decoration_font);
369
	gnome_print_setrgbcolor (pj->print_context, 0, 0, 0);
370

371 372 373
	/* Check if there's room to print. top and bottom are on the clip
	 * path, so we are actually requiring room for a 6x4 pt
	 * character. */
374
	if (ABS (top - bottom) < 8)
375 376 377
		return;
	if (ABS (left - right) < 6)
		return;
378

379 380 381 382 383 384 385
	gnome_print_gsave (pj->print_context);

	print_make_rectangle_path (pj->print_context,
				   left, bottom, right, top);

#ifndef NO_DEBUG_PRINT
	if (print_debugging > 0) {
386 387
		static double dash[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
		static gint n_dash = 6;
388

389
		gnome_print_gsave (pj->print_context);
390
		gnome_print_setdash (pj->print_context, n_dash, dash, 0.0);
391 392 393 394 395 396 397 398 399 400 401
		gnome_print_stroke  (pj->print_context);
		gnome_print_grestore (pj->print_context);
	}
#endif
	/* Clip the header or footer */
	gnome_print_clip      (pj->print_context);

	print_hf_element (pj, hf->left_format,   LEFT_HEADER, y);
	print_hf_element (pj, hf->middle_format, MIDDLE_HEADER, y);
	print_hf_element (pj, hf->right_format,  RIGHT_HEADER, y);
	gnome_print_grestore (pj->print_context);
402 403
}

404 405
/*
 * print_headers
406
 * @pj: printing context
407 408 409 410 411
 * Print headers
 *
 * Align ascenders flush with inside of top margin.
 */
static void
412
print_headers (PrintJobInfo const *pj)
413
{
414
	PrintMargins const *pm = &pj->pi->margins;
415
	double top, bottom, y, ascender;
416 417
	double header = 0, footer = 0, left = 0, right = 0;

Jody Goldberg's avatar
Jody Goldberg committed
418
	print_info_get_margins   (pj->pi, &header, &footer, &left, &right);
419 420

	ascender = gnome_font_get_ascender (pj->decoration_font);
421 422 423
	y = pj->height - header - ascender;
	top    =  1 + pj->height - MIN (header, pm->top.points);
	bottom = -1 + pj->height - MAX (header, pm->top.points);
424 425

	print_hf_line (pj, pj->pi->header, y,
426 427
		       -1 + left, bottom,
		       pj->width - right, top);
428 429 430 431
}

/*
 * print_footers
432
 * @pj: printing context
433 434 435 436
 * Print footers
 *
 * Align descenders flush with inside of bottom margin.
 */
437
static void
438
print_footers (PrintJobInfo const *pj)
439 440
{
	PrintMargins *pm = &pj->pi->margins;
441
	double top, bottom, y;
442 443
	double header = 0, footer = 0, left = 0, right = 0;

Jody Goldberg's avatar
Jody Goldberg committed
444
	print_info_get_margins   (pj->pi, &header, &footer, &left, &right);
445

446
	y = footer
Jody Goldberg's avatar
Jody Goldberg committed
447
		- gnome_font_get_descender (pj->decoration_font);
448 449
	top    =  1 + MAX (footer, pm->bottom.points);
	bottom = -1 + MIN (footer, pm->bottom.points);
450

451
	/* Clip path for the header
452 453 454
	 * NOTE : postscript clip paths exclude the border, gdk includes it.
	 */
	print_hf_line (pj, pj->pi->footer, y,
455 456
		       -1 + left, bottom,
		       pj->width - right, top);
457 458
}

459 460 461 462
static void
setup_scale (PrintJobInfo const *pj)
{
	double affine [6];
463 464
	double x_scale = pj->pi->scaling.percentage.x / 100.;
	double y_scale = pj->pi->scaling.percentage.y / 100.;
465

466
	art_affine_scale (affine, x_scale, y_scale);
467 468 469 470
	gnome_print_concat (pj->print_context, affine);

}

Jody Goldberg's avatar
Jody Goldberg committed
471
static GnmValue *
472
cb_range_empty (Sheet *sheet, int col, int row, GnmCell *cell, gpointer flags)
473 474 475 476 477 478 479
{
	ColRowInfo const *cri = sheet_col_get_info (sheet, col);
	if (!cri->visible)
		return NULL;
	cri = sheet_row_get_info (sheet, row);
	if (!cri->visible)
		return NULL;
480
	return VALUE_TERMINATE;
481 482
}

Miguel de Icaza's avatar
Miguel de Icaza committed
483 484 485
/**
 * print_page:
 * @sheet:     the sheet to print
486
 * @range:     a range
Miguel de Icaza's avatar
Miguel de Icaza committed
487 488 489
 * @pj:        printing context
 *
 * Return value: always 0
490 491 492 493 494
 *
 * Excel prints repeated rows like this: Pages up to and including the page
 * where the first of the repeated rows "naturally" occurs are printed in
 * the normal way. On subsequent pages, repated rows are printed before the
 * regular flow.
Miguel de Icaza's avatar
Miguel de Icaza committed
495 496
 */
static int
Jody Goldberg's avatar
Jody Goldberg committed
497
print_page (PrintJobInfo const *pj, Sheet const *sheet, GnmRange *range,
498
	    gboolean output)
499 500
{
	PrintMargins *margins = &pj->pi->margins;
501 502
	/* print_height/width are sizes of the regular grid,
	 * not including repeating rows and columns */
503 504 505
	double print_height, print_width;
	double repeat_cols_used_x = 0., repeat_rows_used_y = 0.;
	double x, y, clip_y;
506
	char *pagenotxt;
507
	gboolean printed;
508
	GList *l;
509
	double header = 0, footer = 0, left = 0, right = 0;
Miguel de Icaza's avatar
Miguel de Icaza committed
510

511 512 513 514
	/* FIXME: Can col / row space calculation be factored out? */

	/* Space for repeated rows depends on whether we print them or not */
	if (pj->pi->repeat_top.use &&
515
	    range->start.row > pj->pi->repeat_top.range.start.row) {
516 517
		repeat_rows_used_y = pj->repeat_rows_used_y;
		/* Make sure start_row never is inside the repeated range */
518 519
		range->start.row = MAX (range->start.row,
				        pj->pi->repeat_top.range.end.row + 1);
520 521
	} else
		repeat_rows_used_y = 0;
522

523 524
	/* Space for repeated cols depends on whether we print them or not */
	if (pj->pi->repeat_left.use &&
525
	    range->start.col > pj->pi->repeat_left.range.start.col) {
526 527
		repeat_cols_used_x = pj->repeat_cols_used_x;
		/* Make sure start_col never is inside the repeated range */
528 529
		range->start.col = MAX (range->start.col,
				        pj->pi->repeat_left.range.end.col + 1);
530 531 532
	} else
		repeat_cols_used_x = 0;

533
	/* If there are no cells in the area check for spans */
534 535
	printed = (NULL != sheet_foreach_cell_in_range ((Sheet *)sheet,
							CELL_ITER_IGNORE_NONEXISTENT,
536 537 538 539
							range->start.col,
							range->start.row,
							range->end.col,
							range->end.row,
540 541
							cb_range_empty, NULL));
	if (!printed) {
542 543
		int i = range->start.row;
		for (; i <= range->end.row ; ++i) {
Jody Goldberg's avatar
Jody Goldberg committed
544
			ColRowInfo const *ri = sheet_row_get_info (sheet, i);
545
			if (ri->visible &&
546 547
			    (NULL != row_span_get (ri, range->start.col) ||
			     NULL != row_span_get (ri, range->end.col))) {
548 549 550 551 552
				printed = TRUE;
				break;
			}
		}
	}
553 554
	if (!printed && pj->pi->print_even_if_only_styles)
		printed = sheet_style_has_visible_content (sheet, range);
555

556
	/* Check for sheet objects if nothing has been found so far */
557
	if (!printed)
558 559
		for (l = sheet->sheet_objects; l && !printed; l = l->next) {
			SheetObject *so = SHEET_OBJECT (l->data);
560
			printed = range_overlap (range, &so->anchor.cell_bound);
561 562
		}

563 564
	if (!output)
		return printed;
565

566 567
	if (!printed)
		return 0;
568

569 570
	x = 0.;
	y = 0.;
571

572 573
	print_height = sheet_row_get_distance_pts (sheet, range->start.row,
						   range->end.row + 1);
574
	if (pj->pi->center_vertically){
575
		double h = print_height;
576

577
		if (pj->pi->print_titles)
578 579
			h += sheet->rows.default_style.size_pts;
		h += repeat_rows_used_y;
580
		h *= pj->pi->scaling.percentage.y / 100.;
581
		y = (pj->y_points - h)/2;
582 583
	}

584 585
	print_width = sheet_col_get_distance_pts (sheet, range->start.col,
						  range->end.col + 1);
586
	if (pj->pi->center_horizontally){
587 588
		double w = print_width;

589
		if (pj->pi->print_titles)
590 591
			w += sheet->cols.default_style.size_pts;
		w += repeat_cols_used_x;
592
		w *= pj->pi->scaling.percentage.x / 100.;
593
		x = (pj->x_points - w)/2;
594 595
	}

596
	print_info_get_margins   (pj->pi, &header, &footer, &left, &right);
597
	/* Margins */
598 599
	x += left;
	y += MAX (margins->top.points, header);
600 601
	if (pj->pi->print_grid_lines) {
		/* the initial grid lines */
602 603
		x += 1.;
		y += 1.;
604 605
	} else {
		/* If there are no grids ignore the leading cell margins */
606 607
		x -= sheet->cols.default_style.margin_a;
		y -= sheet->rows.default_style.margin_a;
608
	}
609

610 611 612 613 614 615 616 617
	/* Note: we cannot have spaces in page numbers.  */
	pagenotxt = hf_format_render (_("&[PAGE]"),
				      pj->render_info, HF_RENDER_PRINT);
	if (!pagenotxt)
		pagenotxt = g_strdup_printf ("%d", pj->render_info->page);
	gnome_print_beginpage (pj->print_context, pagenotxt);
	g_free (pagenotxt);

618 619 620 621
	if (pj->decoration_font) {
		print_headers (pj);
		print_footers (pj);
	}
622 623 624 625 626

	/*
	 * Print any titles that might be used
	 */
	if (pj->pi->print_titles) {
627
		print_titles (pj, sheet, range, x, y);
628 629 630
		x += sheet->cols.default_style.size_pts;
		y += sheet->rows.default_style.size_pts;
	}
631

632 633 634 635 636 637 638 639 640
	/* Clip the page */
	/* Gnome-print coordinates are lower left based,
	 * like Postscript */
	clip_y = 1 + pj->height - y;
	print_make_rectangle_path (
		pj->print_context,
		x - 1, clip_y,
		x + pj->x_points + 1,
		clip_y - pj->y_points - 2);
641
#ifndef NO_DEBUG_PRINT
642 643 644 645 646
	if (print_debugging > 0) {
		gnome_print_gsave (pj->print_context);
		gnome_print_stroke  (pj->print_context);
		gnome_print_grestore (pj->print_context);
	}
647
#endif
648
	gnome_print_clip (pj->print_context);
649

650 651
	/* Start a new path because the background fill function does not */
	gnome_print_newpath (pj->print_context);
652

653
	setup_scale (pj);
654 655
	x /= pj->pi->scaling.percentage.x / 100.;
	y /= pj->pi->scaling.percentage.y / 100.;
656 657 658 659 660 661 662

	if (pj->pi->repeat_top.use && repeat_rows_used_y > 0.) {
		/* Intersection of repeated rows and columns */
		if (pj->pi->repeat_left.use && repeat_cols_used_x > 0.)
			print_page_repeated_intersect (pj, sheet,
				x, y, repeat_cols_used_x, repeat_rows_used_y);

663 664 665
		print_page_repeated_rows (pj, sheet, range->start.col,
					  range->end.col,
					  x + repeat_cols_used_x, y);
666 667 668 669
		y += repeat_rows_used_y;
	}

	if (pj->pi->repeat_left.use && repeat_cols_used_x > 0. ) {
670 671
		print_page_repeated_cols (pj, sheet, range->start.row,
					  range->end.row, x, y);
672 673
		x += repeat_cols_used_x;
	}
674

675
	print_page_cells (pj, sheet, range, x, y);
676 677

	gnome_print_showpage (pj->print_context);
Miguel de Icaza's avatar
Miguel de Icaza committed
678 679

	return 1;
680 681 682
}

/*
683 684
 * Computes number of rows or columns that fit in @usable starting
 * at index @start and limited to index @end
685
 */
686
static int
687
compute_group (PrintJobInfo const *pj, Sheet const *sheet,
688
	       int start, int end, double usable,
689
	       ColRowInfo const *(get_info)(Sheet const *sheet, int const p))
690
{
691
	double size_pts = 1.; /* The initial grid line */
692
	int idx, count = 0;
693

694 695
	for (idx = start; idx <= end; idx++, count++) {
		ColRowInfo const *info = (*get_info) (sheet, idx);
696 697
		if (info->visible) {
			size_pts += info->size_pts;
698 699
			if (size_pts > usable)
				break;
700
		}
701 702
	}

703 704 705 706 707
	/* FIXME : Find a way to inform the user that one of the rows/cols does
	 * not fit on a page
	 */
	g_return_val_if_fail (count > 0, 1);

708
	return count;
709 710
}

711 712 713 714 715 716 717 718 719 720
/* computer_scale_fit_to
 * Computes the scaling needed to fit all the rows or columns into the @usable
 * area.
 * This function is called when printing, and the user has selected the 'fit-to'
 * printing option. It will adjust the internal x and y scaling values to
 * make the sheet fit the desired number of pages, as suggested by the user.
 * It will only reduce the scaling to fit inside a page, not enlarge.
 */
static double
compute_scale_fit_to (PrintJobInfo const *pj, Sheet const *sheet,
721 722 723
		      int start, int end, double usable,
		      ColRowInfo const *(get_info)(Sheet const *sheet, int const p),
		      gint pages)
724
{
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
	double size_pts; /* The initial grid line on each page*/
	int idx;
	double scale;
	double max_unit = 0;

	/* This means to take whatever space is needed.  */
	if (pages <= 0)
		return 100;

	size_pts =  1. * pages;

	/* Work how much space the sheet requires. */
	for (idx = start; idx <= end; idx++) {
		ColRowInfo const *info = (*get_info) (sheet, idx);
		if (info->visible) {
			size_pts += info->size_pts;
			if (info->size_pts > max_unit)
				max_unit = (info->size_pts > usable) ?
					usable : info->size_pts;
		}
	}

	usable *= pages; /* Our usable area is this big. */

	/* What scale is required to fit the sheet onto this usable area? */
	/* Note that on each page but the last we may loose space that can */
	/* be nearly as large as the largest unit. */
	scale = usable / (size_pts + (pages - 1) * max_unit) * 100.;

	/* If the sheet needs to be shrunk, we update the scale.
	 * If it already fits, we simply leave the scale at 100.
	 * Another feature might be to enlarge a sheet so that it fills
	 * the page. But this is not a requested feature yet.
	 */
	return (scale < 100.) ? scale : 100.;
760 761
}

762 763 764 765
#define COL_FIT(col) (col >= SHEET_MAX_COLS ? (SHEET_MAX_COLS-1) : col)
#define ROW_FIT(row) (row >= SHEET_MAX_ROWS ? (SHEET_MAX_ROWS-1) : row)

static int
766
print_range_down_then_right (PrintJobInfo const *pj, Sheet const *sheet,
Jody Goldberg's avatar
Jody Goldberg committed
767
			     GnmRange const *r, gboolean output)
768
{
769 770
	double usable_x, usable_x_initial, usable_x_repeating;
	double usable_y, usable_y_initial, usable_y_repeating;
771
	int pages = 0;
772
	int col = r->start.col;
773 774 775 776 777 778 779
	gboolean printed;

	usable_x_initial   = pj->x_points - pj->titles_used_x;
	usable_x_repeating = usable_x_initial - pj->repeat_cols_used_x;
	usable_y_initial   = pj->y_points - pj->titles_used_y;
	usable_y_repeating = usable_y_initial - pj->repeat_rows_used_y;

Morten Welinder's avatar
Morten Welinder committed
780
       /*
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
        * Has the user selected the 'SIZE_FIT' scaling type?
	* If so adjust the scaling percentages to the correct values.
        */
       if (pj->pi->scaling.type == SIZE_FIT) {
               int col = r->start.col;
               int row = r->start.row;

              /* Temporarily calculate usable_x. */
               if (col < pj->pi->repeat_left.range.end.col) {
                       usable_x = usable_x_initial;
                       col = MIN (col,
                                  pj->pi->repeat_left.range.end.col);
               } else
                       usable_x = usable_x_repeating;

               /* Re-adjust x-scaling if necessary. */
               pj->pi->scaling.percentage.x = compute_scale_fit_to (pj,
                                               sheet,
                                               col,
                                               r->end.col,
                                               usable_x,
                                               sheet_col_get_info,
                                               pj->pi->scaling.dim.cols);

               /* Temporarily calculate usable_y. */
               if (row < pj->pi->repeat_top.range.end.row) {
                       usable_y = usable_y_initial;
                       row = MIN (row,
                                  pj->pi->repeat_top.range.end.row);
               } else
                       usable_y = usable_y_repeating;

               /* Re-adjust y-scaling if necessary. */
               pj->pi->scaling.percentage.y = compute_scale_fit_to (pj,
                                               sheet,
                                               row,
                                               r->end.row,
                                               usable_y,
                                               sheet_row_get_info,
                                               pj->pi->scaling.dim.rows);
Andreas J. Guelzow's avatar
ditto  
Andreas J. Guelzow committed
821 822 823 824 825 826
	       /* Making sure that the x and y scales are identical */
	       if (pj->pi->scaling.percentage.y > pj->pi->scaling.percentage.x)
		       pj->pi->scaling.percentage.y = pj->pi->scaling.percentage.x;
	       else
		       pj->pi->scaling.percentage.x = pj->pi->scaling.percentage.y;
      }
827

828
	while (col <= r->end.col) {
829
		int col_count;
830
		int row = r->start.row;
831

832
		if (col <= pj->pi->repeat_left.range.end.col) {
833 834
			usable_x = usable_x_initial;
			col = MIN (col,
835
				   pj->pi->repeat_left.range.end.col);
836 837
		} else
			usable_x = usable_x_repeating;
838

839
		usable_x /= pj->pi->scaling.percentage.x / 100.;
840
		col_count = compute_group (pj, sheet, col, r->end.col,
841
					   usable_x, sheet_col_get_info);
842

843
		while (row <= r->end.row) {
Jody Goldberg's avatar
Jody Goldberg committed
844
			GnmRange range;
845
			int row_count;
846

847
			if (row <= pj->pi->repeat_top.range.end.row) {
848 849
				usable_y = usable_y_initial;
				row = MIN (row,
850
					   pj->pi->repeat_top.range.end.row);
851 852
			} else
				usable_y = usable_y_repeating;
853

854
			usable_y /= pj->pi->scaling.percentage.y / 100.;
855 856
			row_count = compute_group (pj, sheet, row, r->end.row,
						   usable_y, sheet_row_get_info);
857 858 859 860
			range_init (&range, COL_FIT (col), ROW_FIT (row),
				    COL_FIT (col + col_count - 1),
				    ROW_FIT (row + row_count - 1));
			printed = print_page (pj, sheet, &range, output);
861

862
			row += row_count;
Miguel de Icaza's avatar
Miguel de Icaza committed
863

864 865 866 867 868 869
			if (printed) {
				pages++;
				/* Only update page count when actually printing */
				if (output)
					pj->render_info->page++;
			}
870
		}
871 872
		col += col_count;
	}
873

874 875
	return pages;
}
876

877
static int
878
print_range_right_then_down (PrintJobInfo const *pj, Sheet const *sheet,
Jody Goldberg's avatar
Jody Goldberg committed
879
			     GnmRange const *r, gboolean output)
880
{
881 882
	double usable_x, usable_x_initial, usable_x_repeating;
	double usable_y, usable_y_initial, usable_y_repeating;
883
	int pages = 0;
884
	int row = r->start.row;
885
	gboolean printed;
886

887 888 889 890
	usable_x_initial   = pj->x_points - pj->titles_used_x;
	usable_x_repeating = usable_x_initial - pj->repeat_cols_used_x;
	usable_y_initial   = pj->y_points - pj->titles_used_y;
	usable_y_repeating = usable_y_initial - pj->repeat_rows_used_y;
Miguel de Icaza's avatar
Miguel de Icaza committed
891

892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
       /* Calculate any scaling needed to fit to a requested number
        * of pages.
        */
       if (pj->pi->scaling.type == SIZE_FIT) {
               int col = r->start.col;
               int row = r->start.row;

               /* Temporarily calculate usable_x. */
               if (col < pj->pi->repeat_left.range.end.col) {
                       usable_x = usable_x_initial;
                       col = MIN (col,
                                  pj->pi->repeat_left.range.end.col);
               } else
                       usable_x = usable_x_repeating;

               /* Re-adjust x-scaling if necessary. */
               pj->pi->scaling.percentage.x = compute_scale_fit_to (pj,
                                               sheet,
                                               col,
                                               r->end.col,
                                               usable_x,
                                               sheet_col_get_info,
                                               pj->pi->scaling.dim.cols);

               /* Temporarily calculate usable_y. */
               if (row < pj->pi->repeat_top.range.end.row) {
                       usable_y = usable_y_initial;
                       row = MIN (row,
                                  pj->pi->repeat_top.range.end.row);
               } else
                       usable_y = usable_y_repeating;

               /* Re-adjust y-scaling if necessary. */
               pj->pi->scaling.percentage.y = compute_scale_fit_to (pj,
                                               sheet,
                                               row,
                                               r->end.row,
                                               usable_y,
                                               sheet_row_get_info,
                                               pj->pi->scaling.dim.rows);
Andreas J. Guelzow's avatar
ditto  
Andreas J. Guelzow committed
932 933 934 935 936
	       /* Making sure that the x and y scales are identical */
	       if (pj->pi->scaling.percentage.y > pj->pi->scaling.percentage.x)
		       pj->pi->scaling.percentage.y = pj->pi->scaling.percentage.x;
	       else
		       pj->pi->scaling.percentage.x = pj->pi->scaling.percentage.y;
937 938 939
       }


940
	while (row <= r->end.row) {
941
		int row_count;
942
		int col = r->start.col;
943

Jody Goldberg's avatar
Jody Goldberg committed
944
		if (row <= pj->pi->repeat_top.range.end.row) {
945 946
			usable_y = usable_y_initial;
			row = MIN (row,
947
				   pj->pi->repeat_top.range.end.row);
948 949
		} else
			usable_y = usable_y_repeating;
950

951
		usable_y /= pj->pi->scaling.percentage.y / 100.;
952
		row_count = compute_group (pj, sheet, row, r->end.row,
953
					   usable_y, sheet_row_get_info);
954

955
		while (col <= r->end.col) {
Jody Goldberg's avatar
Jody Goldberg committed
956
			GnmRange range;
957
			int col_count;
958

Jody Goldberg's avatar
Jody Goldberg committed
959
			if (col <= pj->pi->repeat_left.range.end.col) {
960 961
				usable_x = usable_x_initial;
				col = MIN (col,
962
					   pj->pi->repeat_left.range.end.col);
963 964 965
			} else
				usable_x = usable_x_repeating;

966
			usable_x /= pj->pi->scaling.percentage.x / 100.;
967 968
			col_count = compute_group (pj, sheet, col, r->end.col,
						   usable_x, sheet_col_get_info);
969

970 971 972 973
			range_init (&range, COL_FIT (col), ROW_FIT (row),
				    COL_FIT (col + col_count - 1),
				    ROW_FIT (row + row_count - 1));
			printed = print_page (pj, sheet, &range, output);
Miguel de Icaza's avatar
Miguel de Icaza committed
974

975 976
			col += col_count;

977 978 979 980 981 982
			if (printed) {
				pages++;
				/* Only update page count when actually printing */
				if (output)
					pj->render_info->page++;
			}
983
		}
984
		row += row_count;
985 986
	}

987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
	return pages;
}

/*
 * @print_sheet_range:
 * @sheet: the sheet being printed
 * @r: the requested range of cells to be printed.
 * @pj: print context
 * @output: %TRUE if we actually want to print, %FALSE if we want to just
 *          use print_sheet_range() to probe whether the range contains data
 *
 * This routine is used for both printing as well as computing the number
 * of pages with actual content to print
 *
 * Return value: the number of pages printed.
 */
static int
1004
print_sheet_range (PrintJobInfo *pj, Sheet const *sheet,
Jody Goldberg's avatar
Jody Goldberg committed
1005
		   GnmRange const *r, gboolean output)
1006 1007 1008 1009
{
	int pages;

 	if (pj->pi->print_order == PRINT_ORDER_DOWN_THEN_RIGHT)
1010
		pages = print_range_down_then_right (pj, sheet, r, output);
1011
	else
1012
		pages = print_range_right_then_down (pj, sheet, r, output);
1013

Miguel de Icaza's avatar
Miguel de Icaza committed
1014
	return pages;
1015 1016
}

Miguel de Icaza's avatar
Miguel de Icaza committed
1017
static double
1018 1019
print_range_used_units (Sheet const *sheet, gboolean compute_rows,
			PrintRepeatRange const *range)
Miguel de Icaza's avatar
Miguel de Icaza committed
1020
{
Jody Goldberg's avatar
Jody Goldberg committed
1021
	GnmRange const *r = &range->range;
Miguel de Icaza's avatar
Miguel de Icaza committed
1022
	if (compute_rows)
1023
		return sheet_row_get_distance_pts
1024
			(sheet, r->start.row, r->end.row+1);
Miguel de Icaza's avatar
Miguel de Icaza committed
1025
	else
1026
		return sheet_col_get_distance_pts
1027
			(sheet, r->start.col, r->end.col+1);
Miguel de Icaza's avatar
Miguel de Icaza committed
1028 1029
}

1030
static void
1031
print_job_info_init_sheet (PrintJobInfo *pj, Sheet const *sheet)
1032 1033 1034 1035 1036
{
	/*
	 * This should be set in print_job_info_get, but we need
	 * to get access to one of the sheets
	 */
1037
	if (pj->pi->print_titles) {
1038 1039
		pj->titles_used_x = sheet->cols.default_style.size_pts;
		pj->titles_used_y = sheet->rows.default_style.size_pts;
1040 1041 1042 1043 1044
	} else {
		pj->titles_used_x = 0;
		pj->titles_used_y = 0;
	}

1045 1046 1047 1048 1049 1050
	pj->repeat_rows_used_y = (pj->pi->repeat_top.use)
	    ? print_range_used_units (sheet, TRUE, &pj->pi->repeat_top)
	    : 0.;
	pj->repeat_cols_used_x = (pj->pi->repeat_left.use)
	    ? print_range_used_units (sheet, FALSE, &pj->pi->repeat_left)
	    : 0.;
Miguel de Icaza's avatar
Miguel de Icaza committed
1051

1052
	pj->render_info->sheet = sheet;
1053 1054
}

Miguel de Icaza's avatar
Miguel de Icaza committed
1055 1056 1057 1058 1059
/*
 * code to count the number of pages that will be printed.
 * Unfortuantely a lot of data here is caclualted again when you
 * actually print the page ...
 */
1060 1061 1062 1063

typedef struct _PageCountInfo {
	int pages;
	PrintJobInfo *pj;
Jody Goldberg's avatar
Jody Goldberg committed
1064
	GnmRange r;
1065 1066 1067 1068
	int current_output_sheet;
} PageCountInfo;

static void
1069
compute_sheet_pages (PageCountInfo *pc, Sheet const *sheet)
1070 1071
{
	PrintJobInfo *pj = pc->pj;
Jody Goldberg's avatar
Jody Goldberg committed
1072
	GnmRange r;
1073 1074 1075 1076