netsurf/riscos/print.c
James Bursa 9b78daf135 [project @ 2005-07-02 18:17:51 by bursa]
Rewrite calculation of box minimum and maximum widths to improve layout of many pages. Move calculation of column types and border collapsing to box tree normalising stage, since they are layout independent. Add window height parameter to layout and make <html> and <body> at least window height.

svn path=/import/netsurf/; revision=1777
2005-07-02 18:17:51 +00:00

790 lines
20 KiB
C

/*
* This file is part of NetSurf, http://netsurf.sourceforge.net/
* Licensed under the GNU General Public License,
* http://www.opensource.org/licenses/gpl-license
* Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
*/
#include <assert.h>
#include <string.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 "netsurf/utils/config.h"
#include "netsurf/content/content.h"
#include "netsurf/desktop/plotters.h"
#include "netsurf/render/box.h"
#include "netsurf/render/font.h"
#include "netsurf/render/html.h"
#include "netsurf/render/layout.h"
#include "netsurf/riscos/gui.h"
#include "netsurf/riscos/menus.h"
#include "netsurf/riscos/print.h"
#include "netsurf/riscos/wimp.h"
#include "netsurf/utils/log.h"
#include "netsurf/utils/messages.h"
#include "netsurf/utils/utils.h"
/** \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
*/
/* extern globals */
struct gui_window *print_current_window = 0;
bool print_text_black = false;
bool print_active = false;
#ifdef WITH_PRINT
/* 1 millipoint == 1/400 OS unit == 1/800 browser units */
/* static globals */
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;
/* a font in a document */
struct print_font {
font_f handle;
void *font_name;
};
static void print_update_sheets_shaded_state(bool on);
static void print_send_printsave(struct content *c);
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(struct box *box);
static bool print_find_fonts(struct box *box, struct print_font **print_fonts,
int *font_count);
/**
* Prepares all aspects of the print dialog prior to opening.
*
* \param g parent window
*/
void ro_gui_print_prepare(struct gui_window *g) {
char *pdName;
bool printers_exists = true;
os_error *e;
assert(g != NULL);
print_current_window = g;
print_prev_message = 0;
/* Read Printer Driver name */
e = xpdriver_info(0, 0, 0, 0, &pdName, 0, 0, 0);
if (e) {
LOG(("%s", e->errmess));
printers_exists = false;
}
print_bg_images = g->option.background_images;
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);
ro_gui_set_icon_string(dialog_print, ICON_PRINT_SHEETS_TEXT,
messages_get("PrintSheetFilled"));
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);
if (!printers_exists) {
ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_PRINT, true);
}
else {
ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_PRINT, false);
ro_gui_set_window_title(dialog_print, pdName);
}
}
/**
* Handle mouse clicks in print dialog
*
* \param pointer wimp_pointer block
*/
void ro_gui_print_click(wimp_pointer *pointer)
{
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));
if (pointer->buttons == wimp_CLICK_MENU)
return;
switch (pointer->i) {
case ICON_PRINT_SHEETS:
/* retain selection state */
ro_gui_set_icon_selected_state(dialog_print,
pointer->i, true);
print_update_sheets_shaded_state(false);
break;
case ICON_PRINT_TO_BOTTOM:
print_update_sheets_shaded_state(true);
case ICON_PRINT_UPRIGHT:
case ICON_PRINT_SIDEWAYS:
/* retain selection state */
ro_gui_set_icon_selected_state(dialog_print,
pointer->i, true);
break;
case ICON_PRINT_COPIES_UP: copies += 1; break;
case ICON_PRINT_COPIES_DOWN: copies -= 1; break;
case ICON_PRINT_SHEETS_UP: sheets += 1; break;
case ICON_PRINT_SHEETS_DOWN: sheets -= 1; break;
case ICON_PRINT_CANCEL:
print_cleanup();
break;
case ICON_PRINT_PRINT:
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_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_current_window->option.background_images = ro_gui_get_icon_selected_state(dialog_print, ICON_PRINT_BG_IMAGES);
print_send_printsave(print_current_window->bw->current_content);
break;
}
if (copies < 1)
copies = 1;
else if (copies > 99)
copies = 99;
ro_gui_set_icon_integer(dialog_print, ICON_PRINT_COPIES, copies);
if (sheets < 1)
sheets = 1;
else if (sheets > 99)
sheets = 99;
ro_gui_set_icon_integer(dialog_print, ICON_PRINT_SHEETS_VALUE, sheets);
if (sheets > 1)
ro_gui_set_icon_string(dialog_print, ICON_PRINT_SHEETS_TEXT,
messages_get("PrintSheetsFilled"));
else
ro_gui_set_icon_string(dialog_print, ICON_PRINT_SHEETS_TEXT,
messages_get("PrintSheetFilled"));
}
/**
* Handle keypresses in print dialog
*
* \param key wimp_key block
* \return true if keypress dealt with, false otherwise.
*/
bool ro_gui_print_keypress(wimp_key *key)
{
switch (key->c) {
case wimp_KEY_ESCAPE:
print_cleanup();
return true;
case wimp_KEY_RETURN:
if (ro_gui_get_icon_shaded_state(dialog_print, ICON_PRINT_PRINT))
return true;
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_num_copies = atoi(ro_gui_get_icon_string(dialog_print, ICON_PRINT_COPIES));
if (ro_gui_get_icon_selected_state(dialog_print, ICON_PRINT_SHEETS))
print_max_sheets = atoi(ro_gui_get_icon_string(dialog_print, ICON_PRINT_SHEETS_VALUE));
else
print_max_sheets = -1;
print_current_window->option.background_images = ro_gui_get_icon_selected_state(dialog_print, ICON_PRINT_BG_IMAGES);
print_send_printsave(print_current_window->bw->current_content);
return true;
}
return false;
}
/**
* 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 c content to print
*/
void print_send_printsave(struct content *c)
{
wimp_full_message_data_xfer m;
os_error *e;
int len;
len = strlen(c->title) + 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(c);
strncpy(m.file_name, c->title, 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);
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 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 (print_current_window) {
print_document(print_current_window, "printer:");
}
print_cleanup();
}
/**
* Handle message_PRINT_ERROR
*
* \param m the message containing the error
*/
void 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);
print_cleanup();
}
/**
* Handle message_PRINT_TYPE_ODD
*
* \param m the message to handle
*/
void 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 (print_current_window && print_send_printtypeknown(m)) {
print_document(print_current_window, "printer:");
}
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 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 ||
!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);
print_cleanup();
return true;
}
type = info_type >> 16;
/* print to file */
if (!print_document(print_current_window,
m->data.data_xfer.file_name)) {
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;
print_cleanup();
return true;
}
/**
* Handle a bounced dataload message
*
* \param m the message to handle
*/
void 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);
print_cleanup();
}
/**
* Cleanup after printing
*/
void print_cleanup(void)
{
if (print_current_window)
print_current_window->option.background_images =
print_bg_images;
print_current_window = 0;
print_text_black = false;
print_prev_message = 0;
print_max_sheets = -1;
ro_gui_menu_closed();
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;
struct content *c = g->bw->current_content;
const char *error_message;
pdriver_features features;
os_fw fhandle, old_job = 0;
os_error *error;
/* no point printing a blank page */
if (!c) {
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;
}
width = (right - left) / 800;
height = (top - bottom) / 800;
/* layout the document to the correct width */
saved_width = c->width;
saved_height = c->height;
if (c->type == CONTENT_HTML)
layout_document(c, 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 &&
c->type == CONTENT_HTML) {
if ((error_message = print_declare_fonts(box)))
goto error;
}*/
plot = ro_plotters;
ro_plot_set_scale(print_scale);
ro_gui_current_redraw_gui = g;
current_redraw_browser = NULL; /* we don't want to print the selection */
/* print is now active */
print_active = true;
do {
int clip_x0, clip_y0, clip_x1, clip_y1;
os_box b = {left / 400 - 2, bottom / 400 - 2,
right / 400 + 2, top / 400 + 2};
os_hom_trfm t = { { {65536, 0}, {0, 65536} } };
os_coord p = {left, bottom};
osbool more;
xhourglass_percentage((int) (yscroll * 100 / c->height));
/* 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;
}
ro_plot_origin_x = left / 400;
ro_plot_origin_y = top / 400 + yscroll * 2;
while (more) {
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;
if (!content_redraw(c, 0, 0,
c->width, c->height,
clip_x0, clip_y0, clip_x1, clip_y1,
print_scale,
0xFFFFFF)) {
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 <= c->height && --sheets != 0);
/* make print inactive */
print_active = false;
ro_gui_current_redraw_gui = 0;
/* clean up */
error = xpdriver_end_jobw(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 */
if (c->type == CONTENT_HTML)
layout_document(c, saved_width, saved_height);
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 (c->type == CONTENT_HTML)
layout_document(c, saved_width, saved_height);
return false;
}
/**
* Declare fonts to the printer driver.
*
* \param box box tree being printed
* \return 0 on success, error message on error
*/
const char *print_declare_fonts(struct box *box)
{
struct print_font *print_fonts = calloc(255, sizeof (*print_fonts));
unsigned int font_count = 0, i;
const char *error_message = 0;
os_error *error;
if (!print_fonts)
return messages_get("NoMemory");
if (!print_find_fonts(box, &print_fonts, &font_count)) {
LOG(("print_find_fonts() failed"));
error_message = "print_find_fonts() failed";
goto end;
}
for (i = 0; i != font_count; ++i) {
error = xpdriver_declare_font(print_fonts[i].handle,
print_fonts[i].font_name, 0);
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 != font_count; i++)
free(print_fonts[i].font_name);
free(print_fonts);
return error_message;
}
/**
* Find all fonts in a document
*
* \param box Root of box tree
* \param print_fonts pointer to array of fonts in document
* \paran font_count number of fonts declared
* \return true on success, false otherwise
*/
bool print_find_fonts(struct box *box, struct print_font **print_fonts, int *font_count)
{
struct box *a;
const char *txt;
size_t txt_len;
size_t width, rolength, consumed;
const char *rofontname, *rotext;
int i;
assert(box);
#if 0
if (box->text && box->font && box->length > 0) {
txt = box->text;
txt_len = box->length;
if (box->font->ftype == FONTTYPE_UFONT) {
/** \todo handle ufont */
LOG(("ufont"));
return false;
}
nsfont_txtenum(box->font, txt, txt_len,
&width, &rofontname,
&rotext, &rolength,
&consumed);
if (rotext == NULL) {
LOG(("rotext = null (%d)", txt_len));
return false;
}
for (i = 0; i != *font_count; ++i) {
if (!strcmp(((*print_fonts)[i]).font_name, rofontname))
break;
}
if (i == *font_count) {
/* max 255 fonts (as per draw) */
if (*font_count == 255)
return false;
if ((((*print_fonts)[*font_count]).font_name = strdup(rofontname)) == NULL) {
LOG(("failed to strdup (%s)", rofontname));
return false;
}
((*print_fonts)[(*font_count)++]).handle = (font_f)box->font->handle;
}
free((void*)rotext);
}
for (a = box->children; a; a = a->next) {
if (!print_find_fonts(a, print_fonts, font_count))
return false;
}
#endif
return true;
}
#endif