/*
 * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
 * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * NetSurf is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "utils/config.h"

#include <assert.h>
#include <string.h>
#include <swis.h>
#include <oslib/font.h>
#include <oslib/hourglass.h>
#include <oslib/osfile.h>
#include <oslib/osfind.h>
#include <oslib/pdriver.h>
#include <oslib/wimp.h>
#include <rufl.h>

#include "utils/config.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
#include "utils/nsoption.h"
#include "content/content.h"
#include "content/hlcache.h"
#include "desktop/browser.h"
#include "desktop/plotters.h"

#include "riscos/gui.h"
#include "riscos/dialog.h"
#include "riscos/menus.h"
#include "riscos/print.h"
#include "riscos/wimp.h"
#include "riscos/wimp_event.h"
#include "riscos/filetype.h"
#include "riscos/font.h"


#define ICON_PRINT_TO_BOTTOM 1
#define ICON_PRINT_SHEETS 2
#define ICON_PRINT_SHEETS_VALUE 3
#define ICON_PRINT_SHEETS_DOWN 4
#define ICON_PRINT_SHEETS_UP 5
#define ICON_PRINT_SHEETS_TEXT 6
#define ICON_PRINT_FG_IMAGES 7
#define ICON_PRINT_BG_IMAGES 8
#define ICON_PRINT_IN_BACKGROUND 9
#define ICON_PRINT_UPRIGHT 10
#define ICON_PRINT_SIDEWAYS 11
#define ICON_PRINT_COPIES 12
#define ICON_PRINT_COPIES_DOWN 13
#define ICON_PRINT_COPIES_UP 14
#define ICON_PRINT_CANCEL 15
#define ICON_PRINT_PRINT 16
#define ICON_PRINT_TEXT_BLACK 20


/** \todo landscape format pages
 *  \todo be somewhat more intelligent and try not to crop pages
 *        half way up a line of text
 *  \todo make use of print stylesheets
 */

struct gui_window *ro_print_current_window = NULL;
bool print_text_black = false;
bool print_active = false;

/* 1 millipoint == 1/400 OS unit == 1/800 browser units */

static int print_prev_message = 0;
static bool print_in_background = false;
static float print_scale = 1.0;
static int print_num_copies = 1;
static bool print_bg_images = false;
static int print_max_sheets = -1;
static bool print_sideways = false;
/** List of fonts in current print. */
static char **print_fonts_list = 0;
/** Number of entries in print_fonts_list. */
static unsigned int print_fonts_count;
/** Error in print_fonts_plot_text() or print_fonts_callback(). */
static const char *print_fonts_error;

void gui_window_redraw_window(struct gui_window *g);

static bool ro_gui_print_click(wimp_pointer *pointer);
static bool ro_gui_print_apply(wimp_w w);
static void print_update_sheets_shaded_state(bool on);
static void print_send_printsave(hlcache_handle *h);
static bool print_send_printtypeknown(wimp_message *m);
static bool print_document(struct gui_window *g, const char *filename);
static const char *print_declare_fonts(hlcache_handle *h);
static bool print_fonts_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style);
static bool print_fonts_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style);
static bool print_fonts_plot_polygon(const int *p, unsigned int n, const plot_style_t *style);
static bool print_fonts_plot_clip(const struct rect *clip);
static bool print_fonts_plot_text(int x, int y, const char *text, size_t length,
		const plot_font_style_t *fstyle);
static bool print_fonts_plot_disc(int x, int y, int radius, const plot_style_t *style);
static bool print_fonts_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style);
static bool print_fonts_plot_bitmap(int x, int y, int width, int height,
		struct bitmap *bitmap, colour bg,
		bitmap_flags_t flags);
static bool print_fonts_plot_path(const float *p, unsigned int n, colour fill, float width,
		colour c, const float transform[6]);
static void print_fonts_callback(void *context,
		const char *font_name, unsigned int font_size,
		const char *s8, unsigned short *s16, unsigned int n,
		int x, int y);


/** Plotter for print_declare_fonts(). All the functions do nothing except for
 * print_fonts_plot_text, which records the fonts used. */
static const struct plotter_table print_fonts_plotters = {
	.rectangle = print_fonts_plot_rectangle,
	.line = print_fonts_plot_line,
	.polygon = print_fonts_plot_polygon,
	.clip = print_fonts_plot_clip,
	.text = print_fonts_plot_text,
	.disc = print_fonts_plot_disc,
	.arc = print_fonts_plot_arc,
	.bitmap = print_fonts_plot_bitmap,
	.path = print_fonts_plot_path,
	.option_knockout = false,
};


/**
 * Initialise the print dialog.
 */

void ro_gui_print_init(void)
{
  	wimp_i radio_print_type[] = {ICON_PRINT_TO_BOTTOM, ICON_PRINT_SHEETS,
  			-1};
  	wimp_i radio_print_orientation[] = {ICON_PRINT_UPRIGHT,
  			ICON_PRINT_SIDEWAYS, -1};

	dialog_print = ro_gui_dialog_create("print");
	ro_gui_wimp_event_register_radio(dialog_print, radio_print_type);
	ro_gui_wimp_event_register_radio(dialog_print, radio_print_orientation);
	ro_gui_wimp_event_register_checkbox(dialog_print, ICON_PRINT_FG_IMAGES);
	ro_gui_wimp_event_register_checkbox(dialog_print, ICON_PRINT_BG_IMAGES);
	ro_gui_wimp_event_register_checkbox(dialog_print,
			ICON_PRINT_IN_BACKGROUND);
	ro_gui_wimp_event_register_checkbox(dialog_print,
			ICON_PRINT_TEXT_BLACK);
	ro_gui_wimp_event_register_text_field(dialog_print,
			ICON_PRINT_SHEETS_TEXT);
	ro_gui_wimp_event_register_numeric_field(dialog_print,
			ICON_PRINT_COPIES, ICON_PRINT_COPIES_UP,
			ICON_PRINT_COPIES_DOWN, 1, 99, 1, 0);
	ro_gui_wimp_event_register_numeric_field(dialog_print,
			ICON_PRINT_SHEETS_VALUE, ICON_PRINT_SHEETS_UP,
			ICON_PRINT_SHEETS_DOWN, 1, 99, 1, 0);
	ro_gui_wimp_event_register_cancel(dialog_print, ICON_PRINT_CANCEL);
	ro_gui_wimp_event_register_mouse_click(dialog_print,
			ro_gui_print_click);
	ro_gui_wimp_event_register_ok(dialog_print, ICON_PRINT_PRINT,
			ro_gui_print_apply);
	ro_gui_wimp_event_set_help_prefix(dialog_print, "HelpPrint");
}


/**
 * Prepares all aspects of the print dialog prior to opening.
 *
 * \param g parent window
 */

void ro_gui_print_prepare(struct gui_window *g)
{
	char *desc;
	bool printers_exists = true;
	os_error *error;

	assert(g);

	ro_print_current_window = g;
	print_prev_message = 0;

	/* Read Printer Driver name */
	error = xpdriver_info(0, 0, 0, 0, &desc, 0, 0, 0);
	if (error) {
		LOG("xpdriver_info: 0x%x: %s", error->errnum, error->errmess);
		printers_exists = false;
	}

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_TO_BOTTOM,
			true);

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_SHEETS, false);
	ro_gui_set_icon_integer(dialog_print, ICON_PRINT_SHEETS_VALUE, 1);
	print_update_sheets_shaded_state(true);

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_FG_IMAGES,
			true);
	ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_FG_IMAGES, true);

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_BG_IMAGES,
			print_bg_images);

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_IN_BACKGROUND,
			false);

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_UPRIGHT, true);
	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_SIDEWAYS,
			false);

	ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_TEXT_BLACK,
			false);

	ro_gui_set_icon_integer(dialog_print, ICON_PRINT_COPIES, 1);

	ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_PRINT,
			!printers_exists);
	if (printers_exists)
		ro_gui_set_window_title(dialog_print, desc);

	ro_gui_wimp_event_memorise(dialog_print);
}


/**
 * Handle mouse clicks in print dialog
 *
 * \param pointer wimp_pointer block
 */

bool ro_gui_print_click(wimp_pointer *pointer)
{
	if (pointer->buttons == wimp_CLICK_MENU)
		return true;

	switch (pointer->i) {
		case ICON_PRINT_TO_BOTTOM:
		case ICON_PRINT_SHEETS:
			print_update_sheets_shaded_state(pointer->i !=
					ICON_PRINT_SHEETS);
			break;
	}
	return false;
}


/**
 * Handle click on the Print button in the print dialog.
 */

bool ro_gui_print_apply(wimp_w w)
{
	int copies = atoi(ro_gui_get_icon_string(dialog_print,
						ICON_PRINT_COPIES));
	int sheets = atoi(ro_gui_get_icon_string(dialog_print,
						ICON_PRINT_SHEETS_VALUE));

	print_in_background = ro_gui_get_icon_selected_state(dialog_print,
			ICON_PRINT_IN_BACKGROUND);
	print_text_black = ro_gui_get_icon_selected_state(dialog_print,
			ICON_PRINT_TEXT_BLACK);
	print_sideways = ro_gui_get_icon_selected_state(dialog_print,
			ICON_PRINT_SIDEWAYS);
	print_num_copies = copies;
	if (ro_gui_get_icon_selected_state(dialog_print, ICON_PRINT_SHEETS))
		print_max_sheets = sheets;
	else
		print_max_sheets = -1;
	print_bg_images = ro_gui_get_icon_selected_state(dialog_print,
			ICON_PRINT_BG_IMAGES);

	print_send_printsave(browser_window_get_content(
			ro_print_current_window->bw));

	return true;
}


/**
 * Set shaded state of sheets
 *
 * \param on whether to turn shading on or off
 */

void print_update_sheets_shaded_state(bool on)
{
	ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_VALUE, on);
	ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_DOWN, on);
	ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_UP, on);
	ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_TEXT, on);
	ro_gui_set_caret_first(dialog_print);
}


/**
 * Send a message_PRINT_SAVE
 *
 * \param h handle to content to print.
 */

void print_send_printsave(hlcache_handle *h)
{
	wimp_full_message_data_xfer m;
	os_error *e;
	int len;

	len = strlen(content_get_title(h)) + 1;
	if (212 < len)
		len = 212;

	m.size = ((44+len+3) & ~3);
	m.your_ref = 0;
	m.action = message_PRINT_SAVE;
	m.w = (wimp_w)0;
	m.i = m.pos.x = m.pos.y = 0;
	m.est_size = 1024; /* arbitrary value - it really doesn't matter */
	m.file_type = ro_content_filetype(h);
	strncpy(m.file_name, content_get_title(h), 211);
	m.file_name[211] = 0;
	e = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
			(wimp_message *)&m, 0);
	if (e) {
		LOG("xwimp_send_message: 0x%x: %s", e->errnum, e->errmess);
		warn_user("WimpError", e->errmess);
		ro_print_cleanup();
	}
	print_prev_message = m.my_ref;
}


/**
 * Send a message_PRINT_TYPE_KNOWN
 *
 * \param m message to reply to
 * \return true on success, false otherwise
 */

bool print_send_printtypeknown(wimp_message *m)
{
	os_error *e;

	m->size = 20;
	m->your_ref = m->my_ref;
	m->action = message_PRINT_TYPE_KNOWN;
	e = xwimp_send_message(wimp_USER_MESSAGE, m, m->sender);
	if (e) {
		LOG("xwimp_send_message: 0x%x: %s", e->errnum, e->errmess);
		warn_user("WimpError", e->errmess);
		return false;
	}

	return true;
}


/**
 * Handle a bounced message_PRINT_SAVE
 *
 * \param m the bounced message
 */

void ro_print_save_bounce(wimp_message *m)
{
	if (m->my_ref == 0 || m->my_ref != print_prev_message)
		return;

	/* try to print anyway (we're graphics printing) */
	if (ro_print_current_window) {
		print_document(ro_print_current_window, "printer:");
	}
	ro_print_cleanup();
}


/**
 * Handle message_PRINT_ERROR
 *
 * \param m the message containing the error
 */

void ro_print_error(wimp_message *m)
{
	pdriver_message_print_error *p = (pdriver_message_print_error*)&m->data;
	if (m->your_ref == 0 || m->your_ref != print_prev_message)
		return;

	if (m->size == 20)
		warn_user("PrintErrorRO2", 0);
	else
		warn_user("PrintError", p->errmess);

	ro_print_cleanup();
}


/**
 * Handle message_PRINT_TYPE_ODD
 *
 * \param m the message to handle
 */

void ro_print_type_odd(wimp_message *m)
{
	if ((m->your_ref == 0 || m->your_ref == print_prev_message) &&
						!print_in_background) {
		/* reply to a previous message (ie printsave) */
		if (ro_print_current_window && print_send_printtypeknown(m)) {
			print_document(ro_print_current_window, "printer:");
		}
		ro_print_cleanup();
	}
	else {
		/* broadcast message */
		/* no need to do anything */
	}

}


/**
 * Handle message_DATASAVE_ACK for the printing protocol.
 *
 * \param  m  the message to handle
 * \return true if message successfully handled, false otherwise
 *
 * We cheat here and, instead of giving Printers what it asked for (a copy of
 * the file so it can poke us later via a broadcast of PrintTypeOdd), we give
 * it a file that it can print itself without having to bother us further. For
 * PostScript printers (type 0) we give it a PostScript file. Otherwise, we give
 * it a PrintOut file.
 *
 * This method has a couple of advantages:
 * - we can reuse this code for background printing (we simply ignore the
 *   PrintTypeOdd reply)
 * - there's no need to ensure all components of a page queued to be printed
 *   still exist when it reaches the top of the queue. (which reduces complexity
 *   a fair bit)
 */

bool ro_print_ack(wimp_message *m)
{
	pdriver_info_type info_type;
	pdriver_type type;
	os_error *error;

	if (m->your_ref == 0 || m->your_ref != print_prev_message ||
			!ro_print_current_window)
		return false;

	/* read printer driver type */
	error = xpdriver_info(&info_type, 0, 0, 0, 0, 0, 0, 0);
	if (error) {
		LOG("xpdriver_info: 0x%x: %s", error->errnum, error->errmess);
		warn_user("PrintError", error->errmess);
		ro_print_cleanup();
		return true;
	}
	type = info_type >> 16;

	/* print to file */
	if (!print_document(ro_print_current_window,
			m->data.data_xfer.file_name)) {
		ro_print_cleanup();
		return true;
	}

	/* send dataload */
	m->your_ref = m->my_ref;
	m->action = message_DATA_LOAD;

	if (type == pdriver_TYPE_PS)
		m->data.data_xfer.file_type = osfile_TYPE_POSTSCRIPT;
	else
		m->data.data_xfer.file_type = osfile_TYPE_PRINTOUT;

	error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED, m, m->sender);
	if (error) {
		LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
		warn_user("WimpError", error->errmess);
		/* and delete temporary file */
		xosfile_delete(m->data.data_xfer.file_name,
				0, 0, 0, 0, 0);
	}
	print_prev_message = m->my_ref;

	ro_print_cleanup();
	return true;
}


/**
 * Handle a bounced dataload message
 *
 * \param m the message to handle
 */

void ro_print_dataload_bounce(wimp_message *m)
{
	if (m->your_ref == 0 || m->your_ref != print_prev_message)
		return;

	xosfile_delete(m->data.data_xfer.file_name, 0, 0, 0, 0, 0);
	ro_print_cleanup();
}


/**
 * Cleanup after printing
 */

void ro_print_cleanup(void)
{
	ro_print_current_window = NULL;
	print_text_black = false;
	print_prev_message = 0;
	print_max_sheets = -1;
	ro_gui_menu_destroy();
	ro_gui_dialog_close(dialog_print);
}


/**
 * Print a document.
 *
 * \param  g         gui_window containing the document to print
 * \param  filename  name of file to print to
 * \return true on success, false on error and error reported
 */

bool print_document(struct gui_window *g, const char *filename)
{
	int left, right, top, bottom, width, height;
	int saved_width, saved_height;
	int yscroll = 0, sheets = print_max_sheets;
	hlcache_handle *h = browser_window_get_content(g->bw);
	const char *error_message;
	pdriver_features features;
	os_fw fhandle, old_job = 0;
	os_error *error;

	/* no point printing a blank page */
	if (!h) {
		warn_user("PrintError", "nothing to print");
		return false;
	}

	/* read printer driver features */
	error = xpdriver_info(0, 0, 0, &features, 0, 0, 0, 0);
	if (error) {
		LOG("xpdriver_info: 0x%x: %s", error->errnum, error->errmess);
		warn_user("PrintError", error->errmess);
		return false;
	}

	/* read page size */
	error = xpdriver_page_size(0, 0, &left, &bottom, &right, &top);
	if (error) {
		LOG("xpdriver_page_size: 0x%x: %s", error->errnum, error->errmess);
		warn_user("PrintError", error->errmess);
		return false;
	}

	if (print_sideways) {
		width = (top - bottom) / 800;
		height = (right - left) / 800;
	} else {
		width = (right - left) / 800;
		height = (top - bottom) / 800;
	}

	/* layout the document to the correct width */
	saved_width = content_get_width(h);
	saved_height = content_get_height(h);
	if (content_get_type(h) == CONTENT_HTML)
		content_reformat(h, false, width, height);

	/* open printer file */
	error = xosfind_openoutw(osfind_NO_PATH | osfind_ERROR_IF_DIR |
			osfind_ERROR_IF_ABSENT, filename, 0, &fhandle);
	if (error) {
		LOG("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess);
		warn_user("PrintError", error->errmess);
		return false;
	}

	/* select print job */
	error = xpdriver_select_jobw(fhandle, "NetSurf", &old_job);
	if (error) {
		LOG("xpdriver_select_jobw: 0x%x: %s", error->errnum, error->errmess);
		warn_user("PrintError", error->errmess);
		xosfind_closew(fhandle);
		return false;
	}

	rufl_invalidate_cache();

	/* declare fonts, if necessary */
	if (features & pdriver_FEATURE_DECLARE_FONT) {
		if ((error_message = print_declare_fonts(h)))
			goto error;
	}

	ro_gui_current_redraw_gui = g;

	/* print is now active */
	print_active = true;

	do {
		struct rect clip;
		os_box b;
		os_hom_trfm t;
		os_coord p;
		osbool more;

		if (print_sideways) {
			b.x0 = bottom / 400 -2;
			b.y0 = left / 400 - 2;
			b.x1 = top / 400 + 2;
			b.y1 = right / 400 + 2;
			t.entries[0][0] = 0;
			t.entries[0][1] = 65536;
			t.entries[1][0] = -65536;
			t.entries[1][1] = 0;
			p.x = right;
			p.y = bottom;
			ro_plot_origin_x = bottom / 400;
			ro_plot_origin_y = right / 400 + yscroll * 2;
		} else {
			b.x0 = left / 400 -2;
			b.y0 = bottom / 400 - 2;
			b.x1 = right / 400 + 2;
			b.y1 = top / 400 + 2;
			t.entries[0][0] = 65536;
			t.entries[0][1] = 0;
			t.entries[1][0] = 0;
			t.entries[1][1] = 65536;
			p.x = left;
			p.y = bottom;
			ro_plot_origin_x = left / 400;
			ro_plot_origin_y = top / 400 + yscroll * 2;
		}

		xhourglass_percentage((int) (yscroll * 100 /
				content_get_height(h)));

		/* give page rectangle */
		error = xpdriver_give_rectangle(0, &b, &t, &p, os_COLOUR_WHITE);
		if (error) {
			LOG("xpdriver_give_rectangle: 0x%x: %s", error->errnum, error->errmess);
			error_message = error->errmess;
			goto error;
		}

		LOG("given rectangle: [(%d, %d), (%d, %d)]", b.x0, b.y0, b.x1, b.y1);

		/* and redraw the document */
		error = xpdriver_draw_page(print_num_copies, &b, 0, 0,
				&more, 0);
		if (error) {
			LOG("xpdriver_draw_page: 0x%x: %s", error->errnum, error->errmess);
			error_message = error->errmess;
			goto error;
		}

		while (more) {
			struct content_redraw_data data;
			/* TODO: turn knockout off for print */
			struct redraw_context ctx = {
				.interactive = false,
				.background_images = print_bg_images,
				.plot = &ro_plotters
			};

			LOG("redrawing area: [(%d, %d), (%d, %d)]", b.x0, b.y0, b.x1, b.y1);
			clip.x0 = (b.x0 - ro_plot_origin_x) / 2;
			clip.y0 = (ro_plot_origin_y - b.y1) / 2;
			clip.x1 = (b.x1 - ro_plot_origin_x) / 2;
			clip.y1 = (ro_plot_origin_y - b.y0) / 2;

			data.x = 0;
			data.y = 0;
			data.width = content_get_width(h);
			data.height = content_get_height(h);
			data.background_colour = 0xFFFFFF;
			data.scale = print_scale;
			data.repeat_x = false;
			data.repeat_y = false;

			if (!content_redraw(h, &data, &clip, &ctx)) {
				error_message = "redraw error";
				goto error;
			}

			error = xpdriver_get_rectangle(&b, &more, 0);
			if (error) {
				LOG("xpdriver_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
				error_message = error->errmess;
				goto error;
			}
		}

		yscroll += height;
	} while (yscroll <= content_get_height(h) && --sheets != 0);

	/* make print inactive */
	print_active = false;
	ro_gui_current_redraw_gui = 0;

	/* clean up
	 *
	 * Call PDriver_EndJob via _swix() so that r9 is preserved.  This
	 * prevents a crash if the SWI corrupts it on exit (as seems to
	 * happen on some versions of RISC OS 6).
	 */

	error = (os_error *) _swix(PDriver_EndJob, _IN(0), (int) fhandle);
	if (error) {
		LOG("xpdriver_end_jobw: 0x%x: %s", error->errnum, error->errmess);
		error_message = error->errmess;
		goto error;
	}

	error = xosfind_closew(fhandle);
	if (error) {
		LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
		warn_user("PrintError", error->errmess);
		return false;
	}

	if (old_job) {
		error = xpdriver_select_jobw(old_job, 0, 0);
		if (error) {
			LOG("xpdriver_select_jobw: 0x%x: %s", error->errnum, error->errmess);
			warn_user("PrintError", error->errmess);
			/* the printing succeeded anyway */
			return true;
		}
	}

	rufl_invalidate_cache();

	/* restore document layout and redraw browser window */
	if (content_get_type(h) == CONTENT_HTML)
		content_reformat(h, false, saved_width, saved_height);

	gui_window_redraw_window(g);

	return true;

error:
	xpdriver_abort_job(fhandle);
	xosfind_closew(fhandle);
	if (old_job)
		xpdriver_select_jobw(old_job, 0, 0);
	print_active = false;
	ro_gui_current_redraw_gui = 0;

	warn_user("PrintError", error_message);

	rufl_invalidate_cache();

	/* restore document layout */
	if (content_get_type(h)  == CONTENT_HTML)
		content_reformat(h, false, saved_width, saved_height);

	return false;
}


/**
 * Declare fonts to the printer driver.
 *
 * \param h handle to content being printed
 * \return 0 on success, error message on error
 */

const char *print_declare_fonts(hlcache_handle *h)
{
	unsigned int i;
	struct rect clip;
	struct content_redraw_data data;
	const char *error_message = 0;
	os_error *error;
	struct redraw_context ctx = {
		.interactive = false,
		.background_images = false,
		.plot = &print_fonts_plotters
	};

	free(print_fonts_list);
	print_fonts_list = 0;
	print_fonts_count = 0;
	print_fonts_error = 0;

	clip.x0 = clip.y0 = INT_MIN;
	clip.x1 = clip.y1 = INT_MAX;

	data.x = 0;
	data.y = 0;
	data.width = content_get_width(h);
	data.height = content_get_height(h);
	data.background_colour = 0xFFFFFF;
	data.scale = 1;
	data.repeat_x = false;
	data.repeat_y = false;

	if (!content_redraw(h, &data, &clip, &ctx)) {
		if (print_fonts_error)
			return print_fonts_error;
		return "Declaring fonts failed.";
	}

	for (i = 0; i != print_fonts_count; ++i) {
		LOG("%u %s", i, print_fonts_list[i]);
		error = xpdriver_declare_font(0, print_fonts_list[i],
				pdriver_KERNED);
		if (error) {
			LOG("xpdriver_declare_font: 0x%x: %s", error->errnum, error->errmess);
			error_message = error->errmess;
			goto end;
		}
	}
	error = xpdriver_declare_font(0, 0, 0);
	if (error) {
		LOG("xpdriver_declare_font: 0x%x: %s", error->errnum, error->errmess);
		error_message = error->errmess;
		goto end;
	}

end:
	for (i = 0; i != print_fonts_count; i++)
		free(print_fonts_list[i]);
	free(print_fonts_list);
	print_fonts_list = 0;

	return error_message;
}


bool print_fonts_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
{
	return true;
}


bool print_fonts_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
{
	return true;
}

bool print_fonts_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
{
	return true;
}


bool print_fonts_plot_clip(const struct rect *clip)
{
	return true;
}

bool print_fonts_plot_disc(int x, int y, int radius, const plot_style_t *style)
{
	return true;
}

bool print_fonts_plot_arc(int x, int y, int radius, int angle1, int angle2,
		const plot_style_t *style)
{
	return true;
}

bool print_fonts_plot_bitmap(int x, int y, int width, int height,
		struct bitmap *bitmap, colour bg, bitmap_flags_t flags)
{
	return true;
}

bool print_fonts_plot_path(const float *p, unsigned int n, colour fill, float width,
		colour c, const float transform[6])
{
	return true;
}


/**
 * Plotter for text plotting during font listing.
 */

bool print_fonts_plot_text(int x, int y, const char *text, size_t length,
		const plot_font_style_t *fstyle)
{
	const char *font_family;
	unsigned int font_size;
	rufl_style font_style;
	rufl_code code;

	nsfont_read_style(fstyle, &font_family, &font_size, &font_style);

	code = rufl_paint_callback(font_family, font_style, font_size,
			text, length, 0, 0, print_fonts_callback, 0);
	if (code != rufl_OK) {
		if (code == rufl_FONT_MANAGER_ERROR) {
			LOG("rufl_paint_callback: rufl_FONT_MANAGER_ERROR: ""0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
			print_fonts_error = rufl_fm_error->errmess;
		} else {
			LOG("rufl_paint_callback: 0x%x", code);
		}
		return false;
	}
	if (print_fonts_error)
		return false;

	return true;
}


/**
 * Callback for print_fonts_plot_text().
 *
 * The font name is added to print_fonts_list.
 */

void print_fonts_callback(void *context,
		const char *font_name, unsigned int font_size,
		const char *s8, unsigned short *s16, unsigned int n,
		int x, int y)
{
	unsigned int i;
	char **fonts_list;

	(void) context;  /* unused */
	(void) font_size;  /* unused */
	(void) x;  /* unused */
	(void) y;  /* unused */

	assert(s8 || s16);

	/* check if the font name is new */
	for (i = 0; i != print_fonts_count &&
			strcmp(print_fonts_list[i], font_name) != 0; i++)
		;
	if (i != print_fonts_count)
		return;

	/* add to list of fonts */
	fonts_list = realloc(print_fonts_list,
			sizeof print_fonts_list[0] *
			(print_fonts_count + 1));
	if (!fonts_list) {
		print_fonts_error = messages_get("NoMemory");
		return;
	}
	fonts_list[print_fonts_count] = strdup(font_name);
	if (!fonts_list[print_fonts_count]) {
		print_fonts_error = messages_get("NoMemory");
		return;
	}
	print_fonts_list = fonts_list;
	print_fonts_count++;
}