/* 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 */ /* Author : A.D.Hodgkinson */ /* History: 27-Jan-97: Created */ /***************************************************/ #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 "TBEvents.h" #include "Utils.h" #include "Browser.h" #include "FontManage.h" #include "Images.h" #include "Memory.h" #include "Redraw.h" #include "Reformat.h" #include "Save.h" #include "Toolaction.h" #include "Toolbars.h" #include "Windows.h" #include "Printing.h" /* Local structures */ typedef struct { browser_data * p; /* Records for whom the print dialogue was opened. */ int copies; /* Number of copies to print. */ int start; /* 1 = whole page, 0 = top of visible area. */ int end; /* 0 = whole page, -1 = to bottom of visible area, > 0 = number of pages to fill. */ int pages; /* Remembers the number of pages set, if 'end' is 0 or -1, to enable Cancel to work. */ int reformat; /* 1 = reformat to fit page (if start is not 0 and end is not -1), else don't. */ int orientation; /* 1 = portrait, 0 = landscape. */ } print_info; /* Local variables */ static int globaljob = 0; static int globalold_job = 0; static int mustfree = 0; static print_info print_current; static print_info print_old; /* Static function prototypes */ static int print_pre_initiate (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int print_pre_restore (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static void print_free_memory (browser_data * localbrowser); /* Local macro - swap two signed / unsigned ints / chars. */ #define SWAP(a,b) { (a) ^= (b); (b) ^= (a); (a) ^= (b); } /*************************************************/ /* 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) { ObjectId id; int upper, lower; /* Get the browser_data structure pointer */ ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &print_current.p)); /* Get the print dialogue's actual window object ID into id. */ ChkError(printdbox_get_window_id(0, idb->self_id, &id)); /* Register handlers for alternate Print/Cancel buttons */ ChkError(event_register_toolbox_handler(id, EStartPrint, print_pre_initiate, (void *) idb->ancestor_id)); ChkError(event_register_toolbox_handler(id, ECancelPrint, print_pre_restore, (void *) idb->ancestor_id)); /* Various alterations of icons / buttons for different UI styles */ if (!strcmp(lookup_token("AlterNumranges:no",0,0),"yes")) { _kernel_oserror * e; WimpGetIconStateBlock icon; int iconlist[10]; char buffer[MaxMsgLen]; /* Get the object's window handle and the icon handle for the given component */ e = window_get_wimp_handle(0, id, &icon.window_handle); if (!e) { ComponentId writable; int loop; for (loop = 0; loop < 2; loop ++) { /* Get the number range's writabe component ID */ e = numberrange_get_components(NumberRange_GetComponents_ReturnNumericalField, id, loop == 1 ? CopiesNum : EndManyNum, &writable, NULL, NULL, NULL); /* Turn this into an icon handle */ if (!e) e = gadget_get_icon_list(0, 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_token("AlterWith",1,0), sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = 0; windows_process_icon_text(&icon, buffer, 0); } } } } } /* If print_current.copies is zero, this is the first time */ /* the dialogue has been opened, so set up defaults. */ if (!print_current.copies) { /* Number of copies */ print_current.copies = atoi(lookup_choice("PrintCopies:1",0,0)); /* Check it's within bounds */ ChkError(numberrange_get_bounds(NumberRange_LowerBound | NumberRange_UpperBound, id, CopiesNum, &lower, &upper, NULL, NULL)); if (print_current.copies < lower) print_current.copies = lower; if (print_current.copies > upper) print_current.copies = upper; /* Start position - 'start' or 'visible', though in fact any */ /* non-'visible' string defaults as 'start'. */ if (!strcmp(lookup_choice("PrintStart:start",0,0),"visible")) print_current.start = 0; else print_current.start = 1; /* End position - 'end', 'visible', or a number of pages to fill. */ /* if 'end' or 'visible' aren't recognised the number of pages is */ /* defaulted to, if this gives a result <= 0, 1 is assumed. */ if (!strcmp(lookup_choice("PrintEnd:end",0,0),"end")) print_current.end = 0; else if (!strcmp(lookup_choice("PrintEnd",1,0),"visible")) print_current.end = -1; else { print_current.end = atoi(lookup_choice("PrintEnd",1,0)); if (print_current.end <= 0) print_current.end = 1; } ChkError(numberrange_get_bounds(NumberRange_LowerBound | NumberRange_UpperBound, id, CopiesNum, &lower, &upper, NULL, NULL)); if (print_current.end > 1) { /* Check it's within bounds */ if (print_current.end < lower) print_current.end = lower; if (print_current.end > upper) print_current.end = upper; print_current.pages = print_current.end; } else print_current.pages = lower; /* Reformat - 'yes' or 'no', default to 'yes' */ if (!strcmp(lookup_choice("PrintReform:yes",0,0),"no")) print_current.reformat = 0; else print_current.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")) print_current.orientation = 0; else print_current.orientation = 1; } /* Copy print_current to print_old in case restoration is needed later, */ /* and fill in the dialogue as appropriate. */ print_old = print_current; /* Start position */ if (!print_current.start) ChkError(radiobutton_set_state(0, id, StartVisible, 1)); else ChkError(radiobutton_set_state(0, id, StartWhole, 1)); /* End position, including the 'number of pages to fill' number range */ if (!print_current.end) ChkError(radiobutton_set_state(0, id, EndWhole, 1)); else if (print_current.end < 0) ChkError(radiobutton_set_state(0, id, EndVisible, 1)); else ChkError(radiobutton_set_state(0, id, EndMany, 1)); if (print_current.end > 0) ChkError(numberrange_set_value(0, id, EndManyNum, print_current.end)); else ChkError(numberrange_set_value(0, id, EndManyNum, print_current.pages)); /* The reformat optionn, including greying / ungreying it */ ChkError(optionbutton_set_state(0, id, ReformToFit, print_current.reformat)); /* As well as greying / ungreying the reformat option, this handles */ /* the label text on the 'pages to fill' number range. */ { IdBlock idlocal; idlocal.self_id = id; print_check_contents(NULL, NULL, &idlocal, NULL); } /* Orientation */ if (!print_current.orientation) ChkError(radiobutton_set_state(0, id, OriSideways, 1)); else ChkError(radiobutton_set_state(0, id, OriUpright, 1)); /* Number of copies */ ChkError(numberrange_set_value(0, id, CopiesNum, print_current.copies)); /* Register a handler to cope with the pages number range changing */ ChkError(event_register_toolbox_handler(id, NumberRange_ValueChanged, print_check_contents, NULL)); /* Animation handler if there's an appropriate gadget */ { int temp_type; if ( fixed.dboxanims && !gadget_get_type(0, id, DisplayAnim, &temp_type) ) register_null_claimant(Wimp_ENull, toolbars_animate_slow, (void *) id); } /* Done! */ return 1; } /*************************************************/ /* print_pre_restore() */ /* */ /* Called for a non-standard Cancel button in a */ /* PrintDBox object or an alternate window it is */ /* using. Closes the dialogue, resulting in an */ /* appropriate DialogueCompleted event being */ /* raised; print_restore will consequently be */ /* called for the dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ static int print_pre_restore(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { ChkError(toolbox_hide_object(0, idb->self_id)); return 1; } /*************************************************/ /* print_restore() */ /* */ /* Called when a print dialogue is cancelled. */ /* Simply copies the local static print_info */ /* structure 'print_old' over 'print_current' so */ /* that next time the dialogue is opened, it */ /* holds the old settings. */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ int print_restore(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { // print_current = print_old; ChkError(event_deregister_toolbox_handlers_for_object(idb->self_id)); /* If there was a null handler for the dialogue, remove it */ { int temp_type; if ( fixed.dboxanims && !gadget_get_type(0, idb->self_id, DisplayAnim, &temp_type) ) deregister_null_claimant(Wimp_ENull, toolbars_animate_slow, (browser_data *) idb->self_id); } /* Restore focus to the browser window */ { browser_data * ancestor; if (idb->ancestor_id) ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &ancestor)); else ancestor = last_browser; if (ancestor) browser_give_general_focus(ancestor); } return 1; } /*************************************************/ /* print_pre_restore() */ /* */ /* Called for a non-standard Print button in a */ /* PrintDBox object or an alternate window it is */ /* using. Raises appropriate events to complete */ /* the dialogue and initate printing through */ /* print_initiate. */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ static int print_pre_initiate(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { idb->ancestor_id = (ObjectId) handle; print_initiate(eventcode, event, idb, (void *) 1 /* (Flag - see print_initiate) */); ChkError(toolbox_hide_object(0, idb->self_id)); return 1; } /*************************************************/ /* print_initiate() */ /* */ /* Examines the Print dialogue box's contents */ /* and fills in the local static print_info */ /* structure 'print_current' with them; then */ /* starts a print routine by broadcasting a */ /* PrintSave message. */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ int print_initiate(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; ObjectId id; int selected; /* Get the browser_data pointer for the ancestor window - i.e. */ /* the browser from which this print dialogue was opened. */ ChkError(toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b)); /* Get the Object ID of the window that has opened on behalf of */ /* the Print dialogue. */ /* */ /* If called from print_pre_initiate, the self_id will be that */ /* of the alternate window anyway. (int) handle = 1 flags this. */ /* */ /* Amazingly, printdbox_get_window_id doesn't seem to return an */ /* error - just an invalid ID - if called on something that is */ /* not a PrintDBox object... */ if (!((int) handle)) ChkError(printdbox_get_window_id(0, idb->self_id, &id)); else id = idb->self_id; /* Fill in the local static print_info structure 'print_current'. */ /* */ /* 'start' = 1 for whole page, 0 for just the visible area */ ChkError(radiobutton_get_state(0, id, StartWhole, &print_current.start, NULL)); /* 'end' = 0 for whole page, -1 for just the visible area, or */ /* any other number = the number of pages to fill. */ ChkError(radiobutton_get_state(0, id, EndWhole, NULL, &selected)); switch (selected) { case EndVisible: print_current.end = -1; break; case EndMany: ChkError(numberrange_get_value(0, id, EndManyNum, &print_current.end)); break; case EndWhole: default: print_current.end = 0; } /* 'reformat' = 1 to reformat to fit the paper, else 0 */ ChkError(optionbutton_get_state(0, id, ReformToFit, &print_current.reformat)); /* 'orientation' = 1 for portrait, 0 for landscape */ ChkError(radiobutton_get_state(0, id, OriUpright, &print_current.orientation, NULL)); /* Get the number of copies into 'copies' */ ChkError(numberrange_get_value(0, id, CopiesNum, &print_current.copies)); /* First stage of printing protocol: Broadcast a PrintSave message */ { WimpMessage m; m.hdr.your_ref = 0; m.hdr.action_code = Browser_Message_PrintSave; m.data.data_save.destination_window = 0; m.data.data_save.destination_icon = 0; m.data.data_save.destination_x = 0; m.data.data_save.destination_y = 0; m.data.data_save.estimated_size = -1; m.data.data_save.file_type = FileType_POUT; StrNCpy0(m.data.data_save.leaf_name, lookup_token("PrintName:WebPage",0,0)); m.hdr.size = (strlen(m.data.data_save.leaf_name) + 44); if (m.hdr.size & 3) m.hdr.size = (m.hdr.size & ~3) + 4; ChkError(wimp_send_message(Wimp_EUserMessageRecorded, &m, 0, 0, NULL)); printer_message_ref = m.hdr.my_ref; } return 1; } /*************************************************/ /* print_print() */ /* */ /* Calls the printing engine with parameters */ /* specified in the local static print_info */ /* structure 'print_current'. */ /* */ /* Parameters: Pointer to pathname to print to. */ /*************************************************/ void print_print(const char * path) { _kernel_oserror * e; printing = 1; e = print_page(print_current.p, print_current.copies, print_current.start, print_current.end, print_current.reformat, print_current.orientation, path); printing = 0; if (e) show_error_ret(e); } /*************************************************/ /* 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; */ /* 0 to end at the bottom of the web */ /* page, -1 to end at the bottom of */ /* the visible area, or any >0 value */ /* which is taken as the number of */ /* pages to try and 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. */ /*************************************************/ _kernel_oserror * print_page(browser_data * b, int copies, int from, int to, int reformat, int orientation, const char * path) { _kernel_oserror * e = NULL; WimpRedrawWindowBlock redraw; int job, old_job; BBox box, last_rect; int more, page, maxpages, top, bottom, temp, next_line; int estimated_pages, area_completed, page_area; int lmarg, bmarg, rmarg, tmarg; unsigned int features; int portrait [2] [2] = { {0x10000, 0}, {0, 0x10000} }; int landscape [2] [2] = { {0, -0x10000}, {0x10000, 0} }; browser_data localbrowser; reformat_cell localcell; 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); } localbrowser = *b; memset(&localcell, 0, sizeof(localcell)); /* If the user specified printing to or from something that */ /* depends upon the visible area, can't then reformat. */ if (reformat && (to >= 0) && from) { // int lastchunk, percentage; /* Reformat the page ready for printing. */ /* */ /* First copy over the existing browser_data structure, and */ /* modify the contents as appropriate. */ localbrowser.previous = localbrowser.next = NULL; localbrowser.display_width = rmarg - lmarg; convert_to_os(localbrowser.display_width, &localbrowser.display_width); localbrowser.display_extent = localbrowser.display_width; localbrowser.fetch_status = BS_IDLE; localbrowser.fetch_handle = localbrowser.display_handle; localbrowser.source = NULL; localbrowser.urlfdata = NULL; localbrowser.urlddata = NULL; localbrowser.cell = &localcell; mustfree = 1; /* Now call the reformatter, and loop round until finished. */ _swix(Hourglass_On, 0); e = reformat_format_from(&localbrowser, -1, 1, -1); if (e) return e; while (reformat_formatting(&localbrowser)) { reformat_reformatter(&localbrowser); // Broken now that there's no such thing as a token number... // Moreover, needs adjusting to the cell based model. // // /* Percentage progress indicator based on how far down the */ // /* token list the reformat has reached. */ // // if (localbrowser.ldata && localbrowser.nlines) // { // lastchunk = localbrowser.ldata[localbrowser.nlines - 1].chunks + localbrowser.ldata[localbrowser.nlines - 1].n - 1; // // percentage = (100 * localbrowser.cdata[lastchunk].t) / localbrowser.ntokens; // if (percentage > 99) percentage = 99; // // _swix(Hourglass_Percentage, _IN(0), percentage); // } } _swix(Hourglass_Off, 0); } else mustfree = 0; localbrowser.sourcecolours = 0; /* Hourglass for the print job, as this may take some time... */ /* 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); /* 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(&localbrowser, 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.systemfont) { h = fm_find_font(NULL, "system", 192,192,0,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "system", 192,192,1,0); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "system", 192,192,0,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); h = fm_find_font(NULL, "system", 192,192,1,1); _swix(PDriver_DeclareFont,_INR(0,2),h,0,2); fm_lose_font(NULL, h); } else { 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; 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 */ // Haven't done the wimpt_dy() stuff yet (work out the heights // separately, and if non-zero add wimpt_dy for the window border) if (!fixed.swapbars) { top += (toolbars_url_height(b) + toolbars_button_height(b)); bottom -= (toolbars_url_height(b) + toolbars_button_height(b) + toolbars_status_height(b) - top); } else { top += toolbars_status_height(b); bottom -= (toolbars_url_height(b) + toolbars_button_height(b) + toolbars_status_height(b) - 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. */ // Haven't done the wimpt_dy() stuff yet (work out the heights // separately, and if non-zero add wimpt_dy for the window border) if (!fixed.swapbars) { if (from) top = toolbars_url_height(b) + toolbars_button_height(b); } else { if (from) top = toolbars_status_height(b); } redraw.yscroll = -top; redraw.xscroll = 0; /* maxpages will be set either to the user-specified number of */ /* sheets of paper to fill ('to' holds that number if greater */ /* than zero), else it holds a very large number - i.e. keep */ /* going until another exit condition occurs within the print */ /* loop (e.g. reaching the end of the web page). */ page = 0; if (to > 0) maxpages = to; else maxpages = 0x1000000; /* 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. */ while (page <= maxpages) { 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 (to >= 0) { 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(&localbrowser, &redraw, 1, 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 'to' < 0; if */ /* 'bottom' = 0 as well, there's nothing more to print. */ if (to < 0 && !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(); /* Turn off the hourglass */ _swix(Hourglass_Off, 0); /* 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; print_free_memory(&localbrowser); /* 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); /* Force the hourglass off */ _swix(Hourglass_Smash, 0); /* Free temporarily allocated memory */ print_free_memory(&localbrowser); /* 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(); print_free_memory(&localbrowser); image_mode_change(); return e; out3: /* 'Emergency exit' for errors outside printing, where memory may be temporarily allocted */ _swix(Hourglass_Smash, 0); print_free_memory(&localbrowser); 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_free_memory() */ /* */ /* Frees up browser_data allocated memory used */ /* locally for reformatting a page prior to */ /* printing. */ /*************************************************/ static void print_free_memory(browser_data * localbrowser) { /* mustfree must be set to non-zero for any memory to be freed */ if (mustfree) { memory_set_chunk_size(localbrowser, NULL, CK_LINE, 0); memory_set_chunk_size(localbrowser, NULL, CK_LDAT, 0); } } /*************************************************/ /* 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. */ /*************************************************/ 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, idb->self_id, StartWhole, NULL, &state1)); ChkError(radiobutton_get_state(0, idb->self_id, EndWhole, NULL, &state2)); /* If the StartVisible and EndVisible 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, idb->self_id, ReformToFit, &flags)); if (state1 != StartVisible && state2 != EndVisible) { if (flags & Gadget_Faded) { ChkError(gadget_set_flags(0, idb->self_id, ReformToFit, flags & ~Gadget_Faded)); } } else { if (!(flags & Gadget_Faded)) { ChkError(gadget_set_flags(0, idb->self_id, ReformToFit, flags | Gadget_Faded)); } } /* Check the pages number range, and update the label if necessary. */ { char text[256]; ChkError(numberrange_get_value(0, idb->self_id, EndManyNum, &pages)); ChkError(button_get_value(0, idb->self_id, EndManyLabel, text, 256, NULL)); 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, idb->self_id, EndManyLabel, 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, idb->self_id, EndManyLabel, lookup_token("PagesMany:sheets are filled",0,0))); } } } return 1; } /*************************************************/