/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Printing.c */ /* */ /* Purpose: Printing functions for the browser. */ /* */ /* This source is fairly closely tied to */ /* PrintStyle.c, as the Print dialogue */ /* can open and close the Print Style */ /* dialogue. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 27-Jan-97: Created. */ /* 25-Aug-97: Overhaul (read rewrite) to */ /* the new dialogue handling */ /* model, as for Open URL etc. */ /***************************************************/ #include "signal.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "printdbox.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "Utils.h" #include "Browser.h" #include "FontManage.h" #include "Images.h" #include "Memory.h" #include "Protocols.h" #include "PrintStyle.h" #include "Redraw.h" #include "Reformat.h" #include "Toolaction.h" #include "Toolbars.h" #include "Windows.h" #include "Printing.h" /* Local structures. */ /* */ /* Holds info on the Print dialogue's contents; small enough */ /* to hold as a static, as the code to dynamically allocate it */ /* would occupy more room than the structure itself. */ #define End_Whole 0 #define End_Visible 1 #define End_Many 2 typedef struct { int copies; /* Number of copies to print. */ int pages; /* If 'end' is 2, the number of pages to fill. */ unsigned int end :2; /* 0 = whole page, 1 = to bottom of visible area, 2 = for 'pages' pages. */ unsigned int start :1; /* 1 = whole page, 0 = top of visible area. */ unsigned int reformat :1; /* 1 = reformat to fit page (if start is not 0 and end is not -1), else don't. */ unsigned int orientation :1; /* 1 = portrait, 0 = landscape. */ } print_contents; /* The following stores the four basic display type settings */ /* (underline links, show images etc.) for the browser to be */ /* printed. This is so that the settings may be restored */ /* after a print. */ typedef struct { unsigned int underline_links :1; unsigned int use_source_cols :1; unsigned int show_foreground :1; unsigned int show_background :1; } print_restorable; /* Local variables */ static int globaljob = 0; static int globalold_job = 0; static int defaults_set = 0; static ObjectId self_id = 0; static ObjectId window_id = 0; static ObjectId ancestor_id = 0; static browser_data * ancestor_browser = NULL; static print_contents contents; static print_restorable restore; /* Static function prototypes */ static _kernel_oserror * print_read_contents (ObjectId dialogue, print_contents * contents); static _kernel_oserror * print_set_contents (ObjectId dialogue, print_contents * contents); static int print_start (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int print_cancel (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int print_check_contents (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static _kernel_oserror * print_page (browser_data * b, int copies, int from, int end, int to, int reformat, int orientation, const char * path); static void print_prepare_browser (browser_data * source, browser_data * store, int lmarg, int rmarg, int tmarg, int bmarg); static void print_restore_browser (browser_data * original, browser_data * copy); /*************************************************/ /* print_open_for() */ /* */ /* Creates and opens a Print dialogue for a */ /* given browser, opening near the pointer. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which is the ancestor of the */ /* dialogue; */ /* */ /* Object ID to use as a parent, or */ /* 0 for none. */ /*************************************************/ _kernel_oserror * print_open_for(browser_data * b, ObjectId parent) { ObjectId id; /* Create the object - if it is already created, this will */ /* just return the ID of the existing object. */ RetError(toolbox_create_object(0, "PrintDbox", &id)); RetError(toolbox_show_object(0, id, Toolbox_ShowObject_Centre, NULL, parent, -1)); return NULL; } /*************************************************/ /* print_read_contents() */ /* */ /* Reads the contents of the Print dialogue */ /* into a print_contents structure. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Pointer to the structure to write */ /* to. */ /*************************************************/ static _kernel_oserror * print_read_contents(ObjectId dialogue, print_contents * contents) { int state, selected; /* Start at top of visible area (0) or whole page (1) radios */ RetError(radiobutton_get_state(0, dialogue, PStartWhole, &state, NULL)); contents->start = !!state; /* End radios - bottom of page, of visible area, or stop after */ /* pages defined in the 'PEndManyNum' number range gadget */ RetError(radiobutton_get_state(0, dialogue, PEndWhole, NULL, &selected)); RetError(numberrange_get_value(0, dialogue, PEndManyNum, &contents->pages)); /* Note that PEndVisible etc. are component IDs defined in */ /* Print.h, whilst End_Visible (with the underscore) etc. */ /* are option values defined at the top of this file. */ switch (selected) { default: case PEndWhole: contents->end = End_Whole; break; case PEndVisible: contents->end = End_Visible; break; case PEndMany: contents->end = End_Many; break; } /* Reformat page to fit */ RetError(optionbutton_get_state(0, dialogue, PReformatToFit, &state)); contents->reformat = !!state; /* Orientation radios; portrait (1) or landscape (0) */ RetError(radiobutton_get_state(0, dialogue, POriUpright, &state, NULL)); contents->orientation = !!state; /* Read the 'Number of copies' number range gadget */ RetError(numberrange_get_value(0, dialogue, PCopiesNum, &contents->copies)); return NULL; } /*************************************************/ /* print_set_contents() */ /* */ /* Sets the contents of the Print dialogue from */ /* a print_contents structure. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Pointer to the structure to read */ /* from. */ /*************************************************/ static _kernel_oserror * print_set_contents(ObjectId dialogue, print_contents * contents) { /* Start position */ if (!contents->start) RetError(radiobutton_set_state(0, dialogue, PStartVisible, 1)) else RetError(radiobutton_set_state(0, dialogue, PStartWhole, 1)); /* End position, including the 'number of pages to fill' number range */ switch (contents->end) { default: case End_Whole: RetError(radiobutton_set_state(0, dialogue, PEndWhole, 1)); break; case End_Visible: RetError(radiobutton_set_state(0, dialogue, PEndVisible, 1)); break; case End_Many: RetError(radiobutton_set_state(0, dialogue, PEndMany, 1)); break; } RetError(numberrange_set_value(0, dialogue, PEndManyNum, contents->pages)); /* The reformat option, including greying / ungreying it */ RetError(optionbutton_set_state(0, dialogue, PReformatToFit, contents->reformat)); /* As well as greying / ungreying the reformat option, this handles */ /* the label text on the 'pages to fill' number range. */ print_check_contents(0, NULL, NULL, NULL); /* Orientation */ if (!contents->orientation) RetError(radiobutton_set_state(0, dialogue, POriSideways, 1)) else RetError(radiobutton_set_state(0, dialogue, POriUpright, 1)); /* Number of copies */ RetError(numberrange_set_value(0, dialogue, PCopiesNum, contents->copies)); return NULL; } /*************************************************/ /* print_set_defaults() */ /* */ /* Fills in the local print_contents structure */ /* with the default values to put in a Print */ /* dialogue, if they have not already been */ /* filled in. */ /* */ /* If the dialogue is open, the contents are */ /* updated. */ /* */ /* Returns: 1 if the structure was filled in, */ /* else 0. */ /*************************************************/ int print_set_defaults(void) { if (!defaults_set) { /* Number of copies */ contents.copies = atoi(lookup_choice("PrintCopies:1",0,0)); /* Check it is within bounds */ if (contents.copies < Limits_Lower_Copies) contents.copies = Limits_Lower_Copies; if (contents.copies > Limits_Upper_Copies) contents.copies = Limits_Upper_Copies; /* Start position - 'start' or 'visible', though in fact any */ /* non-'visible' string defaults as 'start'. */ if (!strcmp(lookup_choice("PrintStart:start",0,0),"visible")) contents.start = 0; else contents.start = 1; /* End position - print the whole page, down to the bottom of the */ /* visible area, or fill up as many sheets as specified in the */ /* 'pages' field of the print_contents structure (see below). */ if (!strcmp(lookup_choice("PrintEnd:end",0,0),"end")) contents.end = End_Whole; else if (!strcmp(lookup_choice("PrintEnd",1,0),"visible")) contents.end = End_Visible; else contents.end = End_Many; contents.pages = atoi(lookup_choice("PrintEnd",1,0)); /* Check it is within bounds */ if (contents.pages < Limits_Lower_Sheets) contents.pages = Limits_Lower_Sheets; if (contents.pages > Limits_Upper_Sheets) contents.pages = Limits_Upper_Sheets; /* Reformat - 'yes' or 'no', default to 'yes' */ if (!strcmp(lookup_choice("PrintReform:yes",0,0),"no")) contents.reformat = 0; else contents.reformat = 1; /* Orientation - 'upright' or 'sideways', though in fact any */ /* non-'sideways' string defaults as 'upright'. */ if (!strcmp(lookup_choice("PrintOrient:upright",0,0),"sideways")) contents.orientation = 0; else contents.orientation = 1; defaults_set = 1; if (window_id) print_set_contents(window_id, &contents); return 1; } else return 0; } /*************************************************/ /* print_to_be_shown() */ /* */ /* Called before a print dialogue opens. Deals */ /* with setting this up with default values and */ /* filling in print_old as appropriate, so that */ /* if the dialogue is cancelled its contents may */ /* be correctly restored. */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ int print_to_be_shown(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { int was_open = 0; ObjectId ps_window; ObjectId ps_ancestor; /* If the stored dialogue ID is non-zero on entry, the dialogue */ /* was reopened without closing - so get rid of the various */ /* event handlers before we reregister them. */ if (window_id) { /* Was the Print Style window open too? */ printstyle_return_dialogue_info(&ps_window, &ps_ancestor); if (ps_window) was_open = 1; /* This will close the Print window and Print Style (if open) */ print_close(0, 1); } /* Record the dialogue ID, the ancestor ID, and if this is */ /* non-zero, the browser to which that ID refers. */ self_id = idb->self_id; ancestor_id = idb->ancestor_id; if (ancestor_id) ChkError(toolbox_get_client_handle(0, ancestor_id, (void *) &ancestor_browser)); /* If this is for an ancestor browser, use whatever frame is selected */ /* instead - allows toolbar buttons and keyboard shortcuts to work in */ /* a sensible fashion... */ if (ancestor_browser->selected_frame) { ancestor_browser = ancestor_browser->selected_frame; ancestor_id = ancestor_browser->self_id; } /* If we have a browser, remember its restorable details. */ if (ancestor_browser) { restore.underline_links = ancestor_browser->underline_links; restore.use_source_cols = ancestor_browser->use_source_cols; restore.show_foreground = ancestor_browser->show_foreground; restore.show_background = ancestor_browser->show_background; /* If required, force background images off */ if (!strcmp(lookup_choice("PrintPlain:yes",0,0),"yes")) browser_set_look(ancestor_browser, window_id, -1, -1, -1, 0); } /* Get the underlying window ID */ ChkError(printdbox_get_window_id(0, self_id, &window_id)); /* The Print Style dialogue may be open, too. We could take the lazy route */ /* and just close it, but instead, we will ask it for its window details, */ /* and then recall its close code and ToBeShown code with the right info. */ if (was_open) { IdBlock ps_id; ps_id.self_id = ps_window; /* (Make sure the Print Style routines ask the Print routines for the (new) ancestor) */ ps_id.ancestor_id = NULL; printstyle_to_be_shown(0, NULL, &ps_id, NULL); } /* Register handlers for alternate Print/Cancel buttons */ ChkError(event_register_toolbox_handler(window_id, EPStartPrint, print_start, (void *) ancestor_id)); ChkError(event_register_toolbox_handler(window_id, EPCancelPrint, print_cancel, (void *) ancestor_id)); /* Various alterations of icons / buttons for different UI styles */ if (!strcmp(lookup_control("AlterNumranges:no",0,0),"yes")) { _kernel_oserror * e; WimpGetIconStateBlock icon; int iconlist [Limits_NRangeIcons]; char buffer [Limits_Message]; /* Get the object's window handle and the icon handle for the given component */ e = window_get_wimp_handle(0, window_id, &icon.window_handle); if (!e) { ComponentId writable; int loop; for (loop = 0; loop < 2; loop ++) { /* Get the number range's writable component ID */ e = numberrange_get_components(NumberRange_GetComponents_ReturnNumericalField, window_id, loop == 1 ? PCopiesNum : PEndManyNum, &writable, NULL, NULL, NULL); /* Turn this into an icon handle */ if (!e) e = gadget_get_icon_list(0, window_id, writable, iconlist, sizeof(iconlist), NULL); if (!e) { icon.icon_handle = iconlist[0]; /* Get the icon state and set the icon flags with the */ /* programming text defined in the Messages file */ e = wimp_get_icon_state(&icon); if (!e) { strncpy(buffer, lookup_control("AlterWith",1,0), sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = 0; windows_process_icon_text(&icon, buffer, 0); } } } } } /* Register a handler to cope with the pages number range changing */ ChkError(event_register_toolbox_handler(window_id, NumberRange_ValueChanged, print_check_contents, (void *) window_id)); /* Similarly, the same function is called to ensure things are greyed */ /* or ungreyed as required when the radio buttons that affect the */ /* 'Reformat page to fit paper' option are activated. */ ChkError(event_register_toolbox_handler(window_id, EPEnableReformat, print_check_contents, (void *) window_id)); /* Install an animation handler, if there's an appropriate gadget */ if ( controls.dbox_anims && !gadget_get_type(0, window_id, StatusBarAnimAnim, NULL) ) register_null_claimant(Wimp_ENull, toolbars_animate_slow, (void *) window_id); /* If defaults have never been set before, set them now */ print_set_defaults(); /* Make sure the Print Style dialogue is set up, too */ printstyle_set_defaults(); /* Done! */ return 1; } /*************************************************/ /* print_start() */ /* */ /* Handles clicks on the 'OK' (or 'Print', etc.) */ /* button in the Print dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int print_start(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { /* First, make sure we effectively OK the contents of the */ /* Print Style dialogue. */ printstyle_ok(0, NULL, NULL, NULL); /* Because the printing starts from reception of an external message, */ /* can't use a local copy of the print_contents structure and allow */ /* Adjust-clicks on Print/OK/Whatever. So always close the window. */ ChkError(print_read_contents(window_id, &contents)); ChkError(print_close(0, 0)); /* First stage of printing protocol: Broadcast a PrintSave message */ ChkError(protocols_pp_send_print_save()); return 1; } /*************************************************/ /* print_cancel() */ /* */ /* Handles clicks on the 'Cancel' button in the */ /* Print dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int print_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { WimpGetPointerInfoBlock info; /* Restore the old contents */ ChkError(print_set_contents(window_id, &contents)); /* If Select was pressed, the dialogue should close. */ /* (No button => Escape was pressed). */ ChkError(wimp_get_pointer_info(&info)); if ((info.button_state & Wimp_MouseButtonSelect) || !info.button_state) { ChkError(print_close(0, 0)); /* If we forced background images off, put them back again */ if (!strcmp(lookup_choice("PrintPlain:yes",0,0),"yes")) browser_set_look(ancestor_browser, window_id, -1, -1, -1, restore.show_background); } return 1; } /*************************************************/ /* print_close() */ /* */ /* If the Print dialogue is opened, this will */ /* close it, deregistering any associated event */ /* handlers. */ /* */ /* Parameters: An object ID, or 0. If not zero, */ /* the ID must match the ancestor */ /* recorded when the dialogue was */ /* opened or no action is taken. */ /* */ /* 0 to close the dialogue, 1 to do */ /* everything except that. */ /*************************************************/ _kernel_oserror * print_close(ObjectId ancestor, int do_not_close) { _kernel_oserror * e = NULL; if (ancestor && ancestor != ancestor_id) return NULL; /* If the Print Style window is open, this will close it */ printstyle_close(ancestor_id, do_not_close); if (window_id) { /* Deregister associated event handlers */ e = event_deregister_toolbox_handlers_for_object(window_id); if (e) goto print_close_exit; /* If there was a null handler, remove it */ if ( controls.dbox_anims && !gadget_get_type(0, window_id, StatusBarAnimAnim, NULL) ) deregister_null_claimant(Wimp_ENull, toolbars_animate_slow, (void *) window_id); if (!do_not_close) { /* Restore input focus to the browser window, if the */ /* print dialogue still had it. */ if (ancestor_id != NULL_ObjectId) { WimpGetCaretPositionBlock caret_b; int caret_w; /* Do we have the input focus? */ e = wimp_get_caret_position(&caret_b); if (!e) { e = window_get_wimp_handle(0, window_id, &caret_w); if (caret_w == caret_b.window_handle) { e = browser_give_general_focus(ancestor_browser); if (e) goto print_close_exit; } } } /* Close the dialogue */ e = toolbox_hide_object(0, self_id); } } print_close_exit: self_id = window_id = 0; return e; } /*************************************************/ /* print_check_contents() */ /* */ /* If the state of the various radio buttons */ /* changes, this may be called to see if the */ /* Reformat option in the Print dialogue should */ /* be enabled (ungreyed) or disabled (greyed). */ /* Similarly, if the contents of the number of */ /* sheets to fill number range changes, this */ /* should be called to ensure the label has the */ /* correct pluralisation applied. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int print_check_contents(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { int state1, state2, pages; unsigned int flags; /* Get the radio button states */ ChkError(radiobutton_get_state(0, window_id, PStartWhole, NULL, &state1)); ChkError(radiobutton_get_state(0, window_id, PEndWhole, NULL, &state2)); /* If the PStartVisible and PEndVisible radios are not selected, */ /* can enable the Reformat option; else disable it. But only */ /* change it's state (don't grey it if already greyed, say). */ ChkError(gadget_get_flags(0, window_id, PReformatToFit, &flags)); if (state1 != PStartVisible && state2 != PEndVisible) { if (flags & Gadget_Faded) { ChkError(gadget_set_flags(0, window_id, PReformatToFit, flags & ~Gadget_Faded)); } } else { if (!(flags & Gadget_Faded)) { ChkError(gadget_set_flags(0, window_id, PReformatToFit, flags | Gadget_Faded)); } } /* Check the pages number range, and update the label if necessary. */ { char text[Limits_PEndManyLabel]; ChkError(numberrange_get_value(0, window_id, PEndManyNum, &pages)); ChkError(button_get_value(0, window_id, PEndManyLabel, text, Limits_PEndManyLabel, NULL)); text[sizeof(text) - 1] = 0; if (pages == 1) { /* If the existing text isn't what we intend to change it to, then change it; */ /* i.e. don't set the same thing twice, as this will flicker badly. */ if (strcmp(text, lookup_token("PagesSingle:sheet is filled",0,0))) { ChkError(button_set_value(0, window_id, PEndManyLabel, lookup_token("PagesSingle:sheet is filled",0,0))); } } else { /* Again, only change the text - don't set the same thing twice. */ if (strcmp(text, lookup_token("PagesMany:sheets are filled",0,0))) { ChkError(button_set_value(0, window_id, PEndManyLabel, lookup_token("PagesMany:sheets are filled",0,0))); } } } return 1; } /*************************************************/ /* print_print() */ /* */ /* Calls the printing engine with parameters */ /* specified in the local static print_info */ /* structure 'print_current'. */ /* */ /* Entry point is typically from a handler */ /* dealing with the printing message protocol */ /* (see handle_messages). */ /* */ /* Parameters: Pointer to pathname to print to, */ /* or NULL to go straight to the */ /* 'printer:' device. */ /*************************************************/ void print_print(const char * path) { _kernel_oserror * e; /* Must have a browser to print */ if (!ancestor_browser) return; /* Do the printing */ printing = 1; e = print_page(ancestor_browser, contents.copies, contents.start, contents.end, contents.pages, contents.reformat, contents.orientation, path); printing = 0; if (e) show_error_ret(e); /* On completion, with or without error (as e.g. Escape may */ /* be pressed and you'd still want the following), restore */ /* the basic browser display characteristics, if the Print */ /* dialogue was closed (i.e. Print activated with Select). */ if (!window_id) { show_error_ret(browser_set_look(ancestor_browser, 0, restore.underline_links, restore.use_source_cols, restore.show_foreground, restore.show_background)); } } /*************************************************/ /* print_page() */ /* */ /* Prints out a page, assuming that all the */ /* relevant protocol stuff to ensure it's OK to */ /* proceed has been done already. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page to print; */ /* */ /* The number of copies to print; */ /* */ /* 1 to start at the top of the web */ /* page, else start from the top of */ /* the visible area in the window; */ /* */ /* Where to end - End_Whole, */ /* End_Visible or End_Many (as */ /* defined at the top of the file); */ /* */ /* For End_Many, how many sheets to */ /* fill; */ /* */ /* 1 to reformat to fit the page */ /* width (orientation is taken into */ /* account), else 0 to keep the */ /* width of the window (if it falls */ /* off the page, tough...!); */ /* */ /* 1 = portrait, 0 = landscape; */ /* */ /* Pointer to pathname to print to, */ /* or NULL to go straight to the */ /* 'printer:' device. */ /*************************************************/ static _kernel_oserror * print_page(browser_data * b, int copies, int from, int end, int to, int reformat, int orientation, const char * path) { _kernel_oserror * e = NULL; WimpRedrawWindowBlock redraw; BBox box, last_rect; int must_restore; int job, old_job; int more, page; int top, bottom; int next_line, temp; int estimated_pages; int area_completed, page_area; int lmarg, bmarg; int rmarg, tmarg; unsigned int features; int portrait [2] [2] = { {0x10000, 0}, {0, 0x10000} }; int landscape [2] [2] = { {0, -0x10000}, {0x10000, 0} }; browser_data localbrowser; void (*old_sigint_handler) (int); /* Check to see if there is a printer driver ready */ e = _swix(PDriver_Info, _OUT(3), &features); if (e) return e; /* Find the current page margins (and therefore, page size, */ /* as all margins are expressed as offsets from the bottom */ /* left hand corner of the paper). */ e = _swix(PDriver_PageSize, _OUTR(3,6), &lmarg, &bmarg, &rmarg, &tmarg); if (e) return e; /* If in landscape mode, want to treat the margins */ /* in a reversed sense. */ if (!orientation) { Swap(tmarg,rmarg); Swap(bmarg,lmarg); } /* Start the hourglass, this could take a while. */ /* */ /* Using Hourglass_Start as otherwise the first percentage */ /* setting may be missed, since the hourglass isn't actually */ /* on yet (there's a default delay before appearance with */ /* calling Hourglass_On). */ _swix(Hourglass_Start, _IN(0), 1); /* If the user specified printing to or from something that */ /* depends upon the visible area, can't then reformat. */ if (reformat && end != End_Visible && from) { int leds = 1; /* Reformat the page ready for printing. It used to be possible to do all of */ /* this in a separate browser_data structure and, being careful about flex, */ /* reformat in that 'virtual' browser. This enabled reformatting internally */ /* not to affect the main browser page. */ /* */ /* Tables, however, screwed this up big time. Table cells were malloced, but */ /* no record of this was kept, in the first cut of the code. So in the end, */ /* the address of the cell array was kept in the HStream defining the table. */ /* However, you can only have one user of that at any one time... */ /* */ /* Four solutions to this (where NA = Not Acceptable): */ /* */ /* 1. Get rid of the 'reformat to fit page' option (NA) */ /* 2. Only allow the above when there are no tables on the page (NA) */ /* 3. Copy the entire token stream as well as the flex data (NA) */ /* 4. Reformat in the actual browser and have it reformat again afterwards. */ /* */ /* Since 1 to 3 aren't acceptable - 3 mostly because not only is it a lot of */ /* memory to have to find, but it's in malloc space -> WimpSlot problems - */ /* only 4 is left. So this is what we now do here. Consequently, lots of */ /* bits of the browser_data structure have to be copied away and restored */ /* later, which can get quite messy. */ /* */ /* The fact that option 4 was chosen doesn't mean it isn't hideous... */ print_prepare_browser(b, &localbrowser, lmarg, rmarg, tmarg, bmarg); must_restore = 1; /* Now call the reformatter, and loop round until finished. */ e = reformat_format_from(b, -1, 1, -1); if (e) return e; while (reformat_formatting(b)) { reformat_reformatter(b); /* It is virtually impossible to assess progress without */ /* doing something time consuming like scan the token */ /* list and work out how far down it we are, compared to */ /* the whole length. Instead, alternate the LEDs - this */ /* fits in well with what the table reformatter code */ /* will be doing with the hourglass. */ leds ^= 3; _swix(Hourglass_LEDs, _INR(0,1), 3, leds); } _swix(Hourglass_LEDs, 0, 0); } else must_restore = 0; localbrowser.use_source_cols = 0; /* Open up the output stream */ e = _swix(OS_Find, _INR(0,1) | _OUT(0), 0x8F, path ? path : "printer:", &job); if (e) goto out3; /* Estimate the number of pages to print */ { int page_height; /* Get the printable page height */ convert_to_os(tmarg - bmarg, &page_height); estimated_pages = (reformat_return_extent(b, NULL) / page_height) + 1; } globaljob = job; /* Stop the C library intercepting Escape, since */ /* this should be left to the print SWIs. */ old_sigint_handler = signal(SIGINT, SIG_IGN); /* Start up the printing system */ e = _swix(PDriver_SelectJob, _INR(0,1) | _OUT(0), job, lookup_token("PJobName:Web page",0,0), &old_job); if (e) goto out1; globalold_job = old_job; /* Declare fonts that have been used */ if (features & Browser_Printer_DeclareFont) { fm_face h; /* If using system font, only the system faces will be */ /* used; otherwise, need to declare the sans, serif */ /* and fixed faces. */ if (!choices.system_font) { h = fm_find_font(NULL, "sans", 192,192,0,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "sans", 192,192,1,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "sans", 192,192,0,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "sans", 192,192,1,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "serif", 192,192,0,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "serif", 192,192,1,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "serif", 192,192,0,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "serif", 192,192,1,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "fixed", 192,192,0,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "fixed", 192,192,1,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "fixed", 192,192,0,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "fixed", 192,192,1,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); } /* Finish declaring fonts */ e = _swix(PDriver_DeclareFont, _INR(0,2), 0, 0, 2); if (e) goto out2; } /* Set the bottom left hand corner of the rectangle to redraw */ box.xmin = orientation ? lmarg : bmarg; box.ymin = orientation ? bmarg : rmarg; /* Set 'top' to the offset from the top of the document to get */ /* to the top of the currently visible portion, and 'bottom' */ /* to the offset to get to the bottom of the currently visible */ /* portion. The positive direction is downwards (so they should */ /* both be positive numbers). */ { WimpGetWindowStateBlock state; int htop, hbot; state.window_handle = b->window_handle; e = wimp_get_window_state(&state); if (e) goto out2; /* Get the basic offsets */ top = -state.yscroll; bottom = state.visible_area.ymax - state.visible_area.ymin; /* Correct for toolbar presence */ if (!controls.swap_bars) { htop = toolbars_button_height(b) + toolbars_url_height(b); hbot = toolbars_status_height(b); } else { htop = toolbars_status_height(b); hbot = toolbars_button_height(b) + toolbars_url_height(b); } if (htop) htop += wimpt_dy(); if (hbot) hbot += wimpt_dy(); top += htop; bottom -= (htop + hbot - top); /* If 'from' is non-zero, want to print from the top of the whole */ /* page; else from the top of the visible area as worked out above. */ if (from) top = htop; } redraw.yscroll = -top; redraw.xscroll = 0; /* Loop round for all pages. The y scroll position is set */ /* initially to be correct for the place we want to print */ /* from, and xscroll is set to 0 to mark that no calls */ /* to redraw_draw have happened yet. */ page = 0; while ( ( end == End_Many && page < to ) || ( end != End_Many ) ) { page ++; /* Set up the Redraw block ready for the calls to redraw_draw */ redraw.visible_area.xmax = rmarg - lmarg; redraw.visible_area.xmin = 0; convert_to_os(redraw.visible_area.xmax, &redraw.visible_area.xmax); /* Vertical margins are complicated by the user settings. For printing */ /* down to the bottom of the web page, want to use a full page rectangle; */ /* for printing down to the bottom of the visible area, want to use the */ /* 'bottom' variable worked out above. Note the checking to work out */ /* pagination - it *could* be possible that the visible area is taller */ /* than a single sheet of paper for the current printer. */ convert_to_os(tmarg - bmarg, &temp); /* 'temp' now holds the printable page height in OS units */ if (end != End_Visible) { redraw.visible_area.ymax = temp, redraw.visible_area.ymin = 0; } else { /* Need to subtract an amount from 'bottom' to mark that a page has been */ /* done, but must do that after the page rendering so the redraw routines */ /* have filled in redraw.xscroll with the last coordinate used - if we */ /* don't do this, we can't tell how much of the web page was actually */ /* used. Remember that xscroll and yscroll are negative; xscroll refers */ /* to the vertical offset to start the next page at, and yscroll was the */ /* offset that this one started at. */ if (redraw.xscroll && bottom) bottom -= (redraw.yscroll - redraw.xscroll); if (bottom - top > temp) /* And *not* '>= temp'! See 'else' code below */ { redraw.visible_area.ymax = temp; } else { redraw.visible_area.ymax = bottom - top; bottom = 0; /* Since this rectangle is at most a full page in height but probably */ /* less, must shift the bottom left hand coordinate of it up an */ /* appropriate amount to print the page fragment at the top of the */ /* paper rather than the bottom. */ temp -= redraw.visible_area.ymax; /* (Page height minus rectangle height in OS units) */ convert_to_points(temp, &temp); if (orientation) box.ymin += temp; else box.xmin += temp; /* Signal to redraw_draw that the last line on this page may be split */ /* over the bottom of the page, as that's what the user is seeing in */ /* the real window right now. */ printing = 2; } redraw.visible_area.ymin = 0; } /* redraw_draw will give the y coordinate of the next line to */ /* print when it exits by placing an appropriate coordinate */ /* in the xscroll field of the redraw block that is passed to */ /* it during the main printer redraw loop. */ if (redraw.xscroll) redraw.yscroll = redraw.xscroll, redraw.xscroll = 0; /* Start drawing things */ e = _swix(PDriver_GiveRectangle, _INR(0,4), 0, /* Rectangle ID word - only 1 per page, so 0 will do */ &redraw.visible_area, orientation ? &portrait : &landscape, &box, Redraw_Colour_White); if (e) goto out2; e = _swix(PDriver_DrawPage, _INR(0,3) | _OUT(0), (copies) | ((features & Browser_Printer_PreScansRectangles) ? (1<<24) : (0)), &redraw.redraw_area, 0, 0, &more); if (e) goto out2; /* Give an indication of progress */ { int percent; percent = (100 * (page - 1)) / estimated_pages; if (percent > 99) percent = 99; _swix(Hourglass_Percentage, _IN(0), percent); } /* The redraw loop itself. The area stuff is for the */ /* in-page hourglass percentage; see later comments. */ page_area = (redraw.visible_area.xmax - redraw.visible_area.xmin) * (redraw.visible_area.ymax - redraw.visible_area.ymin); last_rect.xmin = last_rect.xmax = last_rect.ymin = last_rect.ymax = 0; next_line = area_completed = 0; while (more) { /* Ensure images are correct for the current mode */ image_mode_change(); /* Do the redraw */ e = redraw_draw(b, &redraw, 0, 0); if (e) goto out2; /* Don't want to start pagination issues if this is just a prescan */ if ((features & Browser_Printer_PreScansRectangles) && (more & 1<<24)) { redraw.xscroll = 0; } else { int this_area; BBox * i; if (redraw.xscroll != 0) next_line = redraw.xscroll, redraw.xscroll = 0; /* Give a percentage completed indicator. This is first based */ /* on the current page being printed, so there's some scaling */ /* of the 100% range to cope with the fact that if you're on */ /* page 3 of 4, the variation must be between 50% and 75%, */ /* for example. Since rectangle order cannot be relied upon, */ /* need to use the area printed so far for the calculation. */ /* This may fail under unusual circumstances, and certainly */ /* is not fully accurate as the rectangles always overlap by */ /* a small amount (the printer driver gives room for rounding */ /* errors by overlapping the rectangles) but in any case */ /* there will at least be some kind of percentage indication! */ /* With bit image printing, which can be painfully slow, this */ /* is extremely important. */ /* */ /* There is an attempt to correct for overlapping rectangles, */ /* if this one and the last were overlapping. */ i = intersection(&redraw.redraw_area, &last_rect); if (i) { this_area = (redraw.redraw_area.xmax - redraw.redraw_area.xmin) * (redraw.redraw_area.ymax - redraw.redraw_area.ymin) - (i->xmax - i->xmin) * (i->ymax - i->ymin); } else { this_area = (redraw.redraw_area.xmax - redraw.redraw_area.xmin) * (redraw.redraw_area.ymax - redraw.redraw_area.ymin); } last_rect = redraw.redraw_area; area_completed += this_area; if (area_completed > page_area) area_completed = page_area; { int percent; percent = (100 * (page - 1)) / estimated_pages + ((100 / estimated_pages) * area_completed) / page_area; if (percent < 0) percent = 0; if (percent > 99) percent = 99; _swix(Hourglass_Percentage, _IN(0), percent); } } /* Get the next rectangle */ e = _swix(PDriver_GetRectangle, _IN(1) | _OUT(0), &redraw.redraw_area, &more); if (e) goto out2; } if (next_line) redraw.xscroll = next_line; /* If we should print down to the bottom of the visible */ /* area of the page, this is flagged with 'end' set to */ /* End_Visible; if 'bottom' is zero as well, there's */ /* nothing more to print. */ if (end == End_Visible && !bottom) break; /* If xscroll is 0, redraw_draw must not have found any */ /* lines that fell off the bottom of the page - so */ /* there cannot be any more pages. */ if (!redraw.xscroll) break; /* Otherwise, close the outer while loop - which */ /* may mean we loop for another page. */ } /* Finished, so end the job, close the output stream, */ /* turn off the hourglass and restore the previous */ /* job. */ e = _swix(PDriver_EndJob, _IN(0), job); if (e) goto out2; /* Ensure images are restored to the correct mode */ image_mode_change(); /* Restore the old Escape handler */ signal(SIGINT, old_sigint_handler); /* Close the output stream */ globaljob = 0; e = _swix(OS_Find, _INR(0,1), 0x00, job); if (e) goto out3; /* Remove the hourglass percentage indicator */ _swix(Hourglass_Percentage, _IN(0), 100); /* Now we have to put the page back where it was... */ if (must_restore) { int leds = 1; print_restore_browser(b, &localbrowser); /* As before, sit around whilst the reformatter reformats. */ e = reformat_format_from(b, -1, 1, -1); if (e) return e; while (reformat_formatting(b)) { reformat_reformatter(b); leds ^= 3; _swix(Hourglass_LEDs, _INR(0,1), 3, leds); } /* We don't need to restore anything now */ must_restore = 0; } /* Turn off the hourglass */ _swix(Hourglass_Off, 0); if (must_restore) { print_restore_browser(b, &localbrowser); reformat_format_from(b, -1, 1, -1); } /* Restore the previous print job */ globalold_job = 0; e = _swix(PDriver_SelectJob, _INR(0,1), old_job, 0); return e; out1: /* 'Emergency exit' if PDriver_SelectJob fails */ signal(SIGINT, old_sigint_handler); globaljob = 0; /* Close the output stream */ _swix(OS_Find, _INR(0,1), 0x00, job); /* Flag that printing has finished */ printing = 0; /* Force the hourglass off */ _swix(Hourglass_Smash, 0); /* Put the browser back together again */ if (must_restore) { print_restore_browser(b, &localbrowser); reformat_format_from(b, -1, 1, -1); } /* Ensure images are still OK */ image_mode_change(); return e; out2: /* 'Emergency exit' for errors whilst printing */ signal(SIGINT, old_sigint_handler); print_abort_print(); printing = 0; if (must_restore) { print_restore_browser(b, &localbrowser); reformat_format_from(b, -1, 1, -1); } image_mode_change(); return e; out3: /* 'Emergency exit' for errors after printing */ _swix(Hourglass_Smash, 0); printing = 0; if (must_restore) { print_restore_browser(b, &localbrowser); reformat_format_from(b, -1, 1, -1); } image_mode_change(); return e; } /*************************************************/ /* print_abort_print() */ /* */ /* Forcibly aborts a print job. */ /*************************************************/ void print_abort_print(void) { /* Abort the current print job */ _swix(PDriver_AbortJob, _IN(0), globaljob); /* Close the output stream */ _swix(OS_Find, _INR(0,1), 0x00, globaljob); globaljob = 0; /* Restore the previous print job */ _swix(PDriver_SelectJob, _INR(0,1), globalold_job, 0); globalold_job = 0; /* Force the hourglass off */ _swix(Hourglass_Smash, 0); } /*************************************************/ /* print_prepare_browser() */ /* */ /* Prepares a browser for internal reformatting */ /* prior to printing a page, storing various */ /* overwritten values into an alternative */ /* given structure. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page to print; */ /* */ /* Pointer to the browser_data */ /* struct to copy into; */ /* */ /* Left hand page margin, in */ /* millipoints; */ /* */ /* Right hand page margin, in */ /* millipoints. */ /*************************************************/ static void print_prepare_browser(browser_data * source, browser_data * store, int lmarg, int rmarg, int tmarg, int bmarg) { store->previous = source->previous; store->next = source->next; source->previous = source->next = NULL; store->display_width = source->display_width; source->display_width = rmarg - lmarg; convert_to_os(source->display_width, &source->display_width); store->display_height = source->display_height; source->display_height = bmarg - tmarg; convert_to_os(source->display_height, &source->display_height); store->display_extent = source->display_extent; source->display_extent = source->display_width; store->fetch_status = source->fetch_status; source->fetch_status = BS_IDLE; store->fetch_handle = source->fetch_handle; source->fetch_handle = source->display_handle; } /*************************************************/ /* print_restore_browser() */ /* */ /* Puts back the bits and pieces replaced in the */ /* browser_data struct given to the print */ /* routines, copied out because a reformat was */ /* required on the page. */ /* */ /* Parameters: Pointer to the browser_data */ /* struct given to the print */ /* routines; */ /* */ /* Pointer to the browser_data */ /* struct used to store the over- */ /* written values from the original */ /* copy in print_prepare_browser. */ /*************************************************/ static void print_restore_browser(browser_data * original, browser_data * copy) { original->previous = copy->previous; original->next = copy->next; original->display_width = copy->display_width; original->display_extent = copy->display_extent; original->display_height = copy->display_height; original->fetch_status = copy->fetch_status; original->fetch_handle = copy->fetch_handle; } /*************************************************/ /* print_return_dialogue_info() */ /* */ /* Returns information on the Print dialogue, */ /* and its ancestor. */ /* */ /* Parameters: Pointer to an ObjectId, in which */ /* the ID of the PrintDBox object */ /* is placed; */ /* */ /* Pointer to an ObjectId, in which */ /* the ID of the underlying window */ /* object is placed; */ /* */ /* Pointer to an ObjectId, in which */ /* the ID of the ancestor window is */ /* placed; */ /* */ /* Pointer to a pointer to a */ /* browser_data struct, in which the */ /* address of the browser_data */ /* struct associated with the */ /* ancestor object is placed. */ /* */ /* Returns: See parameters list, and note */ /* that the returned values will be */ /* 0, 0, 0 and NULL if the Print */ /* dialogue is closed. */ /* */ /* Assumes: Any of the pointers may be NULL. */ /*************************************************/ void print_return_dialogue_info(ObjectId * self, ObjectId * window, ObjectId * ancestor, browser_data ** ancestor_b) { if (self) *self = self_id; if (window) *window = window_id; if (ancestor) *ancestor = ancestor_id; if (ancestor_b) *ancestor_b = ancestor_browser; }