diff options
Diffstat (limited to 'riscos/print.c')
-rw-r--r-- | riscos/print.c | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/riscos/print.c b/riscos/print.c new file mode 100644 index 000000000..a15d9b35d --- /dev/null +++ b/riscos/print.c @@ -0,0 +1,708 @@ +/* + * 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 "netsurf/utils/config.h" +#include "netsurf/content/content.h" +#include "netsurf/render/font.h" +#include "netsurf/render/html.h" +#include "netsurf/render/layout.h" +#include "netsurf/riscos/gui.h" +#include "netsurf/riscos/print.h" +#include "netsurf/riscos/wimp.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/utils.h" + +/** \todo position images correctly (seem to be offset + * to the right and upwards by half the print margin width) + * \todo fix images when printing with the PostScript driver + * (redraws appear not to be intercepted) + * \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 + */ + +#ifdef WITH_PRINT + +/* 1 millipoint == 1/400 OS unit = 1/800 browser units */ + +struct gui_window *print_current_window = 0; +static bool print_in_background = false; +static int print_num_copies = 1; +static bool print_bg_images = true; +static int print_max_sheets = -1; + +/* array of fonts in document - max 255 */ +struct print_font { + font_f handle; + void *fontName; +}; + +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 void print_document(struct gui_window *g, const char *filename); +static bool print_find_fonts(struct box *box, struct print_font **print_fonts, int *numFonts); + +/** + * Open the print dialog + * + * \param g parent window + * \param x leftmost edge of dialog (only if sub_menu == true) + * \param y topmost edge of dialog (as above) + * \param sub_menu open window as a submenu or as a persistent dialog + * \param keypress whether we were opened by a keypress + */ +void ro_gui_print_open(struct gui_window *g, int x, int y, bool sub_menu, bool keypress) +{ + char *pdName; + bool printers_exists = true; + os_error *e; + + assert(g != NULL); + + print_current_window = g; + + /* 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, "sheet is filled"); + 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_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_window_title(dialog_print, pdName); + } + + if (sub_menu) { + e = xwimp_create_sub_menu((wimp_menu *) dialog_print, x, y); + if (e) { + LOG(("xwimp_create_sub_menu: 0x%x: %s", + e->errnum, e->errmess)); + warn_user("MenuError", e->errmess); + } + } + else { + ro_gui_dialog_open_persistant(g->window, dialog_print, !keypress); + } +} + +/** + * 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_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, "sheets are filled"); + else + ro_gui_set_icon_string(dialog_print, ICON_PRINT_SHEETS_TEXT, "sheet is filled"); +} + +/** + * 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: + print_in_background = ro_gui_get_icon_selected_state(dialog_print, ICON_PRINT_IN_BACKGROUND); + 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(); + } +} + +/** + * 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) +{ + /* 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->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 && !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 + */ +void print_ack(wimp_message *m) +{ + int type; + os_error *e; + + /* Read Printer Driver Type */ + e = xpdriver_info(&type, 0, 0, 0, 0, 0, 0, 0); + if (e) { + LOG(("%s", e->errmess)); + print_cleanup(); + return; + } + + type &= 0xFFFF0000; /* we don't care about the version no */ + + if (print_current_window) { + print_document(print_current_window, + (const char*)m->data.data_xfer.file_name); + + /* send dataload */ + m->your_ref = m->my_ref; + m->action = message_DATA_LOAD; + + /* 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) + */ + if (type == 0) + /* postscript */ + m->data.data_xfer.file_type = 0xff5; + else + /* printout */ + m->data.data_xfer.file_type = 0xff4; + + e = xwimp_send_message(wimp_USER_MESSAGE_RECORDED, m, + m->sender); + if (e) { + LOG(("xwimp_send_message: 0x%x: %s", + e->errnum, e->errmess)); + warn_user("WimpError", e->errmess); + /* and delete temporary file */ + xosfile_delete(m->data.data_xfer.file_name, + 0, 0, 0, 0, 0); + print_cleanup(); + } + } +} + +/** + * Handle a bounced dataload message + * + * \param m the message to handle + */ +void print_dataload_bounce(wimp_message *m) +{ + xosfile_delete(m->data.data_xfer.file_name, 0, 0, 0, 0, 0); + print_cleanup(); +} + +/** + * Cleanup after printing + */ +void print_cleanup(void) +{ + print_current_window->option.background_images = print_bg_images; + print_current_window = 0; + print_max_sheets = -1; + xwimp_create_menu((wimp_menu *)-1, 0, 0); + 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 + */ +void print_document(struct gui_window *g, const char *filename) +{ + struct content *c = g->bw->current_content; + struct box *box = NULL; + int temp; + os_error *e; + pdriver_features features; + int left, right, top, bottom, width, height; + os_fw fhandle, old_job = 0; + int yscroll = 0, sheets = print_max_sheets; + + /* no point printing a blank page */ + if (!c) + return; + + LOG(("Printing page (%d)", print_max_sheets)); + + if (c->type == CONTENT_HTML) + box = c->data.html.layout->children; + + /* Read Printer Driver Features */ + e = xpdriver_info(0, 0, 0, &features, 0, 0, 0, 0); + if (e) { + LOG(("%s", e->errmess)); + return; + } + + LOG(("read features")); + + /* Acquire page size */ + e = xpdriver_page_size(&width, &height, &left, &bottom, &right, &top); + if (e) { + LOG(("%s", e->errmess)); + return; + } + + LOG(("page size: %d x %d", width/800, height/800)); + + width = (right - left) / 800; + height = (top - bottom) / 800; + + LOG(("printable area: [(%d, %d), (%d, %d)] = %d x %d", + left, bottom, right, top, width, height)); + + + temp = c->width; + + /* layout the document to the correct width */ + if (c->type == CONTENT_HTML) + layout_document(box, width, c->data.html.box_pool); + + /* open printer */ + e = xosfind_openoutw(0xf, filename, 0, &fhandle); + if (e) { + LOG(("%s", e->errmess)); + return; + } + + LOG(("opened %s", filename)); + + /* select print job */ + e = xpdriver_select_jobw(fhandle, "NetSurf", &old_job); + if (e) { + LOG(("%s", e->errmess)); + xosfind_closew(fhandle); + return; + } + + LOG(("selected job - polling the wimp now does bad things(TM)")); + + /* declare fonts, if necessary */ + if (features & pdriver_FEATURE_DECLARE_FONT && + c->type == CONTENT_HTML) { + struct print_font *print_fonts = + calloc(255, sizeof(*print_fonts)); + int numFonts = 0; + int i; + + if (!print_fonts) { + LOG(("malloc failed")); + goto error; + } + + if(!print_find_fonts(box, &print_fonts, &numFonts)) { + LOG(("print_find_fonts_failed")); + for (i = 0; i != numFonts; ++i) { + free((print_fonts[i]).fontName); + } + free(print_fonts); + goto error; + } + LOG(("%d", numFonts)); + + for (i = 0; i != numFonts; ++i) { + LOG(("0x%x: %s", (print_fonts[i]).handle, (char*)(print_fonts[i]).fontName)); + e = xpdriver_declare_font((font_f)(print_fonts[i]).handle, (print_fonts[i]).fontName, 0); + if (e) { + for (i = 0; i != numFonts; ++i) { + free((print_fonts[i]).fontName); + } + free(print_fonts); + LOG(("%s", e->errmess)); + goto error; + } + } + + for (i = 0; i != numFonts; ++i) { + free((print_fonts[i]).fontName); + } + free(print_fonts); + + e = xpdriver_declare_font(0, 0, 0); + if (e) { + LOG(("%s", e->errmess)); + goto error; + } + + LOG(("declared fonts")); + } + + do { + 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}; + + e = xhourglass_percentage((int)(yscroll*100/c->height)); + if (e) { + LOG(("%s", e->errmess)); + /* the hourglass failing to be updated + * shouldn't stop the printjob + */ + } + + /* Give page rectangle */ + e = xpdriver_give_rectangle(0, &b, &t, &p, os_COLOUR_WHITE); + if (e) { + LOG(("%s", e->errmess)); + goto error; + } + + LOG(("given rectangle: [(%d, %d), (%d, %d)]", b.x0, b.y0, b.x1, b.y1)); + + /* and redraw the document */ + osbool more; + e = xpdriver_draw_page(print_num_copies, &b, 0, 0, &more, 0); + if (e) { + LOG(("%s", e->errmess)); + goto error; + } + + LOG(("done draw_page")); + + ro_gui_current_redraw_gui = g; + + while (more) { + LOG(("redrawing area: [(%d, %d), (%d, %d)]", b.x0, b.y0, b.x1, b.y1)); + if (c) { + content_redraw(c, b.x0, b.y1+(yscroll*2), + c->width * 2, c->height * 2, + b.x0, b.y0, + b.x1-1, b.y1-1, + 1.0 /* scale == 100% */); + } + e = xpdriver_get_rectangle(&b, &more, 0); + if (e) { + LOG(("%s", e->errmess)); + ro_gui_current_redraw_gui = NULL; + goto error; + } + } + + yscroll += height; + } while (yscroll <= c->height && --sheets != 0); + + ro_gui_current_redraw_gui = NULL; + LOG(("finished redraw")); + + /* clean up */ + e = xpdriver_end_jobw(fhandle); + if (e) { + LOG(("%s", e->errmess)); + goto error; + } + xosfind_close(fhandle); + if (old_job) xpdriver_select_jobw(old_job, 0, 0); + + LOG(("done job")); + + /* restore document layout */ + if (c->type == CONTENT_HTML) + layout_document(box, temp, c->data.html.box_pool); + + return; + +error: + xpdriver_abort_job(fhandle); + xosfind_close(fhandle); + if (old_job) xpdriver_select_jobw(old_job, 0, 0); + + /* restore document layout */ + if (c->type == CONTENT_HTML) + layout_document(box, temp, c->data.html.box_pool); +} + +/** + * Find all fonts in a document + * + * \param box Root of box tree + * \param print_fonts pointer to array of fonts in document + * \paran numFonts number of fonts declared + * \return true on success, false otherwise + */ +bool print_find_fonts(struct box *box, struct print_font **print_fonts, int *numFonts) +{ + struct box *a; + const char *txt; + int txt_len; + unsigned int width, rolength, consumed; + const char *rofontname, *rotext; + int i; + + assert(box); + + 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 != *numFonts; ++i) { + if (!strcmp(((*print_fonts)[i]).fontName, rofontname)) + break; + } + + if (i == *numFonts) { + /* max 255 fonts (as per draw) */ + if (*numFonts == 255) + return false; + if ((((*print_fonts)[*numFonts]).fontName = strdup(rofontname)) == NULL) { + LOG(("failed to strdup (%s)", rofontname)); + return false; + } + ((*print_fonts)[(*numFonts)++]).handle = (font_f)box->font->handle; + } + + free((void*)rotext); + } + + for (a = box->children; a; a = a->next) { + if (!print_find_fonts(a, print_fonts, numFonts)) + return false; + } + + return true; +} +#endif |